text: 'use strict';
  
  Object.defineProperty(exports, '__esModule', { value: true });
  
  require('proxy-polyfill');
  var uuid = require('uuid');
  var reselect = require('reselect');
  
  const MINIMUM_PUBLISH_BID = 0.00000001;
  
  const CHANNEL_ANONYMOUS = 'anonymous';
  const CHANNEL_NEW = 'new';
  const PAGE_SIZE = 20;
  
  const LEVEL_1_STAKED_AMOUNT = 0;
  const LEVEL_2_STAKED_AMOUNT = 1;
  const LEVEL_3_STAKED_AMOUNT = 50;
  const LEVEL_4_STAKED_AMOUNT = 250;
  const LEVEL_5_STAKED_AMOUNT = 1000;
  
  var claim = /*#__PURE__*/Object.freeze({
    MINIMUM_PUBLISH_BID: MINIMUM_PUBLISH_BID,
    CHANNEL_ANONYMOUS: CHANNEL_ANONYMOUS,
    CHANNEL_NEW: CHANNEL_NEW,
    PAGE_SIZE: PAGE_SIZE,
    LEVEL_1_STAKED_AMOUNT: LEVEL_1_STAKED_AMOUNT,
    LEVEL_2_STAKED_AMOUNT: LEVEL_2_STAKED_AMOUNT,
    LEVEL_3_STAKED_AMOUNT: LEVEL_3_STAKED_AMOUNT,
    LEVEL_4_STAKED_AMOUNT: LEVEL_4_STAKED_AMOUNT,
    LEVEL_5_STAKED_AMOUNT: LEVEL_5_STAKED_AMOUNT
  });
  
  const WINDOW_FOCUSED = 'WINDOW_FOCUSED';
  const DAEMON_READY = 'DAEMON_READY';
  const DAEMON_VERSION_MATCH = 'DAEMON_VERSION_MATCH';
  const DAEMON_VERSION_MISMATCH = 'DAEMON_VERSION_MISMATCH';
  const VOLUME_CHANGED = 'VOLUME_CHANGED';
  const SET_WELCOME_VERSION = 'SET_WELCOME_VERSION';
  const SET_ALLOW_ANALYTICS = 'SET_ALLOW_ANALYTICS';
  
  // Navigation
  const CHANGE_AFTER_AUTH_PATH = 'CHANGE_AFTER_AUTH_PATH';
  const WINDOW_SCROLLED = 'WINDOW_SCROLLED';
  const HISTORY_NAVIGATE = 'HISTORY_NAVIGATE';
  
  // Upgrades
  const UPGRADE_CANCELLED = 'UPGRADE_CANCELLED';
  const DOWNLOAD_UPGRADE = 'DOWNLOAD_UPGRADE';
  const UPGRADE_DOWNLOAD_STARTED = 'UPGRADE_DOWNLOAD_STARTED';
  const UPGRADE_DOWNLOAD_COMPLETED = 'UPGRADE_DOWNLOAD_COMPLETED';
  const UPGRADE_DOWNLOAD_PROGRESSED = 'UPGRADE_DOWNLOAD_PROGRESSED';
  const CHECK_UPGRADE_AVAILABLE = 'CHECK_UPGRADE_AVAILABLE';
  const CHECK_UPGRADE_START = 'CHECK_UPGRADE_START';
  const CHECK_UPGRADE_SUCCESS = 'CHECK_UPGRADE_SUCCESS';
  const CHECK_UPGRADE_FAIL = 'CHECK_UPGRADE_FAIL';
  const CHECK_UPGRADE_SUBSCRIBE = 'CHECK_UPGRADE_SUBSCRIBE';
  const UPDATE_VERSION = 'UPDATE_VERSION';
  const UPDATE_REMOTE_VERSION = 'UPDATE_REMOTE_VERSION';
  const SKIP_UPGRADE = 'SKIP_UPGRADE';
  const START_UPGRADE = 'START_UPGRADE';
  const AUTO_UPDATE_DECLINED = 'AUTO_UPDATE_DECLINED';
  const AUTO_UPDATE_DOWNLOADED = 'AUTO_UPDATE_DOWNLOADED';
  const CLEAR_UPGRADE_TIMER = 'CLEAR_UPGRADE_TIMER';
  
  // Wallet
  const GET_NEW_ADDRESS_STARTED = 'GET_NEW_ADDRESS_STARTED';
  const GET_NEW_ADDRESS_COMPLETED = 'GET_NEW_ADDRESS_COMPLETED';
  const FETCH_TRANSACTIONS_STARTED = 'FETCH_TRANSACTIONS_STARTED';
  const FETCH_TRANSACTIONS_COMPLETED = 'FETCH_TRANSACTIONS_COMPLETED';
  const FETCH_TXO_PAGE_STARTED = 'FETCH_TXO_PAGE_STARTED';
  const FETCH_TXO_PAGE_COMPLETED = 'FETCH_TXO_PAGE_COMPLETED';
  const FETCH_TXO_PAGE_FAILED = 'FETCH_TXO_PAGE_FAILED';
  const UPDATE_TXO_FETCH_PARAMS = 'UPDATE_TXO_FETCH_PARAMS';
  const FETCH_SUPPORTS_STARTED = 'FETCH_SUPPORTS_STARTED';
  const FETCH_SUPPORTS_COMPLETED = 'FETCH_SUPPORTS_COMPLETED';
  const ABANDON_SUPPORT_STARTED = 'ABANDON_SUPPORT_STARTED';
  const ABANDON_SUPPORT_COMPLETED = 'ABANDON_SUPPORT_COMPLETED';
  const ABANDON_CLAIM_SUPPORT_STARTED = 'ABANDON_CLAIM_SUPPORT_STARTED';
  const ABANDON_CLAIM_SUPPORT_COMPLETED = 'ABANDON_CLAIM_SUPPORT_COMPLETED';
  const ABANDON_CLAIM_SUPPORT_FAILED = 'ABANDON_CLAIM_SUPPORT_FAILED';
  const ABANDON_CLAIM_SUPPORT_PREVIEW = 'ABANDON_CLAIM_SUPPORT_PREVIEW';
  const PENDING_SUPPORTS_UPDATED = 'PENDING_SUPPORTS_UPDATED';
  const UPDATE_BALANCE = 'UPDATE_BALANCE';
  const UPDATE_TOTAL_BALANCE = 'UPDATE_TOTAL_BALANCE';
  const CHECK_ADDRESS_IS_MINE_STARTED = 'CHECK_ADDRESS_IS_MINE_STARTED';
  const CHECK_ADDRESS_IS_MINE_COMPLETED = 'CHECK_ADDRESS_IS_MINE_COMPLETED';
  const SEND_TRANSACTION_STARTED = 'SEND_TRANSACTION_STARTED';
  const SEND_TRANSACTION_COMPLETED = 'SEND_TRANSACTION_COMPLETED';
  const SEND_TRANSACTION_FAILED = 'SEND_TRANSACTION_FAILED';
  const SUPPORT_TRANSACTION_STARTED = 'SUPPORT_TRANSACTION_STARTED';
  const SUPPORT_TRANSACTION_COMPLETED = 'SUPPORT_TRANSACTION_COMPLETED';
  const SUPPORT_TRANSACTION_FAILED = 'SUPPORT_TRANSACTION_FAILED';
  const CLEAR_SUPPORT_TRANSACTION = 'CLEAR_SUPPORT_TRANSACTION';
  const WALLET_ENCRYPT_START = 'WALLET_ENCRYPT_START';
  const WALLET_ENCRYPT_COMPLETED = 'WALLET_ENCRYPT_COMPLETED';
  const WALLET_ENCRYPT_FAILED = 'WALLET_ENCRYPT_FAILED';
  const WALLET_UNLOCK_START = 'WALLET_UNLOCK_START';
  const WALLET_UNLOCK_COMPLETED = 'WALLET_UNLOCK_COMPLETED';
  const WALLET_UNLOCK_FAILED = 'WALLET_UNLOCK_FAILED';
  const WALLET_DECRYPT_START = 'WALLET_DECRYPT_START';
  const WALLET_DECRYPT_COMPLETED = 'WALLET_DECRYPT_COMPLETED';
  const WALLET_DECRYPT_FAILED = 'WALLET_DECRYPT_FAILED';
  const WALLET_LOCK_START = 'WALLET_LOCK_START';
  const WALLET_LOCK_COMPLETED = 'WALLET_LOCK_COMPLETED';
  const WALLET_LOCK_FAILED = 'WALLET_LOCK_FAILED';
  const WALLET_STATUS_START = 'WALLET_STATUS_START';
  const WALLET_STATUS_COMPLETED = 'WALLET_STATUS_COMPLETED';
  const WALLET_RESTART = 'WALLET_RESTART';
  const WALLET_RESTART_COMPLETED = 'WALLET_RESTART_COMPLETED';
  const SET_TRANSACTION_LIST_FILTER = 'SET_TRANSACTION_LIST_FILTER';
  const UPDATE_CURRENT_HEIGHT = 'UPDATE_CURRENT_HEIGHT';
  const SET_DRAFT_TRANSACTION_AMOUNT = 'SET_DRAFT_TRANSACTION_AMOUNT';
  const SET_DRAFT_TRANSACTION_ADDRESS = 'SET_DRAFT_TRANSACTION_ADDRESS';
  const FETCH_UTXO_COUNT_STARTED = 'FETCH_UTXO_COUNT_STARTED';
  const FETCH_UTXO_COUNT_COMPLETED = 'FETCH_UTXO_COUNT_COMPLETED';
  const FETCH_UTXO_COUNT_FAILED = 'FETCH_UTXO_COUNT_FAILED';
  const TIP_CLAIM_MASS_STARTED = 'TIP_CLAIM_MASS_STARTED';
  const TIP_CLAIM_MASS_COMPLETED = 'TIP_CLAIM_MASS_COMPLETED';
  const TIP_CLAIM_MASS_FAILED = 'TIP_CLAIM_MASS_FAILED';
  const DO_UTXO_CONSOLIDATE_STARTED = 'DO_UTXO_CONSOLIDATE_STARTED';
  const DO_UTXO_CONSOLIDATE_COMPLETED = 'DO_UTXO_CONSOLIDATE_COMPLETED';
  const DO_UTXO_CONSOLIDATE_FAILED = 'DO_UTXO_CONSOLIDATE_FAILED';
  const PENDING_CONSOLIDATED_TXOS_UPDATED = 'PENDING_CONSOLIDATED_TXOS_UPDATED';
  
  // Claims
  const RESOLVE_URIS_STARTED = 'RESOLVE_URIS_STARTED';
  const RESOLVE_URIS_COMPLETED = 'RESOLVE_URIS_COMPLETED';
  const FETCH_CHANNEL_CLAIMS_STARTED = 'FETCH_CHANNEL_CLAIMS_STARTED';
  const FETCH_CHANNEL_CLAIMS_COMPLETED = 'FETCH_CHANNEL_CLAIMS_COMPLETED';
  const FETCH_CLAIM_LIST_MINE_STARTED = 'FETCH_CLAIM_LIST_MINE_STARTED';
  const FETCH_CLAIM_LIST_MINE_COMPLETED = 'FETCH_CLAIM_LIST_MINE_COMPLETED';
  const ABANDON_CLAIM_STARTED = 'ABANDON_CLAIM_STARTED';
  const ABANDON_CLAIM_SUCCEEDED = 'ABANDON_CLAIM_SUCCEEDED';
  const FETCH_CHANNEL_LIST_STARTED = 'FETCH_CHANNEL_LIST_STARTED';
  const FETCH_CHANNEL_LIST_COMPLETED = 'FETCH_CHANNEL_LIST_COMPLETED';
  const FETCH_CHANNEL_LIST_FAILED = 'FETCH_CHANNEL_LIST_FAILED';
  const FETCH_COLLECTION_LIST_STARTED = 'FETCH_COLLECTION_LIST_STARTED';
  const FETCH_COLLECTION_LIST_COMPLETED = 'FETCH_COLLECTION_LIST_COMPLETED';
  const FETCH_COLLECTION_LIST_FAILED = 'FETCH_COLLECTION_LIST_FAILED';
  const CREATE_CHANNEL_STARTED = 'CREATE_CHANNEL_STARTED';
  const CREATE_CHANNEL_COMPLETED = 'CREATE_CHANNEL_COMPLETED';
  const CREATE_CHANNEL_FAILED = 'CREATE_CHANNEL_FAILED';
  const UPDATE_CHANNEL_STARTED = 'UPDATE_CHANNEL_STARTED';
  const UPDATE_CHANNEL_COMPLETED = 'UPDATE_CHANNEL_COMPLETED';
  const UPDATE_CHANNEL_FAILED = 'UPDATE_CHANNEL_FAILED';
  const IMPORT_CHANNEL_STARTED = 'IMPORT_CHANNEL_STARTED';
  const IMPORT_CHANNEL_COMPLETED = 'IMPORT_CHANNEL_COMPLETED';
  const IMPORT_CHANNEL_FAILED = 'IMPORT_CHANNEL_FAILED';
  const CLEAR_CHANNEL_ERRORS = 'CLEAR_CHANNEL_ERRORS';
  const PUBLISH_STARTED = 'PUBLISH_STARTED';
  const PUBLISH_COMPLETED = 'PUBLISH_COMPLETED';
  const PUBLISH_FAILED = 'PUBLISH_FAILED';
  const SET_PLAYING_URI = 'SET_PLAYING_URI';
  const SET_CONTENT_POSITION = 'SET_CONTENT_POSITION';
  const SET_CONTENT_LAST_VIEWED = 'SET_CONTENT_LAST_VIEWED';
  const CLEAR_CONTENT_HISTORY_URI = 'CLEAR_CONTENT_HISTORY_URI';
  const CLEAR_CONTENT_HISTORY_ALL = 'CLEAR_CONTENT_HISTORY_ALL';
  const CLAIM_SEARCH_STARTED = 'CLAIM_SEARCH_STARTED';
  const CLAIM_SEARCH_COMPLETED = 'CLAIM_SEARCH_COMPLETED';
  const CLAIM_SEARCH_FAILED = 'CLAIM_SEARCH_FAILED';
  const CLAIM_SEARCH_BY_TAGS_STARTED = 'CLAIM_SEARCH_BY_TAGS_STARTED';
  const CLAIM_SEARCH_BY_TAGS_COMPLETED = 'CLAIM_SEARCH_BY_TAGS_COMPLETED';
  const CLAIM_SEARCH_BY_TAGS_FAILED = 'CLAIM_SEARCH_BY_TAGS_FAILED';
  const CLAIM_REPOST_STARTED = 'CLAIM_REPOST_STARTED';
  const CLAIM_REPOST_COMPLETED = 'CLAIM_REPOST_COMPLETED';
  const CLAIM_REPOST_FAILED = 'CLAIM_REPOST_FAILED';
  const CLEAR_REPOST_ERROR = 'CLEAR_REPOST_ERROR';
  const CHECK_PUBLISH_NAME_STARTED = 'CHECK_PUBLISH_NAME_STARTED';
  const CHECK_PUBLISH_NAME_COMPLETED = 'CHECK_PUBLISH_NAME_COMPLETED';
  const UPDATE_PENDING_CLAIMS = 'UPDATE_PENDING_CLAIMS';
  const UPDATE_CONFIRMED_CLAIMS = 'UPDATE_CONFIRMED_CLAIMS';
  const ADD_FILES_REFLECTING = 'ADD_FILES_REFLECTING';
  const UPDATE_FILES_REFLECTING = 'UPDATE_FILES_REFLECTING';
  const TOGGLE_CHECKING_REFLECTING = 'TOGGLE_CHECKING_REFLECTING';
  const TOGGLE_CHECKING_PENDING = 'TOGGLE_CHECKING_PENDING';
  const PURCHASE_LIST_STARTED = 'PURCHASE_LIST_STARTED';
  const PURCHASE_LIST_COMPLETED = 'PURCHASE_LIST_COMPLETED';
  const PURCHASE_LIST_FAILED = 'PURCHASE_LIST_FAILED';
  
  const COLLECTION_PUBLISH_STARTED = 'COLLECTION_PUBLISH_STARTED';
  const COLLECTION_PUBLISH_COMPLETED = 'COLLECTION_PUBLISH_COMPLETED';
  const COLLECTION_PUBLISH_FAILED = 'COLLECTION_PUBLISH_FAILED';
  const COLLECTION_PUBLISH_UPDATE_STARTED = 'COLLECTION_PUBLISH_UPDATE_STARTED';
  const COLLECTION_PUBLISH_UPDATE_COMPLETED = 'COLLECTION_PUBLISH_UPDATE_COMPLETED';
  const COLLECTION_PUBLISH_UPDATE_FAILED = 'COLLECTION_PUBLISH_UPDATE_FAILED';
  const COLLECTION_PUBLISH_ABANDON_STARTED = 'COLLECTION_PUBLISH_ABANDON_STARTED';
  const COLLECTION_PUBLISH_ABANDON_COMPLETED = 'COLLECTION_PUBLISH_ABANDON_COMPLETED';
  const COLLECTION_PUBLISH_ABANDON_FAILED = 'COLLECTION_PUBLISH_ABANDON_FAILED';
  const CLEAR_COLLECTION_ERRORS = 'CLEAR_COLLECTION_ERRORS';
  const COLLECTION_ITEMS_RESOLVE_STARTED = 'COLLECTION_ITEMS_RESOLVE_STARTED';
  const COLLECTION_ITEMS_RESOLVE_COMPLETED = 'COLLECTION_ITEMS_RESOLVE_COMPLETED';
  const COLLECTION_ITEMS_RESOLVE_FAILED = 'COLLECTION_ITEMS_RESOLVE_FAILED';
  const COLLECTION_NEW = 'COLLECTION_NEW';
  const COLLECTION_DELETE = 'COLLECTION_DELETE';
  const COLLECTION_PENDING = 'COLLECTION_PENDING';
  const COLLECTION_EDIT = 'COLLECTION_EDIT';
  const COLLECTION_COPY = 'COLLECTION_COPY';
  const COLLECTION_SAVE = 'COLLECTION_SAVE';
  const COLLECTION_ERROR = 'COLLECTION_ERROR';
  
  // Comments
  const COMMENT_LIST_STARTED = 'COMMENT_LIST_STARTED';
  const COMMENT_LIST_COMPLETED = 'COMMENT_LIST_COMPLETED';
  const COMMENT_LIST_FAILED = 'COMMENT_LIST_FAILED';
  const COMMENT_CREATE_STARTED = 'COMMENT_CREATE_STARTED';
  const COMMENT_CREATE_COMPLETED = 'COMMENT_CREATE_COMPLETED';
  const COMMENT_CREATE_FAILED = 'COMMENT_CREATE_FAILED';
  const COMMENT_ABANDON_STARTED = 'COMMENT_ABANDON_STARTED';
  const COMMENT_ABANDON_COMPLETED = 'COMMENT_ABANDON_COMPLETED';
  const COMMENT_ABANDON_FAILED = 'COMMENT_ABANDON_FAILED';
  const COMMENT_UPDATE_STARTED = 'COMMENT_UPDATE_STARTED';
  const COMMENT_UPDATE_COMPLETED = 'COMMENT_UPDATE_COMPLETED';
  const COMMENT_UPDATE_FAILED = 'COMMENT_UPDATE_FAILED';
  const COMMENT_HIDE_STARTED = 'COMMENT_HIDE_STARTED';
  const COMMENT_HIDE_COMPLETED = 'COMMENT_HIDE_COMPLETED';
  const COMMENT_HIDE_FAILED = 'COMMENT_HIDE_FAILED';
  
  // Files
  const FILE_LIST_STARTED = 'FILE_LIST_STARTED';
  const FILE_LIST_SUCCEEDED = 'FILE_LIST_SUCCEEDED';
  const FETCH_FILE_INFO_STARTED = 'FETCH_FILE_INFO_STARTED';
  const FETCH_FILE_INFO_COMPLETED = 'FETCH_FILE_INFO_COMPLETED';
  const FETCH_FILE_INFO_FAILED = 'FETCH_FILE_INFO_FAILED';
  const LOADING_VIDEO_STARTED = 'LOADING_VIDEO_STARTED';
  const LOADING_VIDEO_COMPLETED = 'LOADING_VIDEO_COMPLETED';
  const LOADING_VIDEO_FAILED = 'LOADING_VIDEO_FAILED';
  const DOWNLOADING_STARTED = 'DOWNLOADING_STARTED';
  const DOWNLOADING_PROGRESSED = 'DOWNLOADING_PROGRESSED';
  const DOWNLOADING_COMPLETED = 'DOWNLOADING_COMPLETED';
  const DOWNLOADING_CANCELED = 'DOWNLOADING_CANCELED';
  const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED';
  const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED';
  const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED';
  const FILE_DELETE = 'FILE_DELETE';
  const SET_FILE_LIST_SORT = 'SET_FILE_LIST_SORT';
  const PURCHASE_URI_STARTED = 'PURCHASE_URI_STARTED';
  const PURCHASE_URI_COMPLETED = 'PURCHASE_URI_COMPLETED';
  const PURCHASE_URI_FAILED = 'PURCHASE_URI_FAILED';
  const CLEAR_PURCHASED_URI_SUCCESS = 'CLEAR_PURCHASED_URI_SUCCESS';
  
  // Settings
  const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
  const DAEMON_STATUS_RECEIVED = 'DAEMON_STATUS_RECEIVED';
  const SHARED_PREFERENCE_SET = 'SHARED_PREFERENCE_SET';
  const SAVE_CUSTOM_WALLET_SERVERS = 'SAVE_CUSTOM_WALLET_SERVERS';
  const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED';
  const UPDATE_IS_NIGHT = 'UPDATE_IS_NIGHT';
  
  // User
  const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
  const AUTHENTICATION_SUCCESS = 'AUTHENTICATION_SUCCESS';
  const AUTHENTICATION_FAILURE = 'AUTHENTICATION_FAILURE';
  const USER_EMAIL_DECLINE = 'USER_EMAIL_DECLINE';
  const USER_EMAIL_NEW_STARTED = 'USER_EMAIL_NEW_STARTED';
  const USER_EMAIL_NEW_SUCCESS = 'USER_EMAIL_NEW_SUCCESS';
  const USER_EMAIL_NEW_EXISTS = 'USER_EMAIL_NEW_EXISTS';
  const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE';
  const USER_EMAIL_VERIFY_SET = 'USER_EMAIL_VERIFY_SET';
  const USER_EMAIL_VERIFY_STARTED = 'USER_EMAIL_VERIFY_STARTED';
  const USER_EMAIL_VERIFY_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS';
  const USER_EMAIL_VERIFY_FAILURE = 'USER_EMAIL_VERIFY_FAILURE';
  const USER_EMAIL_VERIFY_RETRY = 'USER_EMAIL_VERIFY_RETRY';
  const USER_PHONE_RESET = 'USER_PHONE_RESET';
  const USER_PHONE_NEW_STARTED = 'USER_PHONE_NEW_STARTED';
  const USER_PHONE_NEW_SUCCESS = 'USER_PHONE_NEW_SUCCESS';
  const USER_PHONE_NEW_FAILURE = 'USER_PHONE_NEW_FAILURE';
  const USER_PHONE_VERIFY_STARTED = 'USER_PHONE_VERIFY_STARTED';
  const USER_PHONE_VERIFY_SUCCESS = 'USER_PHONE_VERIFY_SUCCESS';
  const USER_PHONE_VERIFY_FAILURE = 'USER_PHONE_VERIFY_FAILURE';
  const USER_IDENTITY_VERIFY_STARTED = 'USER_IDENTITY_VERIFY_STARTED';
  const USER_IDENTITY_VERIFY_SUCCESS = 'USER_IDENTITY_VERIFY_SUCCESS';
  const USER_IDENTITY_VERIFY_FAILURE = 'USER_IDENTITY_VERIFY_FAILURE';
  const USER_FETCH_STARTED = 'USER_FETCH_STARTED';
  const USER_FETCH_SUCCESS = 'USER_FETCH_SUCCESS';
  const USER_FETCH_FAILURE = 'USER_FETCH_FAILURE';
  const USER_INVITE_STATUS_FETCH_STARTED = 'USER_INVITE_STATUS_FETCH_STARTED';
  const USER_INVITE_STATUS_FETCH_SUCCESS = 'USER_INVITE_STATUS_FETCH_SUCCESS';
  const USER_INVITE_STATUS_FETCH_FAILURE = 'USER_INVITE_STATUS_FETCH_FAILURE';
  const USER_INVITE_NEW_STARTED = 'USER_INVITE_NEW_STARTED';
  const USER_INVITE_NEW_SUCCESS = 'USER_INVITE_NEW_SUCCESS';
  const USER_INVITE_NEW_FAILURE = 'USER_INVITE_NEW_FAILURE';
  const FETCH_ACCESS_TOKEN_SUCCESS = 'FETCH_ACCESS_TOKEN_SUCCESS';
  
  // Rewards
  const FETCH_REWARDS_STARTED = 'FETCH_REWARDS_STARTED';
  const FETCH_REWARDS_COMPLETED = 'FETCH_REWARDS_COMPLETED';
  const CLAIM_REWARD_STARTED = 'CLAIM_REWARD_STARTED';
  const CLAIM_REWARD_SUCCESS = 'CLAIM_REWARD_SUCCESS';
  const CLAIM_REWARD_FAILURE = 'CLAIM_REWARD_FAILURE';
  const CLAIM_REWARD_CLEAR_ERROR = 'CLAIM_REWARD_CLEAR_ERROR';
  const FETCH_REWARD_CONTENT_COMPLETED = 'FETCH_REWARD_CONTENT_COMPLETED';
  
  // Language
  const DOWNLOAD_LANGUAGE_SUCCEEDED = 'DOWNLOAD_LANGUAGE_SUCCEEDED';
  const DOWNLOAD_LANGUAGE_FAILED = 'DOWNLOAD_LANGUAGE_FAILED';
  
  // Subscriptions
  const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
  const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
  const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
  const SET_SUBSCRIPTION_LATEST = 'SET_SUBSCRIPTION_LATEST';
  const SET_SUBSCRIPTION_NOTIFICATION = 'SET_SUBSCRIPTION_NOTIFICATION';
  const SET_SUBSCRIPTION_NOTIFICATIONS = 'SET_SUBSCRIPTION_NOTIFICATIONS';
  const CHECK_SUBSCRIPTION_STARTED = 'CHECK_SUBSCRIPTION_STARTED';
  const CHECK_SUBSCRIPTION_COMPLETED = 'CHECK_SUBSCRIPTION_COMPLETED';
  const CHECK_SUBSCRIPTIONS_SUBSCRIBE = 'CHECK_SUBSCRIPTIONS_SUBSCRIBE';
  
  // Publishing
  const CLEAR_PUBLISH = 'CLEAR_PUBLISH';
  const UPDATE_PUBLISH_FORM = 'UPDATE_PUBLISH_FORM';
  const PUBLISH_START = 'PUBLISH_START';
  const PUBLISH_SUCCESS = 'PUBLISH_SUCCESS';
  const PUBLISH_FAIL = 'PUBLISH_FAIL';
  const CLEAR_PUBLISH_ERROR = 'CLEAR_PUBLISH_ERROR';
  const REMOVE_PENDING_PUBLISH = 'REMOVE_PENDING_PUBLISH';
  const DO_PREPARE_EDIT = 'DO_PREPARE_EDIT';
  
  // Notifications
  const CREATE_NOTIFICATION = 'CREATE_NOTIFICATION';
  const EDIT_NOTIFICATION = 'EDIT_NOTIFICATION';
  const DELETE_NOTIFICATION = 'DELETE_NOTIFICATION';
  const DISMISS_NOTIFICATION = 'DISMISS_NOTIFICATION';
  const CREATE_TOAST = 'CREATE_TOAST';
  const DISMISS_TOAST = 'DISMISS_TOAST';
  const CREATE_ERROR = 'CREATE_ERROR';
  const DISMISS_ERROR = 'DISMISS_ERROR';
  
  const FETCH_DATE = 'FETCH_DATE';
  
  // Cost info
  const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED';
  const FETCH_COST_INFO_COMPLETED = 'FETCH_COST_INFO_COMPLETED';
  const FETCH_COST_INFO_FAILED = 'FETCH_COST_INFO_FAILED';
  
  // Sync
  const USER_STATE_POPULATE = 'USER_STATE_POPULATE';
  const SYNC_FATAL_ERROR = 'SYNC_FATAL_ERROR';
  
  var action_types = /*#__PURE__*/Object.freeze({
    WINDOW_FOCUSED: WINDOW_FOCUSED,
    DAEMON_READY: DAEMON_READY,
    DAEMON_VERSION_MATCH: DAEMON_VERSION_MATCH,
    DAEMON_VERSION_MISMATCH: DAEMON_VERSION_MISMATCH,
    VOLUME_CHANGED: VOLUME_CHANGED,
    SET_WELCOME_VERSION: SET_WELCOME_VERSION,
    SET_ALLOW_ANALYTICS: SET_ALLOW_ANALYTICS,
    CHANGE_AFTER_AUTH_PATH: CHANGE_AFTER_AUTH_PATH,
    WINDOW_SCROLLED: WINDOW_SCROLLED,
    HISTORY_NAVIGATE: HISTORY_NAVIGATE,
    UPGRADE_CANCELLED: UPGRADE_CANCELLED,
    DOWNLOAD_UPGRADE: DOWNLOAD_UPGRADE,
    UPGRADE_DOWNLOAD_STARTED: UPGRADE_DOWNLOAD_STARTED,
    UPGRADE_DOWNLOAD_COMPLETED: UPGRADE_DOWNLOAD_COMPLETED,
    UPGRADE_DOWNLOAD_PROGRESSED: UPGRADE_DOWNLOAD_PROGRESSED,
    CHECK_UPGRADE_AVAILABLE: CHECK_UPGRADE_AVAILABLE,
    CHECK_UPGRADE_START: CHECK_UPGRADE_START,
    CHECK_UPGRADE_SUCCESS: CHECK_UPGRADE_SUCCESS,
    CHECK_UPGRADE_FAIL: CHECK_UPGRADE_FAIL,
    CHECK_UPGRADE_SUBSCRIBE: CHECK_UPGRADE_SUBSCRIBE,
    UPDATE_VERSION: UPDATE_VERSION,
    UPDATE_REMOTE_VERSION: UPDATE_REMOTE_VERSION,
    SKIP_UPGRADE: SKIP_UPGRADE,
    START_UPGRADE: START_UPGRADE,
    AUTO_UPDATE_DECLINED: AUTO_UPDATE_DECLINED,
    AUTO_UPDATE_DOWNLOADED: AUTO_UPDATE_DOWNLOADED,
    CLEAR_UPGRADE_TIMER: CLEAR_UPGRADE_TIMER,
    GET_NEW_ADDRESS_STARTED: GET_NEW_ADDRESS_STARTED,
    GET_NEW_ADDRESS_COMPLETED: GET_NEW_ADDRESS_COMPLETED,
    FETCH_TRANSACTIONS_STARTED: FETCH_TRANSACTIONS_STARTED,
    FETCH_TRANSACTIONS_COMPLETED: FETCH_TRANSACTIONS_COMPLETED,
    FETCH_TXO_PAGE_STARTED: FETCH_TXO_PAGE_STARTED,
    FETCH_TXO_PAGE_COMPLETED: FETCH_TXO_PAGE_COMPLETED,
    FETCH_TXO_PAGE_FAILED: FETCH_TXO_PAGE_FAILED,
    UPDATE_TXO_FETCH_PARAMS: UPDATE_TXO_FETCH_PARAMS,
    FETCH_SUPPORTS_STARTED: FETCH_SUPPORTS_STARTED,
    FETCH_SUPPORTS_COMPLETED: FETCH_SUPPORTS_COMPLETED,
    ABANDON_SUPPORT_STARTED: ABANDON_SUPPORT_STARTED,
    ABANDON_SUPPORT_COMPLETED: ABANDON_SUPPORT_COMPLETED,
    ABANDON_CLAIM_SUPPORT_STARTED: ABANDON_CLAIM_SUPPORT_STARTED,
    ABANDON_CLAIM_SUPPORT_COMPLETED: ABANDON_CLAIM_SUPPORT_COMPLETED,
    ABANDON_CLAIM_SUPPORT_FAILED: ABANDON_CLAIM_SUPPORT_FAILED,
    ABANDON_CLAIM_SUPPORT_PREVIEW: ABANDON_CLAIM_SUPPORT_PREVIEW,
    PENDING_SUPPORTS_UPDATED: PENDING_SUPPORTS_UPDATED,
    UPDATE_BALANCE: UPDATE_BALANCE,
    UPDATE_TOTAL_BALANCE: UPDATE_TOTAL_BALANCE,
    CHECK_ADDRESS_IS_MINE_STARTED: CHECK_ADDRESS_IS_MINE_STARTED,
    CHECK_ADDRESS_IS_MINE_COMPLETED: CHECK_ADDRESS_IS_MINE_COMPLETED,
    SEND_TRANSACTION_STARTED: SEND_TRANSACTION_STARTED,
    SEND_TRANSACTION_COMPLETED: SEND_TRANSACTION_COMPLETED,
    SEND_TRANSACTION_FAILED: SEND_TRANSACTION_FAILED,
    SUPPORT_TRANSACTION_STARTED: SUPPORT_TRANSACTION_STARTED,
    SUPPORT_TRANSACTION_COMPLETED: SUPPORT_TRANSACTION_COMPLETED,
    SUPPORT_TRANSACTION_FAILED: SUPPORT_TRANSACTION_FAILED,
    CLEAR_SUPPORT_TRANSACTION: CLEAR_SUPPORT_TRANSACTION,
    WALLET_ENCRYPT_START: WALLET_ENCRYPT_START,
    WALLET_ENCRYPT_COMPLETED: WALLET_ENCRYPT_COMPLETED,
    WALLET_ENCRYPT_FAILED: WALLET_ENCRYPT_FAILED,
    WALLET_UNLOCK_START: WALLET_UNLOCK_START,
    WALLET_UNLOCK_COMPLETED: WALLET_UNLOCK_COMPLETED,
    WALLET_UNLOCK_FAILED: WALLET_UNLOCK_FAILED,
    WALLET_DECRYPT_START: WALLET_DECRYPT_START,
    WALLET_DECRYPT_COMPLETED: WALLET_DECRYPT_COMPLETED,
    WALLET_DECRYPT_FAILED: WALLET_DECRYPT_FAILED,
    WALLET_LOCK_START: WALLET_LOCK_START,
    WALLET_LOCK_COMPLETED: WALLET_LOCK_COMPLETED,
    WALLET_LOCK_FAILED: WALLET_LOCK_FAILED,
    WALLET_STATUS_START: WALLET_STATUS_START,
    WALLET_STATUS_COMPLETED: WALLET_STATUS_COMPLETED,
    WALLET_RESTART: WALLET_RESTART,
    WALLET_RESTART_COMPLETED: WALLET_RESTART_COMPLETED,
    SET_TRANSACTION_LIST_FILTER: SET_TRANSACTION_LIST_FILTER,
    UPDATE_CURRENT_HEIGHT: UPDATE_CURRENT_HEIGHT,
    SET_DRAFT_TRANSACTION_AMOUNT: SET_DRAFT_TRANSACTION_AMOUNT,
    SET_DRAFT_TRANSACTION_ADDRESS: SET_DRAFT_TRANSACTION_ADDRESS,
    FETCH_UTXO_COUNT_STARTED: FETCH_UTXO_COUNT_STARTED,
    FETCH_UTXO_COUNT_COMPLETED: FETCH_UTXO_COUNT_COMPLETED,
    FETCH_UTXO_COUNT_FAILED: FETCH_UTXO_COUNT_FAILED,
    TIP_CLAIM_MASS_STARTED: TIP_CLAIM_MASS_STARTED,
    TIP_CLAIM_MASS_COMPLETED: TIP_CLAIM_MASS_COMPLETED,
    TIP_CLAIM_MASS_FAILED: TIP_CLAIM_MASS_FAILED,
    DO_UTXO_CONSOLIDATE_STARTED: DO_UTXO_CONSOLIDATE_STARTED,
    DO_UTXO_CONSOLIDATE_COMPLETED: DO_UTXO_CONSOLIDATE_COMPLETED,
    DO_UTXO_CONSOLIDATE_FAILED: DO_UTXO_CONSOLIDATE_FAILED,
    PENDING_CONSOLIDATED_TXOS_UPDATED: PENDING_CONSOLIDATED_TXOS_UPDATED,
    RESOLVE_URIS_STARTED: RESOLVE_URIS_STARTED,
    RESOLVE_URIS_COMPLETED: RESOLVE_URIS_COMPLETED,
    FETCH_CHANNEL_CLAIMS_STARTED: FETCH_CHANNEL_CLAIMS_STARTED,
    FETCH_CHANNEL_CLAIMS_COMPLETED: FETCH_CHANNEL_CLAIMS_COMPLETED,
    FETCH_CLAIM_LIST_MINE_STARTED: FETCH_CLAIM_LIST_MINE_STARTED,
    FETCH_CLAIM_LIST_MINE_COMPLETED: FETCH_CLAIM_LIST_MINE_COMPLETED,
    ABANDON_CLAIM_STARTED: ABANDON_CLAIM_STARTED,
    ABANDON_CLAIM_SUCCEEDED: ABANDON_CLAIM_SUCCEEDED,
    FETCH_CHANNEL_LIST_STARTED: FETCH_CHANNEL_LIST_STARTED,
    FETCH_CHANNEL_LIST_COMPLETED: FETCH_CHANNEL_LIST_COMPLETED,
    FETCH_CHANNEL_LIST_FAILED: FETCH_CHANNEL_LIST_FAILED,
    FETCH_COLLECTION_LIST_STARTED: FETCH_COLLECTION_LIST_STARTED,
    FETCH_COLLECTION_LIST_COMPLETED: FETCH_COLLECTION_LIST_COMPLETED,
    FETCH_COLLECTION_LIST_FAILED: FETCH_COLLECTION_LIST_FAILED,
    CREATE_CHANNEL_STARTED: CREATE_CHANNEL_STARTED,
    CREATE_CHANNEL_COMPLETED: CREATE_CHANNEL_COMPLETED,
    CREATE_CHANNEL_FAILED: CREATE_CHANNEL_FAILED,
    UPDATE_CHANNEL_STARTED: UPDATE_CHANNEL_STARTED,
    UPDATE_CHANNEL_COMPLETED: UPDATE_CHANNEL_COMPLETED,
    UPDATE_CHANNEL_FAILED: UPDATE_CHANNEL_FAILED,
    IMPORT_CHANNEL_STARTED: IMPORT_CHANNEL_STARTED,
    IMPORT_CHANNEL_COMPLETED: IMPORT_CHANNEL_COMPLETED,
    IMPORT_CHANNEL_FAILED: IMPORT_CHANNEL_FAILED,
    CLEAR_CHANNEL_ERRORS: CLEAR_CHANNEL_ERRORS,
    PUBLISH_STARTED: PUBLISH_STARTED,
    PUBLISH_COMPLETED: PUBLISH_COMPLETED,
    PUBLISH_FAILED: PUBLISH_FAILED,
    SET_PLAYING_URI: SET_PLAYING_URI,
    SET_CONTENT_POSITION: SET_CONTENT_POSITION,
    SET_CONTENT_LAST_VIEWED: SET_CONTENT_LAST_VIEWED,
    CLEAR_CONTENT_HISTORY_URI: CLEAR_CONTENT_HISTORY_URI,
    CLEAR_CONTENT_HISTORY_ALL: CLEAR_CONTENT_HISTORY_ALL,
    CLAIM_SEARCH_STARTED: CLAIM_SEARCH_STARTED,
    CLAIM_SEARCH_COMPLETED: CLAIM_SEARCH_COMPLETED,
    CLAIM_SEARCH_FAILED: CLAIM_SEARCH_FAILED,
    CLAIM_SEARCH_BY_TAGS_STARTED: CLAIM_SEARCH_BY_TAGS_STARTED,
    CLAIM_SEARCH_BY_TAGS_COMPLETED: CLAIM_SEARCH_BY_TAGS_COMPLETED,
    CLAIM_SEARCH_BY_TAGS_FAILED: CLAIM_SEARCH_BY_TAGS_FAILED,
    CLAIM_REPOST_STARTED: CLAIM_REPOST_STARTED,
    CLAIM_REPOST_COMPLETED: CLAIM_REPOST_COMPLETED,
    CLAIM_REPOST_FAILED: CLAIM_REPOST_FAILED,
    CLEAR_REPOST_ERROR: CLEAR_REPOST_ERROR,
    CHECK_PUBLISH_NAME_STARTED: CHECK_PUBLISH_NAME_STARTED,
    CHECK_PUBLISH_NAME_COMPLETED: CHECK_PUBLISH_NAME_COMPLETED,
    UPDATE_PENDING_CLAIMS: UPDATE_PENDING_CLAIMS,
    UPDATE_CONFIRMED_CLAIMS: UPDATE_CONFIRMED_CLAIMS,
    ADD_FILES_REFLECTING: ADD_FILES_REFLECTING,
    UPDATE_FILES_REFLECTING: UPDATE_FILES_REFLECTING,
    TOGGLE_CHECKING_REFLECTING: TOGGLE_CHECKING_REFLECTING,
    TOGGLE_CHECKING_PENDING: TOGGLE_CHECKING_PENDING,
    PURCHASE_LIST_STARTED: PURCHASE_LIST_STARTED,
    PURCHASE_LIST_COMPLETED: PURCHASE_LIST_COMPLETED,
    PURCHASE_LIST_FAILED: PURCHASE_LIST_FAILED,
    COLLECTION_PUBLISH_STARTED: COLLECTION_PUBLISH_STARTED,
    COLLECTION_PUBLISH_COMPLETED: COLLECTION_PUBLISH_COMPLETED,
    COLLECTION_PUBLISH_FAILED: COLLECTION_PUBLISH_FAILED,
    COLLECTION_PUBLISH_UPDATE_STARTED: COLLECTION_PUBLISH_UPDATE_STARTED,
    COLLECTION_PUBLISH_UPDATE_COMPLETED: COLLECTION_PUBLISH_UPDATE_COMPLETED,
    COLLECTION_PUBLISH_UPDATE_FAILED: COLLECTION_PUBLISH_UPDATE_FAILED,
    COLLECTION_PUBLISH_ABANDON_STARTED: COLLECTION_PUBLISH_ABANDON_STARTED,
    COLLECTION_PUBLISH_ABANDON_COMPLETED: COLLECTION_PUBLISH_ABANDON_COMPLETED,
    COLLECTION_PUBLISH_ABANDON_FAILED: COLLECTION_PUBLISH_ABANDON_FAILED,
    CLEAR_COLLECTION_ERRORS: CLEAR_COLLECTION_ERRORS,
    COLLECTION_ITEMS_RESOLVE_STARTED: COLLECTION_ITEMS_RESOLVE_STARTED,
    COLLECTION_ITEMS_RESOLVE_COMPLETED: COLLECTION_ITEMS_RESOLVE_COMPLETED,
    COLLECTION_ITEMS_RESOLVE_FAILED: COLLECTION_ITEMS_RESOLVE_FAILED,
    COLLECTION_NEW: COLLECTION_NEW,
    COLLECTION_DELETE: COLLECTION_DELETE,
    COLLECTION_PENDING: COLLECTION_PENDING,
    COLLECTION_EDIT: COLLECTION_EDIT,
    COLLECTION_COPY: COLLECTION_COPY,
    COLLECTION_SAVE: COLLECTION_SAVE,
    COLLECTION_ERROR: COLLECTION_ERROR,
    COMMENT_LIST_STARTED: COMMENT_LIST_STARTED,
    COMMENT_LIST_COMPLETED: COMMENT_LIST_COMPLETED,
    COMMENT_LIST_FAILED: COMMENT_LIST_FAILED,
    COMMENT_CREATE_STARTED: COMMENT_CREATE_STARTED,
    COMMENT_CREATE_COMPLETED: COMMENT_CREATE_COMPLETED,
    COMMENT_CREATE_FAILED: COMMENT_CREATE_FAILED,
    COMMENT_ABANDON_STARTED: COMMENT_ABANDON_STARTED,
    COMMENT_ABANDON_COMPLETED: COMMENT_ABANDON_COMPLETED,
    COMMENT_ABANDON_FAILED: COMMENT_ABANDON_FAILED,
    COMMENT_UPDATE_STARTED: COMMENT_UPDATE_STARTED,
    COMMENT_UPDATE_COMPLETED: COMMENT_UPDATE_COMPLETED,
    COMMENT_UPDATE_FAILED: COMMENT_UPDATE_FAILED,
    COMMENT_HIDE_STARTED: COMMENT_HIDE_STARTED,
    COMMENT_HIDE_COMPLETED: COMMENT_HIDE_COMPLETED,
    COMMENT_HIDE_FAILED: COMMENT_HIDE_FAILED,
    FILE_LIST_STARTED: FILE_LIST_STARTED,
    FILE_LIST_SUCCEEDED: FILE_LIST_SUCCEEDED,
    FETCH_FILE_INFO_STARTED: FETCH_FILE_INFO_STARTED,
    FETCH_FILE_INFO_COMPLETED: FETCH_FILE_INFO_COMPLETED,
    FETCH_FILE_INFO_FAILED: FETCH_FILE_INFO_FAILED,
    LOADING_VIDEO_STARTED: LOADING_VIDEO_STARTED,
    LOADING_VIDEO_COMPLETED: LOADING_VIDEO_COMPLETED,
    LOADING_VIDEO_FAILED: LOADING_VIDEO_FAILED,
    DOWNLOADING_STARTED: DOWNLOADING_STARTED,
    DOWNLOADING_PROGRESSED: DOWNLOADING_PROGRESSED,
    DOWNLOADING_COMPLETED: DOWNLOADING_COMPLETED,
    DOWNLOADING_CANCELED: DOWNLOADING_CANCELED,
    PLAY_VIDEO_STARTED: PLAY_VIDEO_STARTED,
    FETCH_AVAILABILITY_STARTED: FETCH_AVAILABILITY_STARTED,
    FETCH_AVAILABILITY_COMPLETED: FETCH_AVAILABILITY_COMPLETED,
    FILE_DELETE: FILE_DELETE,
    SET_FILE_LIST_SORT: SET_FILE_LIST_SORT,
    PURCHASE_URI_STARTED: PURCHASE_URI_STARTED,
    PURCHASE_URI_COMPLETED: PURCHASE_URI_COMPLETED,
    PURCHASE_URI_FAILED: PURCHASE_URI_FAILED,
    CLEAR_PURCHASED_URI_SUCCESS: CLEAR_PURCHASED_URI_SUCCESS,
    DAEMON_SETTINGS_RECEIVED: DAEMON_SETTINGS_RECEIVED,
    DAEMON_STATUS_RECEIVED: DAEMON_STATUS_RECEIVED,
    SHARED_PREFERENCE_SET: SHARED_PREFERENCE_SET,
    SAVE_CUSTOM_WALLET_SERVERS: SAVE_CUSTOM_WALLET_SERVERS,
    CLIENT_SETTING_CHANGED: CLIENT_SETTING_CHANGED,
    UPDATE_IS_NIGHT: UPDATE_IS_NIGHT,
    AUTHENTICATION_STARTED: AUTHENTICATION_STARTED,
    AUTHENTICATION_SUCCESS: AUTHENTICATION_SUCCESS,
    AUTHENTICATION_FAILURE: AUTHENTICATION_FAILURE,
    USER_EMAIL_DECLINE: USER_EMAIL_DECLINE,
    USER_EMAIL_NEW_STARTED: USER_EMAIL_NEW_STARTED,
    USER_EMAIL_NEW_SUCCESS: USER_EMAIL_NEW_SUCCESS,
    USER_EMAIL_NEW_EXISTS: USER_EMAIL_NEW_EXISTS,
    USER_EMAIL_NEW_FAILURE: USER_EMAIL_NEW_FAILURE,
    USER_EMAIL_VERIFY_SET: USER_EMAIL_VERIFY_SET,
    USER_EMAIL_VERIFY_STARTED: USER_EMAIL_VERIFY_STARTED,
    USER_EMAIL_VERIFY_SUCCESS: USER_EMAIL_VERIFY_SUCCESS,
    USER_EMAIL_VERIFY_FAILURE: USER_EMAIL_VERIFY_FAILURE,
    USER_EMAIL_VERIFY_RETRY: USER_EMAIL_VERIFY_RETRY,
    USER_PHONE_RESET: USER_PHONE_RESET,
    USER_PHONE_NEW_STARTED: USER_PHONE_NEW_STARTED,
    USER_PHONE_NEW_SUCCESS: USER_PHONE_NEW_SUCCESS,
    USER_PHONE_NEW_FAILURE: USER_PHONE_NEW_FAILURE,
    USER_PHONE_VERIFY_STARTED: USER_PHONE_VERIFY_STARTED,
    USER_PHONE_VERIFY_SUCCESS: USER_PHONE_VERIFY_SUCCESS,
    USER_PHONE_VERIFY_FAILURE: USER_PHONE_VERIFY_FAILURE,
    USER_IDENTITY_VERIFY_STARTED: USER_IDENTITY_VERIFY_STARTED,
    USER_IDENTITY_VERIFY_SUCCESS: USER_IDENTITY_VERIFY_SUCCESS,
    USER_IDENTITY_VERIFY_FAILURE: USER_IDENTITY_VERIFY_FAILURE,
    USER_FETCH_STARTED: USER_FETCH_STARTED,
    USER_FETCH_SUCCESS: USER_FETCH_SUCCESS,
    USER_FETCH_FAILURE: USER_FETCH_FAILURE,
    USER_INVITE_STATUS_FETCH_STARTED: USER_INVITE_STATUS_FETCH_STARTED,
    USER_INVITE_STATUS_FETCH_SUCCESS: USER_INVITE_STATUS_FETCH_SUCCESS,
    USER_INVITE_STATUS_FETCH_FAILURE: USER_INVITE_STATUS_FETCH_FAILURE,
    USER_INVITE_NEW_STARTED: USER_INVITE_NEW_STARTED,
    USER_INVITE_NEW_SUCCESS: USER_INVITE_NEW_SUCCESS,
    USER_INVITE_NEW_FAILURE: USER_INVITE_NEW_FAILURE,
    FETCH_ACCESS_TOKEN_SUCCESS: FETCH_ACCESS_TOKEN_SUCCESS,
    FETCH_REWARDS_STARTED: FETCH_REWARDS_STARTED,
    FETCH_REWARDS_COMPLETED: FETCH_REWARDS_COMPLETED,
    CLAIM_REWARD_STARTED: CLAIM_REWARD_STARTED,
    CLAIM_REWARD_SUCCESS: CLAIM_REWARD_SUCCESS,
    CLAIM_REWARD_FAILURE: CLAIM_REWARD_FAILURE,
    CLAIM_REWARD_CLEAR_ERROR: CLAIM_REWARD_CLEAR_ERROR,
    FETCH_REWARD_CONTENT_COMPLETED: FETCH_REWARD_CONTENT_COMPLETED,
    DOWNLOAD_LANGUAGE_SUCCEEDED: DOWNLOAD_LANGUAGE_SUCCEEDED,
    DOWNLOAD_LANGUAGE_FAILED: DOWNLOAD_LANGUAGE_FAILED,
    CHANNEL_SUBSCRIBE: CHANNEL_SUBSCRIBE,
    CHANNEL_UNSUBSCRIBE: CHANNEL_UNSUBSCRIBE,
    HAS_FETCHED_SUBSCRIPTIONS: HAS_FETCHED_SUBSCRIPTIONS,
    SET_SUBSCRIPTION_LATEST: SET_SUBSCRIPTION_LATEST,
    SET_SUBSCRIPTION_NOTIFICATION: SET_SUBSCRIPTION_NOTIFICATION,
    SET_SUBSCRIPTION_NOTIFICATIONS: SET_SUBSCRIPTION_NOTIFICATIONS,
    CHECK_SUBSCRIPTION_STARTED: CHECK_SUBSCRIPTION_STARTED,
    CHECK_SUBSCRIPTION_COMPLETED: CHECK_SUBSCRIPTION_COMPLETED,
    CHECK_SUBSCRIPTIONS_SUBSCRIBE: CHECK_SUBSCRIPTIONS_SUBSCRIBE,
    CLEAR_PUBLISH: CLEAR_PUBLISH,
    UPDATE_PUBLISH_FORM: UPDATE_PUBLISH_FORM,
    PUBLISH_START: PUBLISH_START,
    PUBLISH_SUCCESS: PUBLISH_SUCCESS,
    PUBLISH_FAIL: PUBLISH_FAIL,
    CLEAR_PUBLISH_ERROR: CLEAR_PUBLISH_ERROR,
    REMOVE_PENDING_PUBLISH: REMOVE_PENDING_PUBLISH,
    DO_PREPARE_EDIT: DO_PREPARE_EDIT,
    CREATE_NOTIFICATION: CREATE_NOTIFICATION,
    EDIT_NOTIFICATION: EDIT_NOTIFICATION,
    DELETE_NOTIFICATION: DELETE_NOTIFICATION,
    DISMISS_NOTIFICATION: DISMISS_NOTIFICATION,
    CREATE_TOAST: CREATE_TOAST,
    DISMISS_TOAST: DISMISS_TOAST,
    CREATE_ERROR: CREATE_ERROR,
    DISMISS_ERROR: DISMISS_ERROR,
    FETCH_DATE: FETCH_DATE,
    FETCH_COST_INFO_STARTED: FETCH_COST_INFO_STARTED,
    FETCH_COST_INFO_COMPLETED: FETCH_COST_INFO_COMPLETED,
    FETCH_COST_INFO_FAILED: FETCH_COST_INFO_FAILED,
    USER_STATE_POPULATE: USER_STATE_POPULATE,
    SYNC_FATAL_ERROR: SYNC_FATAL_ERROR
  });
  
  const CC_LICENSES = [{
    value: 'Creative Commons Attribution 4.0 International',
    url: 'https://creativecommons.org/licenses/by/4.0/legalcode'
  }, {
    value: 'Creative Commons Attribution-ShareAlike 4.0 International',
    url: 'https://creativecommons.org/licenses/by-sa/4.0/legalcode'
  }, {
    value: 'Creative Commons Attribution-NoDerivatives 4.0 International',
    url: 'https://creativecommons.org/licenses/by-nd/4.0/legalcode'
  }, {
    value: 'Creative Commons Attribution-NonCommercial 4.0 International',
    url: 'https://creativecommons.org/licenses/by-nc/4.0/legalcode'
  }, {
    value: 'Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International',
    url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode'
  }, {
    value: 'Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International',
    url: 'https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode'
  }];
  
  const NONE = 'None';
  const PUBLIC_DOMAIN = 'Public Domain';
  const OTHER = 'other';
  const COPYRIGHT = 'copyright';
  
  var licenses = /*#__PURE__*/Object.freeze({
    CC_LICENSES: CC_LICENSES,
    NONE: NONE,
    PUBLIC_DOMAIN: PUBLIC_DOMAIN,
    OTHER: OTHER,
    COPYRIGHT: COPYRIGHT
  });
  
  const AUTH = 'auth';
  const BACKUP = 'backup';
  const CHANNEL = 'channel';
  const DISCOVER = 'discover';
  const FILE = 'file';
  const DOWNLOADED = 'downloaded';
  const PUBLISHED = 'published';
  const GET_CREDITS = 'getcredits';
  const HELP = 'help';
  const INVITE = 'invite';
  const PUBLISH = 'publish';
  const REPORT = 'report';
  const REWARDS = 'rewards';
  const SEARCH = 'search';
  const SEND_CREDITS = 'send';
  const SETTINGS = 'settings';
  const SHOW = 'show';
  const SUBSCRIPTIONS = 'subscriptions';
  const TRANSACTION_HISTORY = 'history';
  const HISTORY = 'user_history';
  const WALLET = 'wallet';
  
  var pages = /*#__PURE__*/Object.freeze({
    AUTH: AUTH,
    BACKUP: BACKUP,
    CHANNEL: CHANNEL,
    DISCOVER: DISCOVER,
    FILE: FILE,
    DOWNLOADED: DOWNLOADED,
    PUBLISHED: PUBLISHED,
    GET_CREDITS: GET_CREDITS,
    HELP: HELP,
    INVITE: INVITE,
    PUBLISH: PUBLISH,
    REPORT: REPORT,
    REWARDS: REWARDS,
    SEARCH: SEARCH,
    SEND_CREDITS: SEND_CREDITS,
    SETTINGS: SETTINGS,
    SHOW: SHOW,
    SUBSCRIPTIONS: SUBSCRIPTIONS,
    TRANSACTION_HISTORY: TRANSACTION_HISTORY,
    HISTORY: HISTORY,
    WALLET: WALLET
  });
  
  /* hardcoded names still exist for these in reducers/settings.js - only discovered when debugging */
  /* Many SETTINGS are stored in the localStorage by their name -
      be careful about changing the value of a SETTINGS constant, as doing so can invalidate existing SETTINGS */
  
  const SHOW_NSFW = 'showNsfw';
  const CREDIT_REQUIRED_ACKNOWLEDGED = 'credit_required_acknowledged';
  const NEW_USER_ACKNOWLEDGED = 'welcome_acknowledged';
  const EMAIL_COLLECTION_ACKNOWLEDGED = 'email_collection_acknowledged';
  const FIRST_RUN_STARTED = 'first_run_started';
  const INVITE_ACKNOWLEDGED = 'invite_acknowledged';
  const FOLLOWING_ACKNOWLEDGED = 'following_acknowledged';
  const TAGS_ACKNOWLEDGED = 'tags_acknowledged';
  const REWARDS_ACKNOWLEDGED = 'rewards_acknowledged';
  const LANGUAGE = 'language';
  const SEARCH_IN_LANGUAGE = 'search_in_language';
  const SHOW_MATURE = 'show_mature';
  const HOMEPAGE = 'homepage';
  const HIDE_REPOSTS = 'hide_reposts';
  const SHOW_ANONYMOUS = 'show_anonymous';
  const SHOW_UNAVAILABLE = 'show_unavailable';
  const INSTANT_PURCHASE_ENABLED = 'instant_purchase_enabled';
  const INSTANT_PURCHASE_MAX = 'instant_purchase_max';
  const THEME = 'theme';
  const THEMES = 'themes';
  const AUTOMATIC_DARK_MODE_ENABLED = 'automatic_dark_mode_enabled';
  const AUTOPLAY = 'autoplay';
  const AUTOPLAY_NEXT = 'autoplay_next';
  const OS_NOTIFICATIONS_ENABLED = 'os_notifications_enabled';
  const AUTO_DOWNLOAD = 'auto_download';
  const AUTO_LAUNCH = 'auto_launch';
  const TO_TRAY_WHEN_CLOSED = 'to_tray_when_closed';
  const SUPPORT_OPTION = 'support_option';
  const HIDE_BALANCE = 'hide_balance';
  const HIDE_SPLASH_ANIMATION = 'hide_splash_animation';
  const FLOATING_PLAYER = 'floating_player';
  const DARK_MODE_TIMES = 'dark_mode_times';
  const ENABLE_SYNC = 'enable_sync';
  const ENABLE_PUBLISH_PREVIEW = 'enable-publish-preview';
  const TILE_LAYOUT = 'tile_layout';
  const VIDEO_THEATER_MODE = 'video_theater_mode';
  const VIDEO_PLAYBACK_RATE = 'video_playback_rate';
  
  // mobile settings
  const BACKGROUND_PLAY_ENABLED = 'backgroundPlayEnabled';
  const FOREGROUND_NOTIFICATION_ENABLED = 'foregroundNotificationEnabled';
  const KEEP_DAEMON_RUNNING = 'keepDaemonRunning';
  const SHOW_URI_BAR_SUGGESTIONS = 'showUriBarSuggestions';
  const RECEIVE_SUBSCRIPTION_NOTIFICATIONS = 'receiveSubscriptionNotifications';
  const RECEIVE_REWARD_NOTIFICATIONS = 'receiveRewardNotifications';
  const RECEIVE_INTERESTS_NOTIFICATIONS = 'receiveInterestsNotifications';
  const RECEIVE_CREATOR_NOTIFICATIONS = 'receiveCreatorNotifications';
  
  var settings = /*#__PURE__*/Object.freeze({
    SHOW_NSFW: SHOW_NSFW,
    CREDIT_REQUIRED_ACKNOWLEDGED: CREDIT_REQUIRED_ACKNOWLEDGED,
    NEW_USER_ACKNOWLEDGED: NEW_USER_ACKNOWLEDGED,
    EMAIL_COLLECTION_ACKNOWLEDGED: EMAIL_COLLECTION_ACKNOWLEDGED,
    FIRST_RUN_STARTED: FIRST_RUN_STARTED,
    INVITE_ACKNOWLEDGED: INVITE_ACKNOWLEDGED,
    FOLLOWING_ACKNOWLEDGED: FOLLOWING_ACKNOWLEDGED,
    TAGS_ACKNOWLEDGED: TAGS_ACKNOWLEDGED,
    REWARDS_ACKNOWLEDGED: REWARDS_ACKNOWLEDGED,
    LANGUAGE: LANGUAGE,
    SEARCH_IN_LANGUAGE: SEARCH_IN_LANGUAGE,
    SHOW_MATURE: SHOW_MATURE,
    HOMEPAGE: HOMEPAGE,
    HIDE_REPOSTS: HIDE_REPOSTS,
    SHOW_ANONYMOUS: SHOW_ANONYMOUS,
    SHOW_UNAVAILABLE: SHOW_UNAVAILABLE,
    INSTANT_PURCHASE_ENABLED: INSTANT_PURCHASE_ENABLED,
    INSTANT_PURCHASE_MAX: INSTANT_PURCHASE_MAX,
    THEME: THEME,
    THEMES: THEMES,
    AUTOMATIC_DARK_MODE_ENABLED: AUTOMATIC_DARK_MODE_ENABLED,
    AUTOPLAY: AUTOPLAY,
    AUTOPLAY_NEXT: AUTOPLAY_NEXT,
    OS_NOTIFICATIONS_ENABLED: OS_NOTIFICATIONS_ENABLED,
    AUTO_DOWNLOAD: AUTO_DOWNLOAD,
    AUTO_LAUNCH: AUTO_LAUNCH,
    TO_TRAY_WHEN_CLOSED: TO_TRAY_WHEN_CLOSED,
    SUPPORT_OPTION: SUPPORT_OPTION,
    HIDE_BALANCE: HIDE_BALANCE,
    HIDE_SPLASH_ANIMATION: HIDE_SPLASH_ANIMATION,
    FLOATING_PLAYER: FLOATING_PLAYER,
    DARK_MODE_TIMES: DARK_MODE_TIMES,
    ENABLE_SYNC: ENABLE_SYNC,
    ENABLE_PUBLISH_PREVIEW: ENABLE_PUBLISH_PREVIEW,
    TILE_LAYOUT: TILE_LAYOUT,
    VIDEO_THEATER_MODE: VIDEO_THEATER_MODE,
    VIDEO_PLAYBACK_RATE: VIDEO_PLAYBACK_RATE,
    BACKGROUND_PLAY_ENABLED: BACKGROUND_PLAY_ENABLED,
    FOREGROUND_NOTIFICATION_ENABLED: FOREGROUND_NOTIFICATION_ENABLED,
    KEEP_DAEMON_RUNNING: KEEP_DAEMON_RUNNING,
    SHOW_URI_BAR_SUGGESTIONS: SHOW_URI_BAR_SUGGESTIONS,
    RECEIVE_SUBSCRIPTION_NOTIFICATIONS: RECEIVE_SUBSCRIPTION_NOTIFICATIONS,
    RECEIVE_REWARD_NOTIFICATIONS: RECEIVE_REWARD_NOTIFICATIONS,
    RECEIVE_INTERESTS_NOTIFICATIONS: RECEIVE_INTERESTS_NOTIFICATIONS,
    RECEIVE_CREATOR_NOTIFICATIONS: RECEIVE_CREATOR_NOTIFICATIONS
  });
  
  const DATE_NEW = 'dateNew';
  const DATE_OLD = 'dateOld';
  const TITLE = 'title';
  const FILENAME = 'filename';
  
  var sort_options = /*#__PURE__*/Object.freeze({
    DATE_NEW: DATE_NEW,
    DATE_OLD: DATE_OLD,
    TITLE: TITLE,
    FILENAME: FILENAME
  });
  
  const API_DOWN = 'apiDown';
  const READY = 'ready';
  const IN_PROGRESS = 'inProgress';
  const COMPLETE = 'complete';
  const MANUAL = 'manual';
  
  var thumbnail_upload_statuses = /*#__PURE__*/Object.freeze({
    API_DOWN: API_DOWN,
    READY: READY,
    IN_PROGRESS: IN_PROGRESS,
    COMPLETE: COMPLETE,
    MANUAL: MANUAL
  });
  
  // eslint-disable-next-line import/prefer-default-export
  const ALL = 'all';
  const SPEND = 'spend';
  const RECEIVE = 'receive';
  const PUBLISH$1 = 'publish';
  const CHANNEL$1 = 'channel';
  const TIP = 'tip';
  const SUPPORT = 'support';
  const UPDATE = 'update';
  const ABANDON = 'abandon';
  
  var transaction_types = /*#__PURE__*/Object.freeze({
    ALL: ALL,
    SPEND: SPEND,
    RECEIVE: RECEIVE,
    PUBLISH: PUBLISH$1,
    CHANNEL: CHANNEL$1,
    TIP: TIP,
    SUPPORT: SUPPORT,
    UPDATE: UPDATE,
    ABANDON: ABANDON
  });
  
  // PAGE SIZE
  const PAGE_SIZE$1 = 50;
  const LATEST_PAGE_SIZE = 20;
  
  var transaction_list = /*#__PURE__*/Object.freeze({
    PAGE_SIZE: PAGE_SIZE$1,
    LATEST_PAGE_SIZE: LATEST_PAGE_SIZE
  });
  
  const PENDING = 'pending';
  const DONE = 'done';
  const READY$1 = 'ready';
  const ERROR = 'error';
  
  var abandon_states = /*#__PURE__*/Object.freeze({
    PENDING: PENDING,
    DONE: DONE,
    READY: READY$1,
    ERROR: ERROR
  });
  
  const ACTIVE = 'active'; // spent, active, all
  const TYPE = 'type'; // all, payment, support, channel, stream, repost
  const SUB_TYPE = 'subtype'; // other, purchase, tip
  const PAGE_SIZE$2 = 'page_size';
  const PAGE = 'page';
  const ALL$1 = 'all';
  // dropdown types
  const SENT = 'sent';
  const RECEIVED = 'received';
  const SUPPORT$1 = 'support';
  const CHANNEL$2 = 'channel';
  const PUBLISH$2 = 'publish';
  const REPOST = 'repost';
  const DROPDOWN_TYPES = [ALL$1, SENT, RECEIVED, SUPPORT$1, CHANNEL$2, PUBLISH$2, REPOST];
  // dropdown subtypes
  const TIP$1 = 'tip';
  const PURCHASE = 'purchase';
  const PAYMENT = 'payment';
  const DROPDOWN_SUBTYPES = [ALL$1, TIP$1, PURCHASE, PAYMENT];
  
  // rpc params
  const TX_TYPE = 'type'; // = other, stream, repost, channel, support, purchase
  const IS_SPENT = 'is_spent';
  const IS_NOT_SPENT = 'is_not_spent';
  const IS_MY_INPUT = 'is_my_input';
  const IS_MY_OUTPUT = 'is_my_output';
  const IS_NOT_MY_INPUT = 'is_not_my_input';
  const IS_NOT_MY_OUTPUT = 'is_not_my_output'; // use to further distinguish payments to self / from self.
  const IS_MY_INPUT_OR_OUTPUT = 'is_my_input_or_output';
  const EXCLUDE_INTERNAL_TRANSFERS = 'exclude_internal_transfers';
  
  // sdk unique types
  const OTHER$1 = 'other';
  const STREAM = 'stream';
  
  const PAGE_SIZE_DEFAULT = 20;
  
  var txo_list = /*#__PURE__*/Object.freeze({
    ACTIVE: ACTIVE,
    TYPE: TYPE,
    SUB_TYPE: SUB_TYPE,
    PAGE_SIZE: PAGE_SIZE$2,
    PAGE: PAGE,
    ALL: ALL$1,
    SENT: SENT,
    RECEIVED: RECEIVED,
    SUPPORT: SUPPORT$1,
    CHANNEL: CHANNEL$2,
    PUBLISH: PUBLISH$2,
    REPOST: REPOST,
    DROPDOWN_TYPES: DROPDOWN_TYPES,
    TIP: TIP$1,
    PURCHASE: PURCHASE,
    PAYMENT: PAYMENT,
    DROPDOWN_SUBTYPES: DROPDOWN_SUBTYPES,
    TX_TYPE: TX_TYPE,
    IS_SPENT: IS_SPENT,
    IS_NOT_SPENT: IS_NOT_SPENT,
    IS_MY_INPUT: IS_MY_INPUT,
    IS_MY_OUTPUT: IS_MY_OUTPUT,
    IS_NOT_MY_INPUT: IS_NOT_MY_INPUT,
    IS_NOT_MY_OUTPUT: IS_NOT_MY_OUTPUT,
    IS_MY_INPUT_OR_OUTPUT: IS_MY_INPUT_OR_OUTPUT,
    EXCLUDE_INTERNAL_TRANSFERS: EXCLUDE_INTERNAL_TRANSFERS,
    OTHER: OTHER$1,
    STREAM: STREAM,
    PAGE_SIZE_DEFAULT: PAGE_SIZE_DEFAULT
  });
  
  const SPEECH_STATUS = 'https://spee.ch/api/config/site/publishing';
  const SPEECH_PUBLISH = 'https://spee.ch/api/claim/publish';
  
  var speech_urls = /*#__PURE__*/Object.freeze({
    SPEECH_STATUS: SPEECH_STATUS,
    SPEECH_PUBLISH: SPEECH_PUBLISH
  });
  
  const ANNOUNCE_HEAD_AND_SD_ONLY = 'announce_head_and_sd_only';
  const API = 'api';
  const BLOB_DOWNLOAD_TIMEOUT = 'blob_download_timeout';
  const BLOB_LRU_CACHE_SIZE = 'blob_lru_cache_size';
  const BLOCKCHAIN_NAME = 'blockchain_name';
  const CACHE_TIME = 'cache_time';
  const COIN_SELECTION_STRATEGY = 'coin_selection_strategy';
  const COMMENT_SERVER = 'comment_server';
  const COMPONENTS_TO_SKIP = 'components_to_skip';
  const CONCURRENT_BLOB_ANNOUNCERS = 'concurrent_blob_announcers';
  const CONCURRENT_REFLECTOR_UPLOADS = 'concurrent_reflector_uploads';
  const CONFIG = 'config';
  const DATA_DIR = 'data_dir';
  const DOWNLOAD_DIR = 'download_dir';
  const DOWNLOAD_TIMEOUT = 'download_timeout';
  const FIXED_PEER_DELAY = 'fixed_peer_delay';
  const KNOWN_DHT_NODES = 'known_dht_nodes';
  const LBRYUM_SERVERS = 'lbryum_servers';
  const MAX_CONNECTIONS_PER_DOWNLOAD = 'max_connections_per_download';
  const MAX_KEY_FEE = 'max_key_fee';
  const DEFAULT_WALLET = 'default_wallet';
  const NETWORK_INTERFACE = 'network_interface';
  const NODE_RPC_TIMEOUT = 'node_rpc_timeout';
  const PEER_CONNECT_TIMEOUT = 'peer_connect_timeout';
  const REFLECT_STREAMS = 'reflect_streams';
  const REFLECTOR_SERVERS = 'reflector_servers';
  const S3_HEADERS_DEPTH = 's3_headers_depth';
  const SAVE_BLOBS = 'save_blobs';
  const SAVE_FILES = 'save_files';
  const SHARE_USAGE_DATA = 'share_usage_data';
  const SPLIT_BUCKETS_UNDER_INDEX = 'split_buckets_under_index';
  const STREAMING_GET = 'streaming_get';
  const STREAMING_SERVER = 'streaming_server';
  const TCP_PORT = 'tcp_port';
  const TRACK_BANDWIDTH = 'track_bandwidth';
  const UDP_PORT = 'udp_port';
  const USE_UPNP = 'use_upnp';
  const WALLET_DIR = 'wallet_dir';
  const WALLETS = 'wallets';
  
  var daemon_settings = /*#__PURE__*/Object.freeze({
    ANNOUNCE_HEAD_AND_SD_ONLY: ANNOUNCE_HEAD_AND_SD_ONLY,
    API: API,
    BLOB_DOWNLOAD_TIMEOUT: BLOB_DOWNLOAD_TIMEOUT,
    BLOB_LRU_CACHE_SIZE: BLOB_LRU_CACHE_SIZE,
    BLOCKCHAIN_NAME: BLOCKCHAIN_NAME,
    CACHE_TIME: CACHE_TIME,
    COIN_SELECTION_STRATEGY: COIN_SELECTION_STRATEGY,
    COMMENT_SERVER: COMMENT_SERVER,
    COMPONENTS_TO_SKIP: COMPONENTS_TO_SKIP,
    CONCURRENT_BLOB_ANNOUNCERS: CONCURRENT_BLOB_ANNOUNCERS,
    CONCURRENT_REFLECTOR_UPLOADS: CONCURRENT_REFLECTOR_UPLOADS,
    CONFIG: CONFIG,
    DATA_DIR: DATA_DIR,
    DOWNLOAD_DIR: DOWNLOAD_DIR,
    DOWNLOAD_TIMEOUT: DOWNLOAD_TIMEOUT,
    FIXED_PEER_DELAY: FIXED_PEER_DELAY,
    KNOWN_DHT_NODES: KNOWN_DHT_NODES,
    LBRYUM_SERVERS: LBRYUM_SERVERS,
    MAX_CONNECTIONS_PER_DOWNLOAD: MAX_CONNECTIONS_PER_DOWNLOAD,
    MAX_KEY_FEE: MAX_KEY_FEE,
    DEFAULT_WALLET: DEFAULT_WALLET,
    NETWORK_INTERFACE: NETWORK_INTERFACE,
    NODE_RPC_TIMEOUT: NODE_RPC_TIMEOUT,
    PEER_CONNECT_TIMEOUT: PEER_CONNECT_TIMEOUT,
    REFLECT_STREAMS: REFLECT_STREAMS,
    REFLECTOR_SERVERS: REFLECTOR_SERVERS,
    S3_HEADERS_DEPTH: S3_HEADERS_DEPTH,
    SAVE_BLOBS: SAVE_BLOBS,
    SAVE_FILES: SAVE_FILES,
    SHARE_USAGE_DATA: SHARE_USAGE_DATA,
    SPLIT_BUCKETS_UNDER_INDEX: SPLIT_BUCKETS_UNDER_INDEX,
    STREAMING_GET: STREAMING_GET,
    STREAMING_SERVER: STREAMING_SERVER,
    TCP_PORT: TCP_PORT,
    TRACK_BANDWIDTH: TRACK_BANDWIDTH,
    UDP_PORT: UDP_PORT,
    USE_UPNP: USE_UPNP,
    WALLET_DIR: WALLET_DIR,
    WALLETS: WALLETS
  });
  
  /*
   * How to use this file:
   * Settings exported from here will trigger the setting to be
   * sent to the preference middleware when set using the
   * usual setDaemonSettings and clearDaemonSettings methods.
   *
   * See redux/settings/actions in the app for where this is used.
   */
  
  // DAEMON
  const SDK_SYNC_KEYS = [LBRYUM_SERVERS, SHARE_USAGE_DATA];
  
  // CLIENT
  const CLIENT_SYNC_KEYS = [SHOW_MATURE, HIDE_REPOSTS, SHOW_ANONYMOUS, INSTANT_PURCHASE_ENABLED, INSTANT_PURCHASE_MAX, THEME, AUTOPLAY, HIDE_BALANCE, HIDE_SPLASH_ANIMATION, FLOATING_PLAYER, DARK_MODE_TIMES, AUTOMATIC_DARK_MODE_ENABLED];
  
  var shared_preferences = /*#__PURE__*/Object.freeze({
    SDK_SYNC_KEYS: SDK_SYNC_KEYS,
    CLIENT_SYNC_KEYS: CLIENT_SYNC_KEYS
  });
  
  const COLLECTION_ID = 'lid';
  const COLLECTION_INDEX = 'linx';
  
  const COL_TYPE_PLAYLIST = 'playlist';
  const COL_TYPE_CHANNELS = 'channelList';
  
  const WATCH_LATER_ID = 'watchlater';
  const FAVORITES_ID = 'favorites';
  const FAVORITE_CHANNELS_ID = 'favoriteChannels';
  const BUILTIN_LISTS = [WATCH_LATER_ID, FAVORITES_ID, FAVORITE_CHANNELS_ID];
  
  const COL_KEY_EDITED = 'edited';
  const COL_KEY_UNPUBLISHED = 'unpublished';
  const COL_KEY_PENDING = 'pending';
  const COL_KEY_SAVED = 'saved';
  
  var collections = /*#__PURE__*/Object.freeze({
    COLLECTION_ID: COLLECTION_ID,
    COLLECTION_INDEX: COLLECTION_INDEX,
    COL_TYPE_PLAYLIST: COL_TYPE_PLAYLIST,
    COL_TYPE_CHANNELS: COL_TYPE_CHANNELS,
    WATCH_LATER_ID: WATCH_LATER_ID,
    FAVORITES_ID: FAVORITES_ID,
    FAVORITE_CHANNELS_ID: FAVORITE_CHANNELS_ID,
    BUILTIN_LISTS: BUILTIN_LISTS,
    COL_KEY_EDITED: COL_KEY_EDITED,
    COL_KEY_UNPUBLISHED: COL_KEY_UNPUBLISHED,
    COL_KEY_PENDING: COL_KEY_PENDING,
    COL_KEY_SAVED: COL_KEY_SAVED
  });
  
  const DEFAULT_FOLLOWED_TAGS = ['art', 'automotive', 'blockchain', 'comedy', 'economics', 'education', 'gaming', 'music', 'news', 'science', 'sports', 'technology'];
  
  const MATURE_TAGS = ['porn', 'porno', 'nsfw', 'mature', 'xxx', 'sex', 'creampie', 'blowjob', 'handjob', 'vagina', 'boobs', 'big boobs', 'big dick', 'pussy', 'cumshot', 'anal', 'hard fucking', 'ass', 'fuck', 'hentai'];
  
  const DEFAULT_ENGLISH_KNOWN_TAGS = ['free speech', 'censorship', 'gaming', 'pop culture', 'entertainment', 'technology', 'music', 'funny', 'education', 'learning', 'news', 'gameplay', 'nature', 'beliefs', 'comedy', 'games', 'film & animation', 'game', 'weapons', 'blockchain', 'video game', 'sports', 'walkthrough', 'lbrytvpaidbeta', 'art', 'pc', 'minecraft', 'playthrough', 'economics', 'automotive', 'play', 'tutorial', 'twitch', 'how to', 'ps4', 'bitcoin', 'fortnite', 'commentary', 'lets play', 'fun', 'politics', 'travel', 'food', 'science', 'xbox', 'liberal', 'democrat', 'progressive', 'survival', 'non-profits', 'activism', 'cryptocurrency', 'playstation', 'nintendo', 'government', 'steam', 'podcast', 'gamer', 'horror', 'conservative', 'reaction', 'trailer', 'love', 'cnn', 'republican', 'political', 'hangoutsonair', 'hoa', 'msnbc', 'cbs', 'anime', 'donald trump', 'fiction', 'fox news', 'crypto', 'ethereum', 'call of duty', 'android', 'multiplayer', 'epic', 'rpg', 'adventure', 'secular talk', 'btc', 'atheist', 'atheism', 'video games', 'ps3', 'cod', 'online', 'agnostic', 'movie', 'fps', 'lets', 'mod', 'world', 'reviews', 'sharefactory', 'space', 'pokemon', 'stream', 'hilarious', 'lol', 'sony', 'god', 'dance', 'pvp', 'tech', 'strategy', 'zombies', 'fail', 'film', 'xbox360', 'animation', 'unboxing', 'money', 'wwe', 'mods', 'indie', 'pubg', 'ios', 'history', 'rap', 'mobile', 'trump', 'hack', 'flat earth', 'trap', 'humor', 'vlogging', 'fox', 'news radio', 'facebook', 'edm', 'fitness', 'vaping', 'hip hop', 'secular', 'jesus', 'song', 'vape', 'guitar', 'remix', 'mining', 'daily', 'diy', 'pets', 'videogame', 'death', 'funny moments', 'religion', 'media', 'viral', 'war', 'nbc', 'freedom', 'gold', 'family', 'meme', 'zombie', 'photography', 'chill', 'sniper', 'computer', 'iphone', 'dragon', 'bible', 'pro', 'overwatch', 'litecoin', 'gta', 'house', 'fire', 'bass', 'truth', 'crash', 'mario', 'league of legends', 'wii', 'mmorpg', 'health', 'marvel', 'racing', 'apple', 'instrumental', 'earth', 'destiny', 'satire', 'race', 'training', 'electronic', 'boss', 'roblox', 'family friendly', 'california', 'react', 'christian', 'mmo', 'twitter', 'help', 'star', 'cars', 'random', 'top 10', 'ninja', 'guns', 'linux', 'lessons', 'vegan', 'future', 'dota 2', 'studio', 'star wars', 'shooting', 'nasa', 'rock', 'league', 'subscribe', 'water', 'gta v', 'car', 'samsung', 'music video', 'skyrim', 'dog', 'comics', 'shooter game', 'bo3', 'halloween', 'liberty', 'eth', 'conspiracy', 'knife', 'fashion', 'stories', 'vapor', 'nvidia', 'cute', 'beat', 'nintendo switch', 'fantasy', 'christmas', 'world of warcraft', 'industry', 'cartoon', 'garden', 'animals', 'windows', 'happy', 'magic', 'memes', 'design', 'tactical', 'fallout 4', 'puzzle', 'parody', 'rv', 'beats', 'building', 'disney', 'drone', 'ps2', 'beach', 'metal', 'christianity', 'business', 'mix', 'bo2', 'cover', 'senate', '4k', 'united states', 'final', 'hero', 'playing', 'dlc', 'ubisoft', 'halo', 'pc gaming', 'raw', 'investing', 'online learning', 'software', 'ark', 'mojang', 'console', 'battle royale', 'canon', 'microsoft', 'camping', 'ufo', 'progressive talk', 'switch', 'fpv', 'arcade', 'school', 'driving', 'bodybuilding', 'drama', 'retro', 'science fiction', 'eggs', 'australia', 'modded', 'rainbow', 'gamers', 'resident evil', 'drawing', 'brasil', 'england', 'hillary clinton', 'singing', 'final fantasy', 'hiphop', 'video blog', 'mature', 'quad', 'noob', 'simulation', 'illuminati', 'poetry', 'dayz', 'manga', 'howto', 'insane', 'press', 'special', 'church', 'ico', 'weird', 'libertarian', 'crafting', 'level', 'comic', 'sandbox', 'daily vlog', 'outdoor', 'black ops', 'sound', 'christ', 'duty', 'juvenile fiction', 'pc game', 'how-to', 'ww2', 'creepy', 'artist', 'galaxy', 'destiny 2', 'new music', 'quest', 'lee', 'pacman', 'super smash bros', 'day', 'survival horror', 'patreon', 'bitcoin price', 'trending', 'open world', 'wii u', 'dope', 'reaper', 'sniping', 'dubstep', 'truck', 'planet', 'dc', 'amazon', 'spirituality', 'universe', 'video game culture', 'community', 'cat', 'aliens', 'tourism', 'altcoins', 'style', 'travel trailer', 'rda', 'gun', 'secret', 'far cry 5', 'auto', 'culture', 'dj', 'mw2', 'lord', 'full time rving', 'role-playing game', 'prank', 'grand theft auto', 'master', 'wrestling', 'sci-fi', 'workout', 'ghost', 'fake news', 'silly', 'season', 'bo4', 'trading', 'extreme', 'economy', 'combat', 'plays', 'muslim', 'pubg mobile', 'clips', 'bo1', 'paypal', 'sims', 'exploration', 'light', 'ripple', 'paranormal', 'football', 'capcom', 'rta', 'discord', 'batman', 'player', 'server', 'anarchy', 'military', 'playlist', 'cosplay', 'rv park', 'rant', 'edit', 'germany', 'reading', 'chris', 'flash', 'loot', 'bitcoin gratis', 'game reviews', 'movies', 'stupid', 'latest news', 'squad gameplay', 'guru', 'timelapse', 'black ops 3', 'holiday', 'soul', 'motivation', 'mw3', 'vacation', 'sega', '19th century', 'pop', 'sims 4', 'post', 'smok', 'island', 'scotland', 'paladins', 'warrior', 'creepypasta', 'role-playing', 'solar', 'vr', 'animal', 'peace', 'consciousness', 'dota', 'audio', 'mass effect', 'humour', 'first look', 'videogames', 'future bass', 'freestyle', 'hardcore', 'portugal', 'dantdm', 'teaser', 'lbry', 'coronavirus', '2020protests', 'covidcuts', 'covid-19', 'LBRYFoundationBoardCandidacy', 'helplbrysavecrypto'];
  
  const DEFAULT_SPANISH_KNOWN_TAGS = ['español', 'tecnología', 'criptomonedas', 'economía', 'bitcoin', 'educación', 'videojuegos', 'música', 'noticias', 'ciencia', 'deportes', 'latinoamérica', 'latam', 'conspiración', 'humor', 'política', 'tutoriales'];
  
  const DEFAULT_KNOWN_TAGS = [...DEFAULT_ENGLISH_KNOWN_TAGS, ...DEFAULT_SPANISH_KNOWN_TAGS];
  
  //      
  
  const CHECK_DAEMON_STARTED_TRY_NUMBER = 200;
  //
  // Basic LBRY sdk connection config
  // Offers a proxy to call LBRY sdk methods
  
  //
  const Lbry = {
    isConnected: false,
    connectPromise: null,
    daemonConnectionString: 'http://localhost:5279',
    alternateConnectionString: '',
    methodsUsingAlternateConnectionString: [],
    apiRequestHeaders: { 'Content-Type': 'application/json-rpc' },
  
    // Allow overriding daemon connection string (e.g. to `/api/proxy` for lbryweb)
    setDaemonConnectionString: value => {
      Lbry.daemonConnectionString = value;
    },
  
    setApiHeader: (key, value) => {
      Lbry.apiRequestHeaders = Object.assign(Lbry.apiRequestHeaders, { [key]: value });
    },
  
    unsetApiHeader: key => {
      Object.keys(Lbry.apiRequestHeaders).includes(key) && delete Lbry.apiRequestHeaders['key'];
    },
    // Allow overriding Lbry methods
    overrides: {},
    setOverride: (methodName, newMethod) => {
      Lbry.overrides[methodName] = newMethod;
    },
    getApiRequestHeaders: () => Lbry.apiRequestHeaders,
  
    // Returns a human readable media type based on the content type or extension of a file that is returned by the sdk
    getMediaType: (contentType, fileName) => {
      if (fileName) {
        const formats = [[/\.(mp4|m4v|webm|flv|f4v|ogv)$/i, 'video'], [/\.(mp3|m4a|aac|wav|flac|ogg|opus)$/i, 'audio'], [/\.(jpeg|jpg|png|gif|svg|webp)$/i, 'image'], [/\.(h|go|ja|java|js|jsx|c|cpp|cs|css|rb|scss|sh|php|py)$/i, 'script'], [/\.(html|json|csv|txt|log|md|markdown|docx|pdf|xml|yml|yaml)$/i, 'document'], [/\.(pdf|odf|doc|docx|epub|org|rtf)$/i, 'e-book'], [/\.(stl|obj|fbx|gcode)$/i, '3D-file'], [/\.(cbr|cbt|cbz)$/i, 'comic-book'], [/\.(lbry)$/i, 'application']];
  
        const res = formats.reduce((ret, testpair) => {
          switch (testpair[0].test(ret)) {
            case true:
              return testpair[1];
            default:
              return ret;
          }
        }, fileName);
        return res === fileName ? 'unknown' : res;
      } else if (contentType) {
        // $FlowFixMe
        return (/^[^/]+/.exec(contentType)[0]
        );
      }
  
      return 'unknown';
    },
  
    //
    // Lbry SDK Methods
    // https://lbry.tech/api/sdk
    //
    status: (params = {}) => daemonCallWithResult('status', params),
    stop: () => daemonCallWithResult('stop', {}),
    version: () => daemonCallWithResult('version', {}),
  
    // Claim fetching and manipulation
    resolve: params => daemonCallWithResult('resolve', params),
    get: params => daemonCallWithResult('get', params),
    claim_search: params => daemonCallWithResult('claim_search', params),
    claim_list: params => daemonCallWithResult('claim_list', params),
    channel_create: params => daemonCallWithResult('channel_create', params),
    channel_update: params => daemonCallWithResult('channel_update', params),
    channel_import: params => daemonCallWithResult('channel_import', params),
    channel_list: params => daemonCallWithResult('channel_list', params),
    stream_abandon: params => daemonCallWithResult('stream_abandon', params),
    stream_list: params => daemonCallWithResult('stream_list', params),
    channel_abandon: params => daemonCallWithResult('channel_abandon', params),
    channel_sign: params => daemonCallWithResult('channel_sign', params),
    support_create: params => daemonCallWithResult('support_create', params),
    support_list: params => daemonCallWithResult('support_list', params),
    stream_repost: params => daemonCallWithResult('stream_repost', params),
    collection_resolve: params => daemonCallWithResult('collection_resolve', params),
    collection_list: params => daemonCallWithResult('collection_list', params),
    collection_create: params => daemonCallWithResult('collection_create', params),
    collection_update: params => daemonCallWithResult('collection_update', params),
  
    // File fetching and manipulation
    file_list: (params = {}) => daemonCallWithResult('file_list', params),
    file_delete: (params = {}) => daemonCallWithResult('file_delete', params),
    file_set_status: (params = {}) => daemonCallWithResult('file_set_status', params),
    blob_delete: (params = {}) => daemonCallWithResult('blob_delete', params),
    blob_list: (params = {}) => daemonCallWithResult('blob_list', params),
  
    // Wallet utilities
    wallet_balance: (params = {}) => daemonCallWithResult('wallet_balance', params),
    wallet_decrypt: () => daemonCallWithResult('wallet_decrypt', {}),
    wallet_encrypt: (params = {}) => daemonCallWithResult('wallet_encrypt', params),
    wallet_unlock: (params = {}) => daemonCallWithResult('wallet_unlock', params),
    wallet_list: (params = {}) => daemonCallWithResult('wallet_list', params),
    wallet_send: (params = {}) => daemonCallWithResult('wallet_send', params),
    wallet_status: (params = {}) => daemonCallWithResult('wallet_status', params),
    address_is_mine: (params = {}) => daemonCallWithResult('address_is_mine', params),
    address_unused: (params = {}) => daemonCallWithResult('address_unused', params),
    address_list: (params = {}) => daemonCallWithResult('address_list', params),
    transaction_list: (params = {}) => daemonCallWithResult('transaction_list', params),
    utxo_release: (params = {}) => daemonCallWithResult('utxo_release', params),
    support_abandon: (params = {}) => daemonCallWithResult('support_abandon', params),
    purchase_list: (params = {}) => daemonCallWithResult('purchase_list', params),
  
    sync_hash: (params = {}) => daemonCallWithResult('sync_hash', params),
    sync_apply: (params = {}) => daemonCallWithResult('sync_apply', params),
  
    // Preferences
    preference_get: (params = {}) => daemonCallWithResult('preference_get', params),
    preference_set: (params = {}) => daemonCallWithResult('preference_set', params),
  
    // Comments
    comment_list: (params = {}) => daemonCallWithResult('comment_list', params),
    comment_create: (params = {}) => daemonCallWithResult('comment_create', params),
    comment_hide: (params = {}) => daemonCallWithResult('comment_hide', params),
    comment_abandon: (params = {}) => daemonCallWithResult('comment_abandon', params),
    comment_update: (params = {}) => daemonCallWithResult('comment_update', params),
  
    // Connect to the sdk
    connect: () => {
      if (Lbry.connectPromise === null) {
        Lbry.connectPromise = new Promise((resolve, reject) => {
          let tryNum = 0;
          // Check every half second to see if the daemon is accepting connections
          function checkDaemonStarted() {
            tryNum += 1;
            Lbry.status().then(resolve).catch(() => {
              if (tryNum <= CHECK_DAEMON_STARTED_TRY_NUMBER) {
                setTimeout(checkDaemonStarted, tryNum < 50 ? 400 : 1000);
              } else {
                reject(new Error('Unable to connect to LBRY'));
              }
            });
          }
  
          checkDaemonStarted();
        });
      }
  
      // Flow thinks this could be empty, but it will always reuturn a promise
      // $FlowFixMe
      return Lbry.connectPromise;
    },
  
    publish: (params = {}) => new Promise((resolve, reject) => {
      if (Lbry.overrides.publish) {
        Lbry.overrides.publish(params).then(resolve, reject);
      } else {
        apiCall('publish', params, resolve, reject);
      }
    })
  };
  
  function checkAndParse(response) {
    if (response.status >= 200 && response.status < 300) {
      return response.json();
    }
    return response.json().then(json => {
      let error;
      if (json.error) {
        const errorMessage = typeof json.error === 'object' ? json.error.message : json.error;
        error = new Error(errorMessage);
      } else {
        error = new Error('Protocol error with unknown response signature');
      }
      return Promise.reject(error);
    });
  }
  
  function apiCall(method, params, resolve, reject) {
    const counter = new Date().getTime();
    const options = {
      method: 'POST',
      headers: Lbry.apiRequestHeaders,
      body: JSON.stringify({
        jsonrpc: '2.0',
        method,
        params,
        id: counter
      })
    };
  
    const connectionString = Lbry.methodsUsingAlternateConnectionString.includes(method) ? Lbry.alternateConnectionString : Lbry.daemonConnectionString;
    return fetch(connectionString + '?m=' + method, options).then(checkAndParse).then(response => {
      const error = response.error || response.result && response.result.error;
  
      if (error) {
        return reject(error);
      }
      return resolve(response.result);
    }).catch(reject);
  }
  
  function daemonCallWithResult(name, params = {}) {
    return new Promise((resolve, reject) => {
      apiCall(name, params, result => {
        resolve(result);
      }, reject);
    });
  }
  
  // This is only for a fallback
  // If there is a Lbry method that is being called by an app, it should be added to /flow-typed/Lbry.js
  const lbryProxy = new Proxy(Lbry, {
    get(target, name) {
      if (name in target) {
        return target[name];
      }
  
      return (params = {}) => new Promise((resolve, reject) => {
        apiCall(name, params, resolve, reject);
      });
    }
  });
  
  //      
  
  const CHECK_LBRYFIRST_STARTED_TRY_NUMBER = 200;
  //
  // Basic LBRYFIRST connection config
  // Offers a proxy to call LBRYFIRST methods
  
  //
  const LbryFirst = {
    isConnected: false,
    connectPromise: null,
    lbryFirstConnectionString: 'http://localhost:1337/rpc',
    apiRequestHeaders: { 'Content-Type': 'application/json' },
  
    // Allow overriding lbryFirst connection string (e.g. to `/api/proxy` for lbryweb)
    setLbryFirstConnectionString: value => {
      LbryFirst.lbryFirstConnectionString = value;
    },
  
    setApiHeader: (key, value) => {
      LbryFirst.apiRequestHeaders = Object.assign(LbryFirst.apiRequestHeaders, { [key]: value });
    },
  
    unsetApiHeader: key => {
      Object.keys(LbryFirst.apiRequestHeaders).includes(key) && delete LbryFirst.apiRequestHeaders['key'];
    },
    // Allow overriding Lbry methods
    overrides: {},
    setOverride: (methodName, newMethod) => {
      LbryFirst.overrides[methodName] = newMethod;
    },
    getApiRequestHeaders: () => LbryFirst.apiRequestHeaders,
  
    //
    // LbryFirst Methods
    //
    status: (params = {}) => lbryFirstCallWithResult('status', params),
    stop: () => lbryFirstCallWithResult('stop', {}),
    version: () => lbryFirstCallWithResult('version', {}),
  
    // Upload to youtube
    upload: (params = {}) => {
      // Only upload when originally publishing for now
      if (!params.file_path) {
        return Promise.resolve();
      }
  
      const uploadParams = {
        Title: params.title,
        Description: params.description,
        FilePath: params.file_path,
        Category: '',
        Keywords: ''
      };
  
      return lbryFirstCallWithResult('youtube.Upload', uploadParams);
    },
  
    hasYTAuth: token => {
      const hasYTAuthParams = {};
      hasYTAuthParams.AuthToken = token;
      return lbryFirstCallWithResult('youtube.HasAuth', hasYTAuthParams);
    },
  
    ytSignup: () => {
      const emptyParams = {};
      return lbryFirstCallWithResult('youtube.Signup', emptyParams);
    },
  
    remove: () => {
      const emptyParams = {};
      return lbryFirstCallWithResult('youtube.Remove', emptyParams);
    },
  
    // Connect to lbry-first
    connect: () => {
      if (LbryFirst.connectPromise === null) {
        LbryFirst.connectPromise = new Promise((resolve, reject) => {
          let tryNum = 0;
          // Check every half second to see if the lbryFirst is accepting connections
          function checkLbryFirstStarted() {
            tryNum += 1;
            LbryFirst.status().then(resolve).catch(() => {
              if (tryNum <= CHECK_LBRYFIRST_STARTED_TRY_NUMBER) {
                setTimeout(checkLbryFirstStarted, tryNum < 50 ? 400 : 1000);
              } else {
                reject(new Error('Unable to connect to LBRY'));
              }
            });
          }
  
          checkLbryFirstStarted();
        });
      }
  
      // Flow thinks this could be empty, but it will always return a promise
      // $FlowFixMe
      return LbryFirst.connectPromise;
    }
  };
  
  function checkAndParse$1(response) {
    if (response.status >= 200 && response.status < 300) {
      return response.json();
    }
    return response.json().then(json => {
      let error;
      if (json.error) {
        const errorMessage = typeof json.error === 'object' ? json.error.message : json.error;
        error = new Error(errorMessage);
      } else {
        error = new Error('Protocol error with unknown response signature');
      }
      return Promise.reject(error);
    });
  }
  
  function apiCall$1(method, params, resolve, reject) {
    const counter = new Date().getTime();
    const paramsArray = [params];
    const options = {
      method: 'POST',
      headers: LbryFirst.apiRequestHeaders,
      body: JSON.stringify({
        jsonrpc: '2.0',
        method,
        params: paramsArray,
        id: counter
      })
    };
  
    return fetch(LbryFirst.lbryFirstConnectionString, options).then(checkAndParse$1).then(response => {
      const error = response.error || response.result && response.result.error;
  
      if (error) {
        return reject(error);
      }
      return resolve(response.result);
    }).catch(reject);
  }
  
  function lbryFirstCallWithResult(name, params = {}) {
    return new Promise((resolve, reject) => {
      apiCall$1(name, params, result => {
        resolve(result);
      }, reject);
    });
  }
  
  // This is only for a fallback
  // If there is a LbryFirst method that is being called by an app, it should be added to /flow-typed/LbryFirst.js
  const lbryFirstProxy = new Proxy(LbryFirst, {
    get(target, name) {
      if (name in target) {
        return target[name];
      }
  
      return (params = {}) => new Promise((resolve, reject) => {
        apiCall$1(name, params, resolve, reject);
      });
    }
  });
  
  var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
  const channelNameMinLength = 1;
  const claimIdMaxLength = 40;
  
  // see https://spec.lbry.com/#urls
  const regexInvalidURI = /[ =&#:$@%?;/\\"<>%\{\}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/u;
  const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
  const regexPartProtocol = '^((?:lbry://)?)';
  const regexPartStreamOrChannelName = '([^:$#/]*)';
  const regexPartModifierSeparator = '([:$#]?)([^/]*)';
  const queryStringBreaker = '^([\\S]+)([?][\\S]*)';
  const separateQuerystring = new RegExp(queryStringBreaker);
  
  /**
   * Parses a LBRY name into its component parts. Throws errors with user-friendly
   * messages for invalid names.
   *
   * Returns a dictionary with keys:
   *   - path (string)
   *   - isChannel (boolean)
   *   - streamName (string, if present)
   *   - streamClaimId (string, if present)
   *   - channelName (string, if present)
   *   - channelClaimId (string, if present)
   *   - primaryClaimSequence (int, if present)
   *   - secondaryClaimSequence (int, if present)
   *   - primaryBidPosition (int, if present)
   *   - secondaryBidPosition (int, if present)
   */
  
  function parseURI(url, requireProto = false) {
    // Break into components. Empty sub-matches are converted to null
  
    const componentsRegex = new RegExp(regexPartProtocol + // protocol
    regexPartStreamOrChannelName + // stream or channel name (stops at the first separator or end)
    regexPartModifierSeparator + // modifier separator, modifier (stops at the first path separator or end)
    '(/?)' + // path separator, there should only be one (optional) slash to separate the stream and channel parts
    regexPartStreamOrChannelName + regexPartModifierSeparator);
    // chop off the querystring first
    let QSStrippedURL, qs;
    const qsRegexResult = separateQuerystring.exec(url);
    if (qsRegexResult) {
      [QSStrippedURL, qs] = qsRegexResult.slice(1).map(match => match || null);
    }
  
    const cleanURL = QSStrippedURL || url;
    const regexMatch = componentsRegex.exec(cleanURL) || [];
    const [proto, ...rest] = regexMatch.slice(1).map(match => match || null);
    const path = rest.join('');
    const [streamNameOrChannelName, primaryModSeparator, primaryModValue, pathSep, possibleStreamName, secondaryModSeparator, secondaryModValue] = rest;
    const searchParams = new URLSearchParams(qs || '');
    const startTime = searchParams.get('t');
  
    // Validate protocol
    if (requireProto && !proto) {
      throw new Error(__('LBRY URLs must include a protocol prefix (lbry://).'));
    }
  
    // Validate and process name
    if (!streamNameOrChannelName) {
      throw new Error(__('URL does not include name.'));
    }
  
    rest.forEach(urlPiece => {
      if (urlPiece && urlPiece.includes(' ')) {
        throw new Error(__('URL can not include a space'));
      }
    });
  
    const includesChannel = streamNameOrChannelName.startsWith('@');
    const isChannel = streamNameOrChannelName.startsWith('@') && !possibleStreamName;
    const channelName = includesChannel && streamNameOrChannelName.slice(1);
  
    if (includesChannel) {
      if (!channelName) {
        throw new Error(__('No channel name after @.'));
      }
  
      if (channelName.length < channelNameMinLength) {
        throw new Error(__(`Channel names must be at least %channelNameMinLength% characters.`, {
          channelNameMinLength
        }));
      }
    }
  
    // Validate and process modifier
    const [primaryClaimId, primaryClaimSequence, primaryBidPosition] = parseURIModifier(primaryModSeparator, primaryModValue);
    const [secondaryClaimId, secondaryClaimSequence, secondaryBidPosition] = parseURIModifier(secondaryModSeparator, secondaryModValue);
    const streamName = includesChannel ? possibleStreamName : streamNameOrChannelName;
    const streamClaimId = includesChannel ? secondaryClaimId : primaryClaimId;
    const channelClaimId = includesChannel && primaryClaimId;
  
    return _extends({
      isChannel,
      path
    }, streamName ? { streamName } : {}, streamClaimId ? { streamClaimId } : {}, channelName ? { channelName } : {}, channelClaimId ? { channelClaimId } : {}, primaryClaimSequence ? { primaryClaimSequence: parseInt(primaryClaimSequence, 10) } : {}, secondaryClaimSequence ? { secondaryClaimSequence: parseInt(secondaryClaimSequence, 10) } : {}, primaryBidPosition ? { primaryBidPosition: parseInt(primaryBidPosition, 10) } : {}, secondaryBidPosition ? { secondaryBidPosition: parseInt(secondaryBidPosition, 10) } : {}, startTime ? { startTime: parseInt(startTime, 10) } : {}, {
  
      // The values below should not be used for new uses of parseURI
      // They will not work properly with canonical_urls
      claimName: streamNameOrChannelName,
      claimId: primaryClaimId
    }, streamName ? { contentName: streamName } : {}, qs ? { queryString: qs } : {});
  }
  
  function parseURIModifier(modSeperator, modValue) {
    let claimId;
    let claimSequence;
    let bidPosition;
  
    if (modSeperator) {
      if (!modValue) {
        throw new Error(__(`No modifier provided after separator %modSeperator%.`, { modSeperator }));
      }
  
      if (modSeperator === '#') {
        claimId = modValue;
      } else if (modSeperator === ':') {
        claimSequence = modValue;
      } else if (modSeperator === '$') {
        bidPosition = modValue;
      }
    }
  
    if (claimId && (claimId.length > claimIdMaxLength || !claimId.match(/^[0-9a-f]+$/))) {
      throw new Error(__(`Invalid claim ID %claimId%.`, { claimId }));
    }
  
    if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) {
      throw new Error(__('Claim sequence must be a number.'));
    }
  
    if (bidPosition && !bidPosition.match(/^-?[1-9][0-9]*$/)) {
      throw new Error(__('Bid position must be a number.'));
    }
  
    return [claimId, claimSequence, bidPosition];
  }
  
  /**
   * Takes an object in the same format returned by parse() and builds a URI.
   *
   * The channelName key will accept names with or without the @ prefix.
   */
  function buildURI(UrlObj, includeProto = true, protoDefault = 'lbry://') {
    const {
      streamName,
      streamClaimId,
      channelName,
      channelClaimId,
      primaryClaimSequence,
      primaryBidPosition,
      secondaryClaimSequence,
      secondaryBidPosition,
      startTime
    } = UrlObj,
          deprecatedParts = _objectWithoutProperties(UrlObj, ['streamName', 'streamClaimId', 'channelName', 'channelClaimId', 'primaryClaimSequence', 'primaryBidPosition', 'secondaryClaimSequence', 'secondaryBidPosition', 'startTime']);
    const { claimId, claimName, contentName } = deprecatedParts;
  
    if (!claimName && !channelName && !streamName) {
      console.error(__("'claimName', 'channelName', and 'streamName' are all empty. One must be present to build a url."));
    }
  
    const formattedChannelName = channelName && (channelName.startsWith('@') ? channelName : `@${channelName}`);
    const primaryClaimName = claimName || contentName || formattedChannelName || streamName;
    const primaryClaimId = claimId || (formattedChannelName ? channelClaimId : streamClaimId);
    const secondaryClaimName = !claimName && contentName || (formattedChannelName ? streamName : null);
    const secondaryClaimId = secondaryClaimName && streamClaimId;
  
    return (includeProto ? protoDefault : '') +
    // primaryClaimName will always exist here because we throw above if there is no "name" value passed in
    // $FlowFixMe
    primaryClaimName + (primaryClaimId ? `#${primaryClaimId}` : '') + (primaryClaimSequence ? `:${primaryClaimSequence}` : '') + (primaryBidPosition ? `${primaryBidPosition}` : '') + (secondaryClaimName ? `/${secondaryClaimName}` : '') + (secondaryClaimId ? `#${secondaryClaimId}` : '') + (secondaryClaimSequence ? `:${secondaryClaimSequence}` : '') + (secondaryBidPosition ? `${secondaryBidPosition}` : '') + (startTime ? `?t=${startTime}` : '');
  }
  
  /* Takes a parseable LBRY URL and converts it to standard, canonical format */
  function normalizeURI(URL) {
    const {
      streamName,
      streamClaimId,
      channelName,
      channelClaimId,
      primaryClaimSequence,
      primaryBidPosition,
      secondaryClaimSequence,
      secondaryBidPosition,
      startTime
    } = parseURI(URL);
  
    return buildURI({
      streamName,
      streamClaimId,
      channelName,
      channelClaimId,
      primaryClaimSequence,
      primaryBidPosition,
      secondaryClaimSequence,
      secondaryBidPosition,
      startTime
    });
  }
  
  function isURIValid(URL) {
    try {
      parseURI(normalizeURI(URL));
    } catch (error) {
      return false;
    }
  
    return true;
  }
  
  function isNameValid(claimName) {
    return !regexInvalidURI.test(claimName);
  }
  
  function isURIClaimable(URL) {
    let parts;
    try {
      parts = parseURI(normalizeURI(URL));
    } catch (error) {
      return false;
    }
  
    return parts && parts.streamName && !parts.streamClaimId && !parts.isChannel;
  }
  
  function convertToShareLink(URL) {
    const {
      streamName,
      streamClaimId,
      channelName,
      channelClaimId,
      primaryBidPosition,
      primaryClaimSequence,
      secondaryBidPosition,
      secondaryClaimSequence
    } = parseURI(URL);
    return buildURI({
      streamName,
      streamClaimId,
      channelName,
      channelClaimId,
      primaryBidPosition,
      primaryClaimSequence,
      secondaryBidPosition,
      secondaryClaimSequence
    }, true, 'https://open.lbry.com/');
  }
  
  /* eslint-disable */
  // underscore's deep equal function
  // https://github.com/jashkenas/underscore/blob/master/underscore.js#L1189
  
  function isEqual(a, b, aStack, bStack) {
    // Identical objects are equal. `0 === -0`, but they aren't identical.
    // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
    if (a === b) return a !== 0 || 1 / a === 1 / b;
    // `null` or `undefined` only equal to itself (strict comparison).
    if (a == null || b == null) return false;
    // `NaN`s are equivalent, but non-reflexive.
    if (a !== a) return b !== b;
    // Exhaust primitive checks
    var type = typeof a;
    if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
    return deepEq(a, b, aStack, bStack);
  }
  
  function deepEq(a, b, aStack, bStack) {
    // Compare `[[Class]]` names.
    var className = toString.call(a);
    if (className !== toString.call(b)) return false;
    switch (className) {
      // Strings, numbers, regular expressions, dates, and booleans are compared by value.
      case '[object RegExp]':
      // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
      case '[object String]':
        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
        // equivalent to `new String("5")`.
        return '' + a === '' + b;
      case '[object Number]':
        // `NaN`s are equivalent, but non-reflexive.
        // Object(NaN) is equivalent to NaN.
        if (+a !== +a) return +b !== +b;
        // An `egal` comparison is performed for other numeric values.
        return +a === 0 ? 1 / +a === 1 / b : +a === +b;
      case '[object Date]':
      case '[object Boolean]':
        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
        // millisecond representations. Note that invalid dates with millisecond representations
        // of `NaN` are not equivalent.
        return +a === +b;
      case '[object Symbol]':
        return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
    }
  
    var areArrays = className === '[object Array]';
    if (!areArrays) {
      if (typeof a != 'object' || typeof b != 'object') return false;
  
      // Objects with different constructors are not equivalent, but `Object`s or `Array`s
      // from different frames are.
      var aCtor = a.constructor,
          bCtor = b.constructor;
      if (aCtor !== bCtor && !(typeof aCtor === 'function' && aCtor instanceof aCtor && typeof bCtor === 'function' && bCtor instanceof bCtor) && 'constructor' in a && 'constructor' in b) {
        return false;
      }
    }
    // Assume equality for cyclic structures. The algorithm for detecting cyclic
    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
  
    // Initializing stack of traversed objects.
    // It's done here since we only need them for objects and arrays comparison.
    aStack = aStack || [];
    bStack = bStack || [];
    var length = aStack.length;
    while (length--) {
      // Linear search. Performance is inversely proportional to the number of
      // unique nested structures.
      if (aStack[length] === a) return bStack[length] === b;
    }
  
    // Add the first object to the stack of traversed objects.
    aStack.push(a);
    bStack.push(b);
  
    // Recursively compare objects and arrays.
    if (areArrays) {
      // Compare array lengths to determine if a deep comparison is necessary.
      length = a.length;
      if (length !== b.length) return false;
      // Deep compare the contents, ignoring non-numeric properties.
      while (length--) {
        if (!isEqual(a[length], b[length], aStack, bStack)) return false;
      }
    } else {
      // Deep compare objects.
      var keys = Object.keys(a),
          key;
      length = keys.length;
      // Ensure that both objects contain the same number of properties before comparing deep equality.
      if (Object.keys(b).length !== length) return false;
      while (length--) {
        // Deep compare each member
        key = keys[length];
        if (!(has(b, key) && isEqual(a[key], b[key], aStack, bStack))) return false;
      }
    }
    // Remove the first object from the stack of traversed objects.
    aStack.pop();
    bStack.pop();
    return true;
  }
  
  function has(obj, path) {
    return obj != null && hasOwnProperty.call(obj, path);
  }
  /* eslint-enable */
  
  var _extends$1 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  function extractUserState(rawObj) {
    if (rawObj && rawObj.version === '0.1' && rawObj.value) {
      const {
        subscriptions,
        following,
        tags,
        blocked,
        coin_swap_codes,
        settings,
        app_welcome_version,
        sharing_3P,
        unpublishedCollections,
        builtinCollections,
        savedCollections
      } = rawObj.value;
  
      return _extends$1({}, subscriptions ? { subscriptions } : {}, following ? { following } : {}, tags ? { tags } : {}, blocked ? { blocked } : {}, coin_swap_codes ? { coin_swap_codes } : {}, settings ? { settings } : {}, app_welcome_version ? { app_welcome_version } : {}, sharing_3P ? { sharing_3P } : {}, unpublishedCollections ? { unpublishedCollections } : {}, builtinCollections ? { builtinCollections } : {}, savedCollections ? { savedCollections } : {});
    }
  
    return {};
  }
  
  function doPopulateSharedUserState(sharedSettings) {
    return dispatch => {
      const {
        subscriptions,
        following,
        tags,
        blocked,
        coin_swap_codes,
        settings,
        app_welcome_version,
        sharing_3P,
        unpublishedCollections,
        builtinCollections,
        savedCollections
      } = extractUserState(sharedSettings);
      dispatch({
        type: USER_STATE_POPULATE,
        data: {
          subscriptions,
          following,
          tags,
          blocked,
          coinSwapCodes: coin_swap_codes,
          settings,
          welcomeVersion: app_welcome_version,
          allowAnalytics: sharing_3P,
          unpublishedCollections,
          builtinCollections,
          savedCollections
        }
      });
    };
  }
  
  function doPreferenceSet(key, value, version, success, fail) {
    return dispatch => {
      const preference = {
        type: typeof value,
        version,
        value
      };
  
      const options = {
        key,
        value: JSON.stringify(preference)
      };
  
      lbryProxy.preference_set(options).then(() => {
        if (success) {
          success(preference);
        }
      }).catch(err => {
        dispatch({
          type: SYNC_FATAL_ERROR,
          error: err
        });
  
        if (fail) {
          fail();
        }
      });
    };
  }
  
  function doPreferenceGet(key, success, fail) {
    return dispatch => {
      const options = {
        key
      };
  
      return lbryProxy.preference_get(options).then(result => {
        if (result) {
          const preference = result[key];
          return success(preference);
        }
  
        return success(null);
      }).catch(err => {
        dispatch({
          type: SYNC_FATAL_ERROR,
          error: err
        });
  
        if (fail) {
          fail(err);
        }
      });
    };
  }
  
  //      
  
  const RUN_PREFERENCES_DELAY_MS = 2000;
  const SHARED_PREFERENCE_VERSION = '0.1';
  let oldShared = {};
  let timeout;
  const buildSharedStateMiddleware = (actions, sharedStateFilters, sharedStateCb) => ({
    getState,
    dispatch
  }) => next => action => {
    const currentState = getState();
  
    // We don't care if sync is disabled here, we always want to backup preferences to the wallet
    if (!actions.includes(action.type) || typeof action === 'function') {
      return next(action);
    }
    clearTimeout(timeout);
    const actionResult = next(action);
    // Call `getState` after calling `next` to ensure the state has updated in response to the action
    function runPreferences() {
      const nextState = getState();
      const syncEnabled = nextState.settings && nextState.settings.clientSettings && nextState.settings.clientSettings.enable_sync;
      const hasVerifiedEmail = nextState.user && nextState.user.user && nextState.user.user.has_verified_email;
      const preferenceKey = syncEnabled && hasVerifiedEmail ? 'shared' : 'local';
      const shared = {};
  
      Object.keys(sharedStateFilters).forEach(key => {
        const filter = sharedStateFilters[key];
        const { source, property, transform } = filter;
        let value = nextState[source][property];
        if (transform) {
          value = transform(value);
        }
  
        shared[key] = value;
      });
  
      if (!isEqual(oldShared, shared)) {
        // only update if the preference changed from last call in the same session
        oldShared = shared;
        dispatch(doPreferenceSet(preferenceKey, shared, SHARED_PREFERENCE_VERSION));
      }
  
      if (sharedStateCb) {
        // Pass dispatch to the callback to consumers can dispatch actions in response to preference set
        sharedStateCb({ dispatch, getState });
      }
      clearTimeout(timeout);
      return actionResult;
    }
    timeout = setTimeout(runPreferences, RUN_PREFERENCES_DELAY_MS);
  };
  
  //      
  
  function doToast(params) {
    if (!params) {
      throw Error("'params' object is required to create a toast notification");
    }
  
    return {
      type: CREATE_TOAST,
      data: {
        id: uuid.v4(),
        params
      }
    };
  }
  
  function doDismissToast() {
    return {
      type: DISMISS_TOAST
    };
  }
  
  function doError(error) {
    return {
      type: CREATE_ERROR,
      data: {
        error
      }
    };
  }
  
  function doDismissError() {
    return {
      type: DISMISS_ERROR
    };
  }
  
  // JSON parser
  const parseJson = (data, filters = []) => {
    const list = data.map(item => {
      const temp = {};
      // Apply filters
      Object.entries(item).forEach(([key, value]) => {
        if (!filters.includes(key)) temp[key] = value;
      });
      return temp;
    });
    // Beautify JSON
    return JSON.stringify(list, null, '\t');
  };
  
  // CSV Parser
  // No need for an external module:
  // https://gist.github.com/btzr-io/55c3450ea3d709fc57540e762899fb85
  const parseCsv = (data, filters = []) => {
    // Get items for header
    const getHeaders = item => {
      const list = [];
      // Apply filters
      Object.entries(item).forEach(([key]) => {
        if (!filters.includes(key)) list.push(key);
      });
      // return headers
      return list.join(',');
    };
  
    // Get rows content
    const getData = list => list.map(item => {
      const row = [];
      // Apply filters
      Object.entries(item).forEach(([key, value]) => {
        if (!filters.includes(key)) row.push(value);
      });
      // return rows
      return row.join(',');
    }).join('\n');
  
    // Return CSV string
    return `${getHeaders(data[0])} \n ${getData(data)}`;
  };
  
  const parseData = (data, format, filters = []) => {
    // Check for validation
    const valid = data && data[0] && format;
    // Pick a format
    const formats = {
      csv: list => parseCsv(list, filters),
      json: list => parseJson(list, filters)
    };
  
    // Return parsed data: JSON || CSV
    return valid && formats[format] ? formats[format](data) : undefined;
  };
  
  const selectState = state => state.wallet || {};
  
  const selectWalletState = selectState;
  
  const selectWalletIsEncrypted = reselect.createSelector(selectState, state => state.walletIsEncrypted);
  
  const selectWalletEncryptPending = reselect.createSelector(selectState, state => state.walletEncryptPending);
  
  const selectWalletEncryptSucceeded = reselect.createSelector(selectState, state => state.walletEncryptSucceded);
  
  const selectPendingSupportTransactions = reselect.createSelector(selectState, state => state.pendingSupportTransactions);
  
  const selectPendingOtherTransactions = reselect.createSelector(selectState, state => state.pendingTxos);
  
  const selectAbandonClaimSupportError = reselect.createSelector(selectState, state => state.abandonClaimSupportError);
  
  const makeSelectPendingAmountByUri = uri => reselect.createSelector(selectClaimIdsByUri, selectPendingSupportTransactions, (claimIdsByUri, pendingSupports) => {
    const uriEntry = Object.entries(claimIdsByUri).find(([u, cid]) => u === uri);
    const claimId = uriEntry && uriEntry[1];
    const pendingSupport = claimId && pendingSupports[claimId];
    return pendingSupport ? pendingSupport.effective : undefined;
  });
  
  const selectWalletEncryptResult = reselect.createSelector(selectState, state => state.walletEncryptResult);
  
  const selectWalletDecryptPending = reselect.createSelector(selectState, state => state.walletDecryptPending);
  
  const selectWalletDecryptSucceeded = reselect.createSelector(selectState, state => state.walletDecryptSucceded);
  
  const selectWalletDecryptResult = reselect.createSelector(selectState, state => state.walletDecryptResult);
  
  const selectWalletUnlockPending = reselect.createSelector(selectState, state => state.walletUnlockPending);
  
  const selectWalletUnlockSucceeded = reselect.createSelector(selectState, state => state.walletUnlockSucceded);
  
  const selectWalletUnlockResult = reselect.createSelector(selectState, state => state.walletUnlockResult);
  
  const selectWalletLockPending = reselect.createSelector(selectState, state => state.walletLockPending);
  
  const selectWalletLockSucceeded = reselect.createSelector(selectState, state => state.walletLockSucceded);
  
  const selectWalletLockResult = reselect.createSelector(selectState, state => state.walletLockResult);
  
  const selectBalance = reselect.createSelector(selectState, state => state.balance);
  
  const selectTotalBalance = reselect.createSelector(selectState, state => state.totalBalance);
  
  const selectReservedBalance = reselect.createSelector(selectState, state => state.reservedBalance);
  
  const selectClaimsBalance = reselect.createSelector(selectState, state => state.claimsBalance);
  
  const selectSupportsBalance = reselect.createSelector(selectState, state => state.supportsBalance);
  
  const selectTipsBalance = reselect.createSelector(selectState, state => state.tipsBalance);
  
  const selectTransactionsById = reselect.createSelector(selectState, state => state.transactions || {});
  
  const selectSupportsByOutpoint = reselect.createSelector(selectState, state => state.supports || {});
  
  const selectTotalSupports = reselect.createSelector(selectSupportsByOutpoint, byOutpoint => {
    let total = parseFloat('0.0');
  
    Object.values(byOutpoint).forEach(support => {
      const { amount } = support;
      total = amount ? total + parseFloat(amount) : total;
    });
  
    return total;
  });
  
  const selectTransactionItems = reselect.createSelector(selectTransactionsById, byId => {
    const items = [];
  
    Object.keys(byId).forEach(txid => {
      const tx = byId[txid];
  
      // ignore dust/fees
      // it is fee only txn if all infos are also empty
      if (Math.abs(tx.value) === Math.abs(tx.fee) && tx.claim_info.length === 0 && tx.support_info.length === 0 && tx.update_info.length === 0 && tx.abandon_info.length === 0) {
        return;
      }
  
      const append = [];
  
      append.push(...tx.claim_info.map(item => Object.assign({}, tx, item, {
        type: item.claim_name[0] === '@' ? CHANNEL$1 : PUBLISH$1
      })));
      append.push(...tx.support_info.map(item => Object.assign({}, tx, item, {
        type: !item.is_tip ? SUPPORT : TIP
      })));
      append.push(...tx.update_info.map(item => Object.assign({}, tx, item, { type: UPDATE })));
      append.push(...tx.abandon_info.map(item => Object.assign({}, tx, item, { type: ABANDON })));
  
      if (!append.length) {
        append.push(Object.assign({}, tx, {
          type: tx.value < 0 ? SPEND : RECEIVE
        }));
      }
  
      items.push(...append.map(item => {
        // value on transaction, amount on outpoint
        // amount is always positive, but should match sign of value
        const balanceDelta = parseFloat(item.balance_delta);
        const value = parseFloat(item.value);
        const amount = balanceDelta || value;
        const fee = parseFloat(tx.fee);
  
        return {
          txid,
          timestamp: tx.timestamp,
          date: tx.timestamp ? new Date(Number(tx.timestamp) * 1000) : null,
          amount,
          fee,
          claim_id: item.claim_id,
          claim_name: item.claim_name,
          type: item.type || SPEND,
          nout: item.nout,
          confirmations: tx.confirmations
        };
      }));
    });
  
    return items.sort((tx1, tx2) => {
      if (!tx1.timestamp && !tx2.timestamp) {
        return 0;
      } else if (!tx1.timestamp && tx2.timestamp) {
        return -1;
      } else if (tx1.timestamp && !tx2.timestamp) {
        return 1;
      }
  
      return tx2.timestamp - tx1.timestamp;
    });
  });
  
  const selectRecentTransactions = reselect.createSelector(selectTransactionItems, transactions => {
    const threshold = new Date();
    threshold.setDate(threshold.getDate() - 7);
    return transactions.filter(transaction => {
      if (!transaction.date) {
        return true; // pending transaction
      }
  
      return transaction.date > threshold;
    });
  });
  
  const selectHasTransactions = reselect.createSelector(selectTransactionItems, transactions => transactions && transactions.length > 0);
  
  const selectIsFetchingTransactions = reselect.createSelector(selectState, state => state.fetchingTransactions);
  
  /**
   * CSV of 'selectTransactionItems'.
   */
  const selectTransactionsFile = reselect.createSelector(selectTransactionItems, transactions => {
    if (!transactions || transactions.length === 0) {
      // No data.
      return undefined;
    }
  
    const parsed = parseData(transactions, 'csv');
    if (!parsed) {
      // Invalid data, or failed to parse.
      return null;
    }
  
    return parsed;
  });
  
  const selectIsSendingSupport = reselect.createSelector(selectState, state => state.sendingSupport);
  
  const selectReceiveAddress = reselect.createSelector(selectState, state => state.receiveAddress);
  
  const selectGettingNewAddress = reselect.createSelector(selectState, state => state.gettingNewAddress);
  
  const selectDraftTransaction = reselect.createSelector(selectState, state => state.draftTransaction || {});
  
  const selectDraftTransactionAmount = reselect.createSelector(selectDraftTransaction, draft => draft.amount);
  
  const selectDraftTransactionAddress = reselect.createSelector(selectDraftTransaction, draft => draft.address);
  
  const selectDraftTransactionError = reselect.createSelector(selectDraftTransaction, draft => draft.error);
  
  const selectBlocks = reselect.createSelector(selectState, state => state.blocks);
  
  const selectCurrentHeight = reselect.createSelector(selectState, state => state.latestBlock);
  
  const selectTransactionListFilter = reselect.createSelector(selectState, state => state.transactionListFilter || '');
  
  const selectFilteredTransactions = reselect.createSelector(selectTransactionItems, selectTransactionListFilter, (transactions, filter) => {
    return transactions.filter(transaction => {
      return filter === ALL || filter === transaction.type;
    });
  });
  
  const selectTxoPageParams = reselect.createSelector(selectState, state => state.txoFetchParams);
  
  const selectTxoPage = reselect.createSelector(selectState, state => state.txoPage && state.txoPage.items || []);
  
  const selectTxoPageNumber = reselect.createSelector(selectState, state => state.txoPage && state.txoPage.page || 1);
  
  const selectTxoItemCount = reselect.createSelector(selectState, state => state.txoPage && state.txoPage.total_items || 1);
  
  const selectFetchingTxosError = reselect.createSelector(selectState, state => state.fetchingTxosError);
  
  const selectIsFetchingTxos = reselect.createSelector(selectState, state => state.fetchingTxos);
  
  const makeSelectFilteredTransactionsForPage = (page = 1) => reselect.createSelector(selectFilteredTransactions, filteredTransactions => {
    const start = (Number(page) - 1) * Number(PAGE_SIZE$1);
    const end = Number(page) * Number(PAGE_SIZE$1);
    return filteredTransactions && filteredTransactions.length ? filteredTransactions.slice(start, end) : [];
  });
  
  const makeSelectLatestTransactions = reselect.createSelector(selectTransactionItems, transactions => {
    return transactions && transactions.length ? transactions.slice(0, LATEST_PAGE_SIZE) : [];
  });
  
  const selectFilteredTransactionCount = reselect.createSelector(selectFilteredTransactions, filteredTransactions => filteredTransactions.length);
  
  const selectIsWalletReconnecting = reselect.createSelector(selectState, state => state.walletReconnecting);
  
  const selectIsFetchingUtxoCounts = reselect.createSelector(selectState, state => state.fetchingUtxoCounts);
  
  const selectIsConsolidatingUtxos = reselect.createSelector(selectState, state => state.consolidatingUtxos);
  
  const selectIsMassClaimingTips = reselect.createSelector(selectState, state => state.massClaimingTips);
  
  const selectPendingConsolidateTxid = reselect.createSelector(selectState, state => state.pendingConsolidateTxid);
  
  const selectPendingMassClaimTxid = reselect.createSelector(selectState, state => state.pendingMassClaimTxid);
  
  const selectUtxoCounts = reselect.createSelector(selectState, state => state.utxoCounts);
  
  var _extends$2 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  function _objectWithoutProperties$1(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
  
  const matureTagMap = MATURE_TAGS.reduce((acc, tag) => _extends$2({}, acc, { [tag]: true }), {});
  
  const isClaimNsfw = claim => {
    if (!claim) {
      throw new Error('No claim passed to isClaimNsfw()');
    }
  
    if (!claim.value) {
      return false;
    }
  
    const tags = claim.value.tags || [];
    for (let i = 0; i < tags.length; i += 1) {
      const tag = tags[i].toLowerCase();
      if (matureTagMap[tag]) {
        return true;
      }
    }
  
    return false;
  };
  
  function createNormalizedClaimSearchKey(options) {
    // Ignore page because we don't care what the last page searched was, we want everything
    // Ignore release_time because that will change depending on when you call claim_search ex: release_time: ">12344567"
    const rest = _objectWithoutProperties$1(options, ['page', 'release_time']);
    const query = JSON.stringify(rest);
    return query;
  }
  
  function filterClaims(claims, query) {
    if (query) {
      const queryMatchRegExp = new RegExp(query, 'i');
      return claims.filter(claim => {
        const { value } = claim;
  
        return value.title && value.title.match(queryMatchRegExp) || claim.signing_channel && claim.signing_channel.name.match(queryMatchRegExp) || claim.name && claim.name.match(queryMatchRegExp);
      });
    }
  
    return claims;
  }
  
  var _extends$3 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  const selectState$1 = state => state.claims || {};
  
  const selectClaimsById = reselect.createSelector(selectState$1, state => state.byId || {});
  
  const selectClaimIdsByUri = reselect.createSelector(selectState$1, state => state.claimsByUri || {});
  
  const selectCurrentChannelPage = reselect.createSelector(selectState$1, state => state.currentChannelPage || 1);
  
  const selectCreatingChannel = reselect.createSelector(selectState$1, state => state.creatingChannel);
  
  const selectCreateChannelError = reselect.createSelector(selectState$1, state => state.createChannelError);
  
  const selectRepostLoading = reselect.createSelector(selectState$1, state => state.repostLoading);
  
  const selectRepostError = reselect.createSelector(selectState$1, state => state.repostError);
  
  const selectClaimsByUri = reselect.createSelector(selectClaimIdsByUri, selectClaimsById, (byUri, byId) => {
    const claims = {};
  
    Object.keys(byUri).forEach(uri => {
      const claimId = byUri[uri];
  
      // NOTE returning a null claim allows us to differentiate between an
      // undefined (never fetched claim) and one which just doesn't exist. Not
      // the cleanest solution but couldn't think of anything better right now
      if (claimId === null) {
        claims[uri] = null;
      } else {
        claims[uri] = byId[claimId];
      }
    });
  
    return claims;
  });
  
  const selectAllClaimsByChannel = reselect.createSelector(selectState$1, state => state.paginatedClaimsByChannel || {});
  
  const selectPendingIds = reselect.createSelector(selectState$1, state => state.pendingIds || []);
  
  const selectPendingClaims = reselect.createSelector(selectPendingIds, selectClaimsById, (pendingIds, byId) => pendingIds.map(id => byId[id]));
  
  const makeSelectClaimIsPending = uri => reselect.createSelector(selectClaimIdsByUri, selectPendingIds, (idsByUri, pendingIds) => {
    const claimId = idsByUri[normalizeURI(uri)];
  
    if (claimId) {
      return pendingIds.some(i => i === claimId);
    }
    return false;
  });
  
  const makeSelectClaimIdForUri = uri => reselect.createSelector(selectClaimIdsByUri, claimIds => claimIds[uri]);
  
  const selectReflectingById = reselect.createSelector(selectState$1, state => state.reflectingById);
  
  const makeSelectClaimForClaimId = claimId => reselect.createSelector(selectClaimsById, byId => byId[claimId]);
  
  const makeSelectClaimForUri = (uri, returnRepost = true) => reselect.createSelector(selectClaimIdsByUri, selectClaimsById, (byUri, byId) => {
    let validUri;
    let channelClaimId;
    let streamClaimId;
    let isChannel;
    try {
      ({ isChannel, channelClaimId, streamClaimId } = parseURI(uri));
      validUri = true;
    } catch (e) {}
  
    if (validUri && byUri) {
      const claimId = uri && byUri[normalizeURI(uri)];
      const claim = byId[claimId];
  
      // Make sure to return the claim as is so apps can check if it's been resolved before (null) or still needs to be resolved (undefined)
      if (claimId === null) {
        return null;
      } else if (claimId === undefined) {
        return undefined;
      }
  
      const repostedClaim = claim && claim.reposted_claim;
      if (repostedClaim && returnRepost) {
        const channelUrl = claim.signing_channel && (claim.signing_channel.canonical_url || claim.signing_channel.permanent_url);
  
        return _extends$3({}, repostedClaim, {
          repost_url: uri,
          repost_channel_url: channelUrl,
          repost_bid_amount: claim && claim.meta && claim.meta.effective_amount
        });
      } else {
        return claim;
      }
    }
  });
  
  const selectMyClaimsRaw = reselect.createSelector(selectState$1, selectClaimsById, (state, byId) => {
    const ids = state.myClaims;
    if (!ids) {
      return ids;
    }
  
    const claims = [];
    ids.forEach(id => {
      if (byId[id]) {
        // I'm not sure why this check is necessary, but it ought to be a quick fix for https://github.com/lbryio/lbry-desktop/issues/544
        claims.push(byId[id]);
      }
    });
    return claims;
  });
  
  const selectAbandoningIds = reselect.createSelector(selectState$1, state => Object.keys(state.abandoningById || {}));
  
  const makeSelectAbandoningClaimById = claimId => reselect.createSelector(selectAbandoningIds, ids => ids.includes(claimId));
  
  const makeSelectIsAbandoningClaimForUri = uri => reselect.createSelector(selectClaimIdsByUri, selectAbandoningIds, (claimIdsByUri, abandoningById) => {
    const claimId = claimIdsByUri[normalizeURI(uri)];
    return abandoningById.indexOf(claimId) >= 0;
  });
  
  const selectMyActiveClaims = reselect.createSelector(selectMyClaimsRaw, selectAbandoningIds, (claims, abandoningIds) => new Set(claims && claims.map(claim => claim.claim_id).filter(claimId => Object.keys(abandoningIds).indexOf(claimId) === -1)));
  
  const makeSelectClaimIsMine = rawUri => {
    let uri;
    try {
      uri = normalizeURI(rawUri);
    } catch (e) {}
  
    return reselect.createSelector(selectClaimsByUri, selectMyActiveClaims, (claims, myClaims) => {
      try {
        parseURI(uri);
      } catch (e) {
        return false;
      }
  
      return claims && claims[uri] && (claims[uri].is_my_output || claims[uri].claim_id && myClaims.has(claims[uri].claim_id));
    });
  };
  
  const selectMyPurchases = reselect.createSelector(selectState$1, state => state.myPurchases);
  
  const selectPurchaseUriSuccess = reselect.createSelector(selectState$1, state => state.purchaseUriSuccess);
  
  const selectMyPurchasesCount = reselect.createSelector(selectState$1, state => state.myPurchasesPageTotalResults);
  
  const selectIsFetchingMyPurchases = reselect.createSelector(selectState$1, state => state.fetchingMyPurchases);
  
  const selectFetchingMyPurchasesError = reselect.createSelector(selectState$1, state => state.fetchingMyPurchasesError);
  
  const makeSelectMyPurchasesForPage = (query, page = 1) => reselect.createSelector(selectMyPurchases, selectClaimsByUri, (myPurchases, claimsByUri) => {
    if (!myPurchases) {
      return undefined;
    }
  
    if (!query) {
      // ensure no duplicates from double purchase bugs
      return [...new Set(myPurchases)];
    }
  
    const fileInfos = myPurchases.map(uri => claimsByUri[uri]);
    const matchingFileInfos = filterClaims(fileInfos, query);
    const start = (Number(page) - 1) * Number(PAGE_SIZE);
    const end = Number(page) * Number(PAGE_SIZE);
    return matchingFileInfos && matchingFileInfos.length ? matchingFileInfos.slice(start, end).map(fileInfo => fileInfo.canonical_url || fileInfo.permanent_url) : [];
  });
  
  const makeSelectClaimWasPurchased = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    return claim && claim.purchase_receipt !== undefined;
  });
  
  const selectAllFetchingChannelClaims = reselect.createSelector(selectState$1, state => state.fetchingChannelClaims || {});
  
  const makeSelectFetchingChannelClaims = uri => reselect.createSelector(selectAllFetchingChannelClaims, fetching => fetching && fetching[uri]);
  
  const makeSelectClaimsInChannelForPage = (uri, page) => reselect.createSelector(selectClaimsById, selectAllClaimsByChannel, (byId, allClaims) => {
    const byChannel = allClaims[uri] || {};
    const claimIds = byChannel[page || 1];
  
    if (!claimIds) return claimIds;
  
    return claimIds.map(claimId => byId[claimId]);
  });
  
  const makeSelectTotalClaimsInChannelSearch = uri => reselect.createSelector(selectClaimsById, selectAllClaimsByChannel, (byId, allClaims) => {
    const byChannel = allClaims[uri] || {};
    return byChannel['itemCount'];
  });
  
  const makeSelectTotalPagesInChannelSearch = uri => reselect.createSelector(selectClaimsById, selectAllClaimsByChannel, (byId, allClaims) => {
    const byChannel = allClaims[uri] || {};
    return byChannel['pageCount'];
  });
  
  const makeSelectClaimsInChannelForCurrentPageState = uri => reselect.createSelector(selectClaimsById, selectAllClaimsByChannel, selectCurrentChannelPage, (byId, allClaims, page) => {
    const byChannel = allClaims[uri] || {};
    const claimIds = byChannel[page || 1];
  
    if (!claimIds) return claimIds;
  
    return claimIds.map(claimId => byId[claimId]);
  });
  
  const makeSelectMetadataForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    const metadata = claim && claim.value;
    return metadata || (claim === undefined ? undefined : null);
  });
  
  const makeSelectMetadataItemForUri = (uri, key) => reselect.createSelector(makeSelectMetadataForUri(uri), metadata => {
    return metadata ? metadata[key] : undefined;
  });
  
  const makeSelectTitleForUri = uri => reselect.createSelector(makeSelectMetadataForUri(uri), metadata => metadata && metadata.title);
  
  const makeSelectDateForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    const timestamp = claim && claim.value && (claim.value.release_time ? claim.value.release_time * 1000 : claim.meta && claim.meta.creation_timestamp ? claim.meta.creation_timestamp * 1000 : null);
    if (!timestamp) {
      return undefined;
    }
    const dateObj = new Date(timestamp);
    return dateObj;
  });
  
  const makeSelectAmountForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    return claim && claim.amount;
  });
  
  const makeSelectEffectiveAmountForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri, false), claim => {
    return claim && claim.meta && typeof claim.meta.effective_amount === 'string' && Number(claim.meta.effective_amount);
  });
  
  const makeSelectContentTypeForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    const source = claim && claim.value && claim.value.source;
    return source ? source.media_type : undefined;
  });
  
  const makeSelectThumbnailForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    const thumbnail = claim && claim.value && claim.value.thumbnail;
    return thumbnail && thumbnail.url ? thumbnail.url.trim().replace(/^http:\/\//i, 'https://') : undefined;
  });
  
  const makeSelectCoverForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    const cover = claim && claim.value && claim.value.cover;
    return cover && cover.url ? cover.url.trim().replace(/^http:\/\//i, 'https://') : undefined;
  });
  
  const selectIsFetchingClaimListMine = reselect.createSelector(selectState$1, state => state.isFetchingClaimListMine);
  
  const selectMyClaimsPage = reselect.createSelector(selectState$1, state => state.myClaimsPageResults || []);
  
  const selectMyClaimsPageNumber = reselect.createSelector(selectState$1, state => state.claimListMinePage && state.claimListMinePage.items || [], state => state.txoPage && state.txoPage.page || 1);
  
  const selectMyClaimsPageItemCount = reselect.createSelector(selectState$1, state => state.myClaimsPageTotalResults || 1);
  
  const selectFetchingMyClaimsPageError = reselect.createSelector(selectState$1, state => state.fetchingClaimListMinePageError);
  
  const selectMyClaims = reselect.createSelector(selectMyActiveClaims, selectClaimsById, selectAbandoningIds, (myClaimIds, byId, abandoningIds) => {
    const claims = [];
  
    myClaimIds.forEach(id => {
      const claim = byId[id];
  
      if (claim && abandoningIds.indexOf(id) === -1) claims.push(claim);
    });
  
    return [...claims];
  });
  
  const selectMyClaimsWithoutChannels = reselect.createSelector(selectMyClaims, myClaims => myClaims.filter(claim => !claim.name.match(/^@/)).sort((a, b) => a.timestamp - b.timestamp));
  
  const selectMyClaimUrisWithoutChannels = reselect.createSelector(selectMyClaimsWithoutChannels, myClaims => {
    return myClaims.sort((a, b) => {
      if (a.height < 1) {
        return -1;
      } else if (b.height < 1) {
        return 1;
      } else {
        return b.timestamp - a.timestamp;
      }
    }).map(claim => {
      return claim.canonical_url || claim.permanent_url;
    });
  });
  
  const selectAllMyClaimsByOutpoint = reselect.createSelector(selectMyClaimsRaw, claims => new Set(claims && claims.length ? claims.map(claim => `${claim.txid}:${claim.nout}`) : null));
  
  const selectMyClaimsOutpoints = reselect.createSelector(selectMyClaims, myClaims => {
    const outpoints = [];
  
    myClaims.forEach(claim => outpoints.push(`${claim.txid}:${claim.nout}`));
  
    return outpoints;
  });
  
  const selectFetchingMyChannels = reselect.createSelector(selectState$1, state => state.fetchingMyChannels);
  
  const selectFetchingMyCollections = reselect.createSelector(selectState$1, state => state.fetchingMyCollections);
  
  const selectMyChannelClaims = reselect.createSelector(selectState$1, selectClaimsById, (state, byId) => {
    const ids = state.myChannelClaims;
    if (!ids) {
      return ids;
    }
  
    const claims = [];
    ids.forEach(id => {
      if (byId[id]) {
        // I'm not sure why this check is necessary, but it ought to be a quick fix for https://github.com/lbryio/lbry-desktop/issues/544
        claims.push(byId[id]);
      }
    });
  
    return claims;
  });
  
  const selectMyChannelUrls = reselect.createSelector(selectMyChannelClaims, claims => claims ? claims.map(claim => claim.canonical_url || claim.permanent_url) : undefined);
  
  const selectMyCollectionIds = reselect.createSelector(selectState$1, state => state.myCollectionClaims);
  
  const selectResolvingUris = reselect.createSelector(selectState$1, state => state.resolvingUris || []);
  
  const selectChannelImportPending = reselect.createSelector(selectState$1, state => state.pendingChannelImport);
  
  const makeSelectIsUriResolving = uri => reselect.createSelector(selectResolvingUris, resolvingUris => resolvingUris && resolvingUris.indexOf(uri) !== -1);
  
  const selectPlayingUri = reselect.createSelector(selectState$1, state => state.playingUri);
  
  const selectChannelClaimCounts = reselect.createSelector(selectState$1, state => state.channelClaimCounts || {});
  
  const makeSelectPendingClaimForUri = uri => reselect.createSelector(selectPendingIds, selectClaimsById, (pending, claims) => {
    let uriIsChannel;
    let uriStreamName;
    let uriChannelName;
    try {
      ({
        isChannel: uriIsChannel,
        streamName: uriStreamName,
        channelName: uriChannelName
      } = parseURI(uri));
    } catch (e) {
      return null;
    }
    const pendingClaims = pending.map(id => claims[id]);
    const matchingClaim = pendingClaims.find(claim => {
      const { streamName, channelName, isChannel } = parseURI(claim.permanent_url);
      if (isChannel) {
        return channelName === uriChannelName;
      } else {
        return streamName === uriStreamName;
      }
    });
    return matchingClaim || null;
  });
  
  const makeSelectTotalItemsForChannel = uri => reselect.createSelector(selectChannelClaimCounts, byUri => byUri && byUri[uri]);
  
  const makeSelectTotalPagesForChannel = (uri, pageSize = 10) => reselect.createSelector(selectChannelClaimCounts, byUri => byUri && byUri[uri] && Math.ceil(byUri[uri] / pageSize));
  
  const makeSelectNsfwCountFromUris = uris => reselect.createSelector(selectClaimsByUri, claims => uris.reduce((acc, uri) => {
    const claim = claims[uri];
    if (claim && isClaimNsfw(claim)) {
      return acc + 1;
    }
    return acc;
  }, 0));
  
  const makeSelectNsfwCountForChannel = uri => reselect.createSelector(selectClaimsById, selectAllClaimsByChannel, selectCurrentChannelPage, (byId, allClaims, page) => {
    const byChannel = allClaims[uri] || {};
    const claimIds = byChannel[page || 1];
  
    if (!claimIds) return 0;
  
    return claimIds.reduce((acc, claimId) => {
      const claim = byId[claimId];
      if (isClaimNsfw(claim)) {
        return acc + 1;
      }
      return acc;
    }, 0);
  });
  
  const makeSelectOmittedCountForChannel = uri => reselect.createSelector(makeSelectTotalItemsForChannel(uri), makeSelectTotalClaimsInChannelSearch(uri), (claimsInChannel, claimsInSearch) => {
    if (claimsInChannel && typeof claimsInSearch === 'number' && claimsInSearch >= 0) {
      return claimsInChannel - claimsInSearch;
    } else return 0;
  });
  
  const makeSelectClaimIsNsfw = uri => reselect.createSelector(makeSelectClaimForUri(uri),
  // Eventually these will come from some list of tags that are considered adult
  // Or possibly come from users settings of what tags they want to hide
  // For now, there is just a hard coded list of tags inside `isClaimNsfw`
  // selectNaughtyTags(),
  claim => {
    if (!claim) {
      return false;
    }
  
    return isClaimNsfw(claim);
  });
  
  // Returns the associated channel uri for a given claim uri
  // accepts a regular claim uri lbry://something
  // returns the channel uri that created this claim lbry://@channel
  const makeSelectChannelForClaimUri = (uri, includePrefix = false) => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    if (!claim || !claim.signing_channel || !claim.is_channel_signature_valid) {
      return null;
    }
  
    const { canonical_url: canonicalUrl, permanent_url: permanentUrl } = claim.signing_channel;
  
    if (canonicalUrl) {
      return includePrefix ? canonicalUrl : canonicalUrl.slice('lbry://'.length);
    } else {
      return includePrefix ? permanentUrl : permanentUrl.slice('lbry://'.length);
    }
  });
  
  const makeSelectChannelPermUrlForClaimUri = (uri, includePrefix = false) => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    if (claim && claim.value_type === 'channel') {
      return claim.permanent_url;
    }
    if (!claim || !claim.signing_channel || !claim.is_channel_signature_valid) {
      return null;
    }
    return claim.signing_channel.permanent_url;
  });
  
  const makeSelectMyChannelPermUrlForName = name => reselect.createSelector(selectMyChannelClaims, claims => {
    const matchingClaim = claims && claims.find(claim => claim.name === name);
    return matchingClaim ? matchingClaim.permanent_url : null;
  });
  
  const makeSelectTagsForUri = uri => reselect.createSelector(makeSelectMetadataForUri(uri), metadata => {
    return metadata && metadata.tags || [];
  });
  
  const selectFetchingClaimSearchByQuery = reselect.createSelector(selectState$1, state => state.fetchingClaimSearchByQuery || {});
  
  const selectFetchingClaimSearch = reselect.createSelector(selectFetchingClaimSearchByQuery, fetchingClaimSearchByQuery => Boolean(Object.keys(fetchingClaimSearchByQuery).length));
  
  const selectClaimSearchByQuery = reselect.createSelector(selectState$1, state => state.claimSearchByQuery || {});
  
  const selectClaimSearchByQueryLastPageReached = reselect.createSelector(selectState$1, state => state.claimSearchByQueryLastPageReached || {});
  
  const makeSelectShortUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.short_url);
  
  const makeSelectCanonicalUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.canonical_url);
  
  const makeSelectPermanentUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.permanent_url);
  
  const makeSelectSupportsForUri = uri => reselect.createSelector(selectSupportsByOutpoint, makeSelectClaimForUri(uri), (byOutpoint, claim) => {
    if (!claim || !claim.is_my_output) {
      return null;
    }
  
    const { claim_id: claimId } = claim;
    let total = 0;
  
    Object.values(byOutpoint).forEach(support => {
      // $FlowFixMe
      const { claim_id, amount } = support;
      total = claim_id === claimId && amount ? total + parseFloat(amount) : total;
    });
  
    return total;
  });
  
  const selectUpdatingChannel = reselect.createSelector(selectState$1, state => state.updatingChannel);
  
  const selectUpdateChannelError = reselect.createSelector(selectState$1, state => state.updateChannelError);
  
  const makeSelectReflectingClaimForUri = uri => reselect.createSelector(selectClaimIdsByUri, selectReflectingById, (claimIdsByUri, reflectingById) => {
    const claimId = claimIdsByUri[normalizeURI(uri)];
    return reflectingById[claimId];
  });
  
  const makeSelectMyStreamUrlsForPage = (page = 1) => reselect.createSelector(selectMyClaimUrisWithoutChannels, urls => {
    const start = (Number(page) - 1) * Number(PAGE_SIZE);
    const end = Number(page) * Number(PAGE_SIZE);
  
    return urls && urls.length ? urls.slice(start, end) : [];
  });
  
  const selectMyStreamUrlsCount = reselect.createSelector(selectMyClaimUrisWithoutChannels, channels => channels.length);
  
  const makeSelectTagInClaimOrChannelForUri = (uri, tag) => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    const claimTags = claim && claim.value && claim.value.tags || [];
    const channelTags = claim && claim.signing_channel && claim.signing_channel.value && claim.signing_channel.value.tags || [];
    return claimTags.includes(tag) || channelTags.includes(tag);
  });
  
  const makeSelectClaimHasSource = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    if (!claim) {
      return false;
    }
  
    return Boolean(claim.value.source);
  });
  
  const makeSelectClaimIsStreamPlaceholder = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    if (!claim) {
      return false;
    }
  
    return Boolean(claim.value_type === 'stream' && !claim.value.source);
  });
  
  const makeSelectTotalStakedAmountForChannelUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
    if (!claim || !claim.amount || !claim.meta || !claim.meta.support_amount) {
      return 0;
    }
  
    return parseFloat(claim.amount) + parseFloat(claim.meta.support_amount) || 0;
  });
  
  const makeSelectStakedLevelForChannelUri = uri => reselect.createSelector(makeSelectTotalStakedAmountForChannelUri(uri), amount => {
    let level = 1;
    switch (true) {
      case amount >= LEVEL_2_STAKED_AMOUNT && amount < LEVEL_3_STAKED_AMOUNT:
        level = 2;
        break;
      case amount >= LEVEL_3_STAKED_AMOUNT && amount < LEVEL_4_STAKED_AMOUNT:
        level = 3;
        break;
      case amount >= LEVEL_4_STAKED_AMOUNT && amount < LEVEL_5_STAKED_AMOUNT:
        level = 4;
        break;
      case amount >= LEVEL_5_STAKED_AMOUNT:
        level = 5;
        break;
    }
    return level;
  });
  
  const selectUpdatingCollection = reselect.createSelector(selectState$1, state => state.updatingCollection);
  
  const selectUpdateCollectionError = reselect.createSelector(selectState$1, state => state.updateCollectionError);
  
  const selectCreatingCollection = reselect.createSelector(selectState$1, state => state.creatingCollection);
  
  const selectCreateCollectionError = reselect.createSelector(selectState$1, state => state.createCollectionError);
  
  function numberWithCommas(x) {
    var parts = x.toString().split('.');
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    return parts.join('.');
  }
  
  function formatCredits(amount, precision, shortFormat = false) {
    let actualAmount = parseFloat(amount);
    let actualPrecision = parseFloat(precision);
    let suffix = '';
  
    if (Number.isNaN(actualAmount) || actualAmount === 0) return '0';
  
    if (actualAmount >= 1000000) {
      if (precision <= 7) {
        if (shortFormat) {
          actualAmount = actualAmount / 1000000;
          suffix = 'M';
        } else {
          actualPrecision -= 7;
        }
      }
    } else if (actualAmount >= 1000) {
      if (precision <= 4) {
        if (shortFormat) {
          actualAmount = actualAmount / 1000;
          suffix = 'K';
        } else {
          actualPrecision -= 4;
        }
      }
    }
  
    return numberWithCommas(actualAmount.toFixed(actualPrecision >= 0 ? actualPrecision : 1).replace(/\.*0+$/, '')) + suffix;
  }
  
  function formatFullPrice(amount, precision = 1) {
    let formated = '';
  
    const quantity = amount.toString().split('.');
    const fraction = quantity[1];
  
    if (fraction) {
      const decimals = fraction.split('');
      const first = decimals.filter(number => number !== '0')[0];
      const index = decimals.indexOf(first);
  
      // Set format fraction
      formated = `.${fraction.substring(0, index + precision)}`;
    }
  
    return parseFloat(quantity[0] + formated);
  }
  
  function creditsToString(amount) {
    const creditString = parseFloat(amount).toFixed(8);
    return creditString;
  }
  
  var _extends$4 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
  
  const FIFTEEN_SECONDS = 15000;
  let walletBalancePromise = null;
  
  function doUpdateBalance() {
    return (dispatch, getState) => {
      const {
        wallet: { totalBalance: totalInStore }
      } = getState();
  
      if (walletBalancePromise === null) {
        walletBalancePromise = lbryProxy.wallet_balance().then(response => {
          walletBalancePromise = null;
  
          const { available, reserved, reserved_subtotals, total } = response;
          const { claims, supports, tips } = reserved_subtotals;
          const totalFloat = parseFloat(total);
  
          if (totalInStore !== totalFloat) {
            dispatch({
              type: UPDATE_BALANCE,
              data: {
                totalBalance: totalFloat,
                balance: parseFloat(available),
                reservedBalance: parseFloat(reserved),
                claimsBalance: parseFloat(claims),
                supportsBalance: parseFloat(supports),
                tipsBalance: parseFloat(tips)
              }
            });
          }
        }).catch(() => {
          walletBalancePromise = null;
        });
      }
  
      return walletBalancePromise;
    };
  }
  
  function doBalanceSubscribe() {
    return dispatch => {
      dispatch(doUpdateBalance());
      setInterval(() => dispatch(doUpdateBalance()), 10000);
    };
  }
  
  function doFetchTransactions(page = 1, pageSize = 999999) {
    return dispatch => {
      dispatch({
        type: FETCH_TRANSACTIONS_STARTED
      });
  
      lbryProxy.transaction_list({ page, page_size: pageSize }).then(result => {
        dispatch({
          type: FETCH_TRANSACTIONS_COMPLETED,
          data: {
            transactions: result.items
          }
        });
      });
    };
  }
  
  function doFetchTxoPage() {
    return (dispatch, getState) => {
      const fetchId = Math.random().toString(36).substr(2, 9);
  
      dispatch({
        type: FETCH_TXO_PAGE_STARTED,
        data: fetchId
      });
  
      const state = getState();
      const queryParams = selectTxoPageParams(state);
  
      lbryProxy.txo_list(queryParams).then(res => {
        const items = res.items || [];
        const claimsById = selectClaimsById(state);
  
        const channelIds = items.reduce((acc, cur) => {
          if (cur.type === 'support' && cur.signing_channel && !claimsById[cur.signing_channel.channel_id]) {
            acc.push(cur.signing_channel.channel_id);
          }
          return acc;
        }, []);
  
        if (channelIds.length) {
          const searchParams = {
            page_size: 9999,
            page: 1,
            no_totals: true,
            claim_ids: channelIds
          };
          // make sure redux has these channels resolved
          dispatch(doClaimSearch(searchParams));
        }
  
        return res;
      }).then(res => {
        dispatch({
          type: FETCH_TXO_PAGE_COMPLETED,
          data: {
            result: res,
            fetchId: fetchId
          }
        });
      }).catch(e => {
        dispatch({
          type: FETCH_TXO_PAGE_COMPLETED,
          data: {
            error: e.message,
            fetchId: fetchId
          }
        });
      });
    };
  }
  
  function doUpdateTxoPageParams(params) {
    return dispatch => {
      dispatch({
        type: UPDATE_TXO_FETCH_PARAMS,
        data: params
      });
  
      dispatch(doFetchTxoPage());
    };
  }
  
  function doFetchUtxoCounts() {
    return (() => {
      var _ref = _asyncToGenerator(function* (dispatch) {
        dispatch({
          type: FETCH_UTXO_COUNT_STARTED
        });
  
        let resultSets = yield Promise.all([lbryProxy.txo_list({ type: 'other', is_not_spent: true, page: 1, page_size: 1 }), lbryProxy.txo_list({ type: 'support', is_not_spent: true, page: 1, page_size: 1 })]);
        const counts = {};
        const paymentCount = resultSets[0]['total_items'];
        const supportCount = resultSets[1]['total_items'];
        counts['other'] = typeof paymentCount === 'number' ? paymentCount : 0;
        counts['support'] = typeof supportCount === 'number' ? supportCount : 0;
  
        dispatch({
          type: FETCH_UTXO_COUNT_COMPLETED,
          data: counts,
          debug: { resultSets }
        });
      });
  
      return function (_x) {
        return _ref.apply(this, arguments);
      };
    })();
  }
  
  function doUtxoConsolidate() {
    return (() => {
      var _ref2 = _asyncToGenerator(function* (dispatch) {
        dispatch({
          type: DO_UTXO_CONSOLIDATE_STARTED
        });
  
        const results = yield lbryProxy.txo_spend({ type: 'other' });
        const result = results[0];
  
        dispatch({
          type: PENDING_CONSOLIDATED_TXOS_UPDATED,
          data: { txids: [result.txid] }
        });
  
        dispatch({
          type: DO_UTXO_CONSOLIDATE_COMPLETED,
          data: { txid: result.txid }
        });
        dispatch(doCheckPendingTxs());
      });
  
      return function (_x2) {
        return _ref2.apply(this, arguments);
      };
    })();
  }
  
  function doTipClaimMass() {
    return (() => {
      var _ref3 = _asyncToGenerator(function* (dispatch) {
        dispatch({
          type: TIP_CLAIM_MASS_STARTED
        });
  
        const results = yield lbryProxy.txo_spend({ type: 'support', is_not_my_input: true });
        const result = results[0];
  
        dispatch({
          type: PENDING_CONSOLIDATED_TXOS_UPDATED,
          data: { txids: [result.txid] }
        });
  
        dispatch({
          type: TIP_CLAIM_MASS_COMPLETED,
          data: { txid: result.txid }
        });
        dispatch(doCheckPendingTxs());
      });
  
      return function (_x3) {
        return _ref3.apply(this, arguments);
      };
    })();
  }
  
  function doGetNewAddress() {
    return dispatch => {
      dispatch({
        type: GET_NEW_ADDRESS_STARTED
      });
  
      lbryProxy.address_unused().then(address => {
        dispatch({
          type: GET_NEW_ADDRESS_COMPLETED,
          data: { address }
        });
      });
    };
  }
  
  function doCheckAddressIsMine(address) {
    return dispatch => {
      dispatch({
        type: CHECK_ADDRESS_IS_MINE_STARTED
      });
  
      lbryProxy.address_is_mine({ address }).then(isMine => {
        if (!isMine) dispatch(doGetNewAddress());
  
        dispatch({
          type: CHECK_ADDRESS_IS_MINE_COMPLETED
        });
      });
    };
  }
  
  function doSendDraftTransaction(address, amount) {
    return (dispatch, getState) => {
      const state = getState();
      const balance = selectBalance(state);
  
      if (balance - amount <= 0) {
        dispatch(doToast({
          title: __('Insufficient credits'),
          message: __('Insufficient credits')
        }));
        return;
      }
  
      dispatch({
        type: SEND_TRANSACTION_STARTED
      });
  
      const successCallback = response => {
        if (response.txid) {
          dispatch({
            type: SEND_TRANSACTION_COMPLETED
          });
          dispatch(doToast({
            message: __('You sent %amount% LBRY Credits', { amount: amount }),
            linkText: __('History'),
            linkTarget: '/wallet'
          }));
        } else {
          dispatch({
            type: SEND_TRANSACTION_FAILED,
            data: { error: response }
          });
          dispatch(doToast({
            message: __('Transaction failed'),
            isError: true
          }));
        }
      };
  
      const errorCallback = error => {
        dispatch({
          type: SEND_TRANSACTION_FAILED,
          data: { error: error.message }
        });
        dispatch(doToast({
          message: __('Transaction failed'),
          isError: true
        }));
      };
  
      lbryProxy.wallet_send({
        addresses: [address],
        amount: creditsToString(amount)
      }).then(successCallback, errorCallback);
    };
  }
  
  function doSetDraftTransactionAmount(amount) {
    return {
      type: SET_DRAFT_TRANSACTION_AMOUNT,
      data: { amount }
    };
  }
  
  function doSetDraftTransactionAddress(address) {
    return {
      type: SET_DRAFT_TRANSACTION_ADDRESS,
      data: { address }
    };
  }
  
  function doSendTip(params, isSupport, successCallback, errorCallback, shouldNotify = true) {
    return (dispatch, getState) => {
      const state = getState();
      const balance = selectBalance(state);
      const myClaims = selectMyClaimsRaw(state);
  
      const shouldSupport = isSupport || (myClaims ? myClaims.find(claim => claim.claim_id === params.claim_id) : false);
  
      if (balance - params.amount <= 0) {
        dispatch(doToast({
          message: __('Insufficient credits'),
          isError: true
        }));
        return;
      }
  
      const success = response => {
        if (shouldNotify) {
          dispatch(doToast({
            message: shouldSupport ? __('You deposited %amount% LBRY Credits as a support!', { amount: params.amount }) : __('You sent %amount% LBRY Credits as a tip, Mahalo!', { amount: params.amount }),
            linkText: __('History'),
            linkTarget: '/wallet'
          }));
        }
  
        dispatch({
          type: SUPPORT_TRANSACTION_COMPLETED
        });
  
        if (successCallback) {
          successCallback(response);
        }
      };
  
      const error = err => {
        dispatch(doToast({
          message: __(`There was an error sending support funds.`),
          isError: true
        }));
  
        dispatch({
          type: SUPPORT_TRANSACTION_FAILED,
          data: {
            error: err
          }
        });
  
        if (errorCallback) {
          errorCallback();
        }
      };
  
      dispatch({
        type: SUPPORT_TRANSACTION_STARTED
      });
  
      lbryProxy.support_create(_extends$4({}, params, {
        tip: !shouldSupport,
        blocking: true,
        amount: creditsToString(params.amount)
      })).then(success, error);
    };
  }
  
  function doClearSupport() {
    return {
      type: CLEAR_SUPPORT_TRANSACTION
    };
  }
  
  function doWalletEncrypt(newPassword) {
    return dispatch => {
      dispatch({
        type: WALLET_ENCRYPT_START
      });
  
      lbryProxy.wallet_encrypt({ new_password: newPassword }).then(result => {
        if (result === true) {
          dispatch({
            type: WALLET_ENCRYPT_COMPLETED,
            result
          });
        } else {
          dispatch({
            type: WALLET_ENCRYPT_FAILED,
            result
          });
        }
      });
    };
  }
  
  function doWalletUnlock(password) {
    return dispatch => {
      dispatch({
        type: WALLET_UNLOCK_START
      });
  
      lbryProxy.wallet_unlock({ password }).then(result => {
        if (result === true) {
          dispatch({
            type: WALLET_UNLOCK_COMPLETED,
            result
          });
        } else {
          dispatch({
            type: WALLET_UNLOCK_FAILED,
            result
          });
        }
      });
    };
  }
  
  // Collect all tips for a claim
  function doSupportAbandonForClaim(claimId, claimType, keep, preview) {
    return dispatch => {
      if (preview) {
        dispatch({
          type: ABANDON_CLAIM_SUPPORT_PREVIEW
        });
      } else {
        dispatch({
          type: ABANDON_CLAIM_SUPPORT_STARTED
        });
      }
  
      const params = { claim_id: claimId };
      if (preview) params['preview'] = true;
      if (keep) params['keep'] = keep;
      return lbryProxy.support_abandon(params).then(res => {
        if (!preview) {
          dispatch({
            type: ABANDON_CLAIM_SUPPORT_COMPLETED,
            data: { claimId, txid: res.txid, effective: res.outputs[0].amount, type: claimType }
          });
          dispatch(doCheckPendingTxs());
        }
        return res;
      }).catch(e => {
        dispatch({
          type: ABANDON_CLAIM_SUPPORT_FAILED,
          data: e.message
        });
      });
    };
  }
  
  function doWalletReconnect() {
    return dispatch => {
      dispatch({
        type: WALLET_RESTART
      });
      let failed = false;
      // this basically returns null when it's done. :(
      // might be good to  dispatch ACTIONS.WALLET_RESTARTED
      const walletTimeout = setTimeout(() => {
        failed = true;
        dispatch({
          type: WALLET_RESTART_COMPLETED
        });
        dispatch(doToast({
          message: __('Your servers were not available. Check your url and port, or switch back to defaults.'),
          isError: true
        }));
      }, FIFTEEN_SECONDS);
      lbryProxy.wallet_reconnect().then(() => {
        clearTimeout(walletTimeout);
        if (!failed) dispatch({ type: WALLET_RESTART_COMPLETED });
      });
    };
  }
  
  function doWalletDecrypt() {
    return dispatch => {
      dispatch({
        type: WALLET_DECRYPT_START
      });
  
      lbryProxy.wallet_decrypt().then(result => {
        if (result === true) {
          dispatch({
            type: WALLET_DECRYPT_COMPLETED,
            result
          });
        } else {
          dispatch({
            type: WALLET_DECRYPT_FAILED,
            result
          });
        }
      });
    };
  }
  
  function doWalletStatus() {
    return dispatch => {
      dispatch({
        type: WALLET_STATUS_START
      });
  
      lbryProxy.wallet_status().then(status => {
        if (status) {
          dispatch({
            type: WALLET_STATUS_COMPLETED,
            result: status.is_encrypted
          });
        }
      });
    };
  }
  
  function doSetTransactionListFilter(filterOption) {
    return {
      type: SET_TRANSACTION_LIST_FILTER,
      data: filterOption
    };
  }
  
  function doUpdateBlockHeight() {
    return dispatch => lbryProxy.status().then(status => {
      if (status.wallet) {
        dispatch({
          type: UPDATE_CURRENT_HEIGHT,
          data: status.wallet.blocks
        });
      }
    });
  }
  
  // Calls transaction_show on txes until any pending txes are confirmed
  const doCheckPendingTxs = () => (dispatch, getState) => {
    const state = getState();
    const pendingTxsById = selectPendingSupportTransactions(state); // {}
    const pendingOtherTxes = selectPendingOtherTransactions(state);
  
    if (!Object.keys(pendingTxsById).length && !pendingOtherTxes.length) {
      return;
    }
    let txCheckInterval;
    const checkTxList = () => {
      const state = getState();
      const pendingSupportTxs = selectPendingSupportTransactions(state); // {}
      const pendingConsolidateTxes = selectPendingOtherTransactions(state);
      const pendingConsTxid = selectPendingConsolidateTxid(state);
      const pendingMassCLaimTxid = selectPendingMassClaimTxid(state);
  
      const promises = [];
      const newPendingTxes = {};
      const noLongerPendingConsolidate = [];
      const types = new Set([]);
      // { claimId: {txid: 123, amount 12.3}, }
      const entries = Object.entries(pendingSupportTxs);
      entries.forEach(([claim, data]) => {
        promises.push(lbryProxy.transaction_show({ txid: data.txid }));
        types.add(data.type);
      });
      if (pendingConsolidateTxes.length) {
        pendingConsolidateTxes.forEach(txid => promises.push(lbryProxy.transaction_show({ txid })));
      }
  
      Promise.all(promises).then(txShows => {
        let changed = false;
        txShows.forEach(result => {
          if (pendingConsolidateTxes.includes(result.txid)) {
            if (result.height > 0) {
              noLongerPendingConsolidate.push(result.txid);
            }
          } else {
            if (result.height <= 0) {
              const match = entries.find(entry => entry[1].txid === result.txid);
              newPendingTxes[match[0]] = match[1];
            } else {
              changed = true;
            }
          }
        });
  
        if (changed) {
          dispatch({
            type: PENDING_SUPPORTS_UPDATED,
            data: newPendingTxes
          });
          if (types.has('channel')) {
            dispatch(doFetchChannelListMine());
          }
          if (types.has('stream')) {
            dispatch(doFetchClaimListMine());
          }
        }
        if (noLongerPendingConsolidate.length) {
          if (noLongerPendingConsolidate.includes(pendingConsTxid)) {
            dispatch(doToast({
              message: __('Your wallet is finished consolidating')
            }));
          }
          if (noLongerPendingConsolidate.includes(pendingMassCLaimTxid)) {
            dispatch(doToast({
              message: __('Your tips have been collected')
            }));
          }
          dispatch({
            type: PENDING_CONSOLIDATED_TXOS_UPDATED,
            data: { txids: noLongerPendingConsolidate, remove: true }
          });
        }
  
        if (!Object.keys(pendingTxsById).length && !pendingOtherTxes.length) {
          clearInterval(txCheckInterval);
        }
      });
    };
  
    txCheckInterval = setInterval(() => {
      checkTxList();
    }, 30000);
  };
  
  // https://github.com/reactjs/redux/issues/911
  function batchActions(...actions) {
    return {
      type: 'BATCH_ACTIONS',
      actions
    };
  }
  
  //      
  
  const selectState$2 = state => state.collections;
  
  const selectSavedCollectionIds = reselect.createSelector(selectState$2, collectionState => collectionState.saved);
  
  const selectBuiltinCollections = reselect.createSelector(selectState$2, state => state.builtin);
  const selectResolvedCollections = reselect.createSelector(selectState$2, state => state.resolved);
  
  const selectMyUnpublishedCollections = reselect.createSelector(selectState$2, state => state.unpublished);
  
  const selectMyEditedCollections = reselect.createSelector(selectState$2, state => state.edited);
  
  const selectPendingCollections = reselect.createSelector(selectState$2, state => state.pending);
  
  const makeSelectEditedCollectionForId = id => reselect.createSelector(selectMyEditedCollections, eLists => eLists[id]);
  
  const makeSelectPendingCollectionForId = id => reselect.createSelector(selectPendingCollections, pending => pending[id]);
  
  const makeSelectPublishedCollectionForId = id => reselect.createSelector(selectResolvedCollections, rLists => rLists[id]);
  
  const makeSelectUnpublishedCollectionForId = id => reselect.createSelector(selectMyUnpublishedCollections, rLists => rLists[id]);
  
  const makeSelectCollectionIsMine = id => reselect.createSelector(selectMyCollectionIds, selectMyUnpublishedCollections, selectBuiltinCollections, (publicIds, privateIds, builtinIds) => {
    return Boolean(publicIds.includes(id) || privateIds[id] || builtinIds[id]);
  });
  
  const selectMyPublishedCollections = reselect.createSelector(selectResolvedCollections, selectPendingCollections, selectMyEditedCollections, selectMyCollectionIds, (resolved, pending, edited, myIds) => {
    // all resolved in myIds, plus those in pending and edited
    const myPublishedCollections = Object.fromEntries(Object.entries(pending).concat(Object.entries(resolved).filter(([key, val]) => myIds.includes(key) &&
    // $FlowFixMe
    !pending[key])));
    // now add in edited:
    Object.entries(edited).forEach(([id, item]) => {
      myPublishedCollections[id] = item;
    });
    return myPublishedCollections;
  });
  
  const selectMyPublishedMixedCollections = reselect.createSelector(selectMyPublishedCollections, published => {
    const myCollections = Object.fromEntries(
    // $FlowFixMe
    Object.entries(published).filter(([key, collection]) => {
      // $FlowFixMe
      return collection.type === 'collection';
    }));
    return myCollections;
  });
  
  const selectMyPublishedPlaylistCollections = reselect.createSelector(selectMyPublishedCollections, published => {
    const myCollections = Object.fromEntries(
    // $FlowFixMe
    Object.entries(published).filter(([key, collection]) => {
      // $FlowFixMe
      return collection.type === 'playlist';
    }));
    return myCollections;
  });
  
  const makeSelectMyPublishedCollectionForId = id => reselect.createSelector(selectMyPublishedCollections, myPublishedCollections => myPublishedCollections[id]);
  
  // export const selectSavedCollections = createSelector(
  //   selectResolvedCollections,
  //   selectSavedCollectionIds,
  //   (resolved, myIds) => {
  //     const mySavedCollections = Object.fromEntries(
  //       Object.entries(resolved).filter(([key, val]) => myIds.includes(key))
  //     );
  //     return mySavedCollections;
  //   }
  // );
  
  const makeSelectIsResolvingCollectionForId = id => reselect.createSelector(selectState$2, state => {
    return state.isResolvingCollectionById[id];
  });
  
  const makeSelectCollectionForId = id => reselect.createSelector(selectBuiltinCollections, selectResolvedCollections, selectMyUnpublishedCollections, selectMyEditedCollections, selectPendingCollections, (bLists, rLists, uLists, eLists, pLists) => {
    const collection = bLists[id] || uLists[id] || eLists[id] || rLists[id] || pLists[id];
    return collection;
  });
  
  const makeSelectCollectionForIdHasClaimUrl = (id, url) => reselect.createSelector(makeSelectCollectionForId(id), collection => collection && collection.items.includes(url));
  
  const makeSelectUrlsForCollectionId = id => reselect.createSelector(makeSelectCollectionForId(id), collection => collection && collection.items);
  
  const makeSelectClaimIdsForCollectionId = id => reselect.createSelector(makeSelectCollectionForId(id), collection => {
    const items = collection && collection.items || [];
    const ids = items.map(item => {
      const { claimId } = parseURI(item);
      return claimId;
    });
    return ids;
  });
  
  const makeSelectIndexForUrlInCollection = (url, id) => reselect.createSelector(makeSelectUrlsForCollectionId(id), makeSelectClaimForUri(url), (urls, claim) => {
    const index = urls && urls.findIndex(u => u === url);
    if (index > -1) {
      return index;
    } else if (claim) {
      const index = urls && urls.findIndex(u => u === claim.permanent_url);
      if (index > -1) return index;
      return claim;
    }
    return null;
  });
  
  const makeSelectNextUrlForCollectionAndUrl = (id, url) => reselect.createSelector(makeSelectIndexForUrlInCollection(url, id), selectClaimsByUri, makeSelectUrlsForCollectionId(id), (index, claims, urls) => {
    if (index > -1) {
      // We'll get the next playble url
      const remainingUrls = urls.slice(index + 1);
      const nextUrl = remainingUrls.find(u => claims[u].value.stream_type && (claims[u].value.stream_type === 'video' || claims[u].value.stream_type === 'audio'));
      return nextUrl || null;
    } else {
      return null;
    }
  });
  
  const makeSelectNameForCollectionId = id => reselect.createSelector(makeSelectCollectionForId(id), collection => {
    return collection && collection.name || '';
  });
  
  const makeSelectCountForCollectionId = id => reselect.createSelector(makeSelectCollectionForId(id), collection => {
    if (collection) {
      if (collection.itemCount !== undefined) {
        return collection.itemCount;
      }
      return collection.items.length;
    }
    return null;
  });
  
  var _extends$5 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  function _asyncToGenerator$1(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
  
  function doResolveUris(uris, returnCachedClaims = false, resolveReposts = true) {
    return (dispatch, getState) => {
      const normalizedUris = uris.map(normalizeURI);
      const state = getState();
  
      const resolvingUris = selectResolvingUris(state);
      const claimsByUri = selectClaimsByUri(state);
      const urisToResolve = normalizedUris.filter(uri => {
        if (resolvingUris.includes(uri)) {
          return false;
        }
  
        return returnCachedClaims ? !claimsByUri[uri] : true;
      });
  
      if (urisToResolve.length === 0) {
        return;
      }
  
      const options = {
        include_purchase_receipt: true
      };
  
      if (urisToResolve.length === 1) {
        options.include_is_my_output = true;
      }
      dispatch({
        type: RESOLVE_URIS_STARTED,
        data: { uris: normalizedUris }
      });
  
      const resolveInfo = {};
  
      const collectionIds = [];
  
      return lbryProxy.resolve(_extends$5({ urls: urisToResolve }, options)).then((() => {
        var _ref = _asyncToGenerator$1(function* (result) {
          let repostedResults = {};
          const repostsToResolve = [];
          const fallbackResolveInfo = {
            stream: null,
            claimsInChannel: null,
            channel: null
          };
  
          function processResult(result, resolveInfo = {}, checkReposts = false) {
            Object.entries(result).forEach(([uri, uriResolveInfo]) => {
              // Flow has terrible Object.entries support
              // https://github.com/facebook/flow/issues/2221
              if (uriResolveInfo) {
                if (uriResolveInfo.error) {
                  // $FlowFixMe
                  resolveInfo[uri] = _extends$5({}, fallbackResolveInfo);
                } else {
                  if (checkReposts) {
                    if (uriResolveInfo.reposted_claim) {
                      // $FlowFixMe
                      const repostUrl = uriResolveInfo.reposted_claim.permanent_url;
                      if (!resolvingUris.includes(repostUrl)) {
                        repostsToResolve.push(repostUrl);
                      }
                    }
                  }
                  let result = {};
                  if (uriResolveInfo.value_type === 'channel') {
                    result.channel = uriResolveInfo;
                    // $FlowFixMe
                    result.claimsInChannel = uriResolveInfo.meta.claims_in_channel;
                  } else if (uriResolveInfo.value_type === 'collection') {
                    result.collection = uriResolveInfo;
                    // $FlowFixMe
                    collectionIds.push(uriResolveInfo.claim_id);
                  } else {
                    result.stream = uriResolveInfo;
                    if (uriResolveInfo.signing_channel) {
                      result.channel = uriResolveInfo.signing_channel;
                      result.claimsInChannel = uriResolveInfo.signing_channel.meta && uriResolveInfo.signing_channel.meta.claims_in_channel || 0;
                    }
                  }
                  // $FlowFixMe
                  resolveInfo[uri] = result;
                }
              }
            });
          }
          processResult(result, resolveInfo, resolveReposts);
  
          if (repostsToResolve.length) {
            dispatch({
              type: RESOLVE_URIS_STARTED,
              data: { uris: repostsToResolve, debug: 'reposts' }
            });
            repostedResults = yield lbryProxy.resolve(_extends$5({ urls: repostsToResolve }, options));
          }
          processResult(repostedResults, resolveInfo);
  
          dispatch({
            type: RESOLVE_URIS_COMPLETED,
            data: { resolveInfo }
          });
  
          if (collectionIds.length) {
            dispatch(doFetchItemsInCollections({ collectionIds: collectionIds, pageSize: 5 }));
          }
  
          return result;
        });
  
        return function (_x) {
          return _ref.apply(this, arguments);
        };
      })());
    };
  }
  
  function doResolveUri(uri) {
    return doResolveUris([uri]);
  }
  
  function doFetchClaimListMine(page = 1, pageSize = 99999, resolve = true, filterBy = []) {
    return dispatch => {
      dispatch({
        type: FETCH_CLAIM_LIST_MINE_STARTED
      });
  
      let claimTypes = ['stream', 'repost'];
      if (filterBy && filterBy.length !== 0) {
        claimTypes = claimTypes.filter(t => filterBy.includes(t));
      }
  
      // $FlowFixMe
      lbryProxy.claim_list({
        page: page,
        page_size: pageSize,
        claim_type: claimTypes,
        resolve
      }).then(result => {
        dispatch({
          type: FETCH_CLAIM_LIST_MINE_COMPLETED,
          data: {
            result,
            resolve
          }
        });
      });
    };
  }
  
  function doAbandonTxo(txo, cb) {
    return dispatch => {
      if (cb) cb(PENDING);
      const isClaim = txo.type === 'claim';
      const isSupport = txo.type === 'support' && txo.is_my_input === true;
      const isTip = txo.type === 'support' && txo.is_my_input === false;
  
      const data = isClaim ? { claimId: txo.claim_id } : { outpoint: `${txo.txid}:${txo.nout}` };
  
      const startedActionType = isClaim ? ABANDON_CLAIM_STARTED : ABANDON_SUPPORT_STARTED;
      const completedActionType = isClaim ? ABANDON_CLAIM_SUCCEEDED : ABANDON_SUPPORT_COMPLETED;
  
      dispatch({
        type: startedActionType,
        data
      });
  
      const errorCallback = () => {
        if (cb) cb(ERROR);
        dispatch(doToast({
          message: isClaim ? 'Error abandoning your claim/support' : 'Error unlocking your tip',
          isError: true
        }));
      };
  
      const successCallback = () => {
        dispatch({
          type: completedActionType,
          data
        });
  
        let abandonMessage;
        if (isClaim) {
          abandonMessage = __('Successfully abandoned your claim.');
        } else if (isSupport) {
          abandonMessage = __('Successfully abandoned your support.');
        } else {
          abandonMessage = __('Successfully unlocked your tip!');
        }
        if (cb) cb(DONE);
  
        dispatch(doToast({
          message: abandonMessage
        }));
      };
  
      const abandonParams = {
        blocking: true
      };
      if (isClaim) {
        abandonParams['claim_id'] = txo.claim_id;
      } else {
        abandonParams['txid'] = txo.txid;
        abandonParams['nout'] = txo.nout;
      }
  
      let method;
      if (isSupport || isTip) {
        method = 'support_abandon';
      } else if (isClaim) {
        const { normalized_name: claimName } = txo;
        method = claimName.startsWith('@') ? 'channel_abandon' : 'stream_abandon';
      }
  
      if (!method) {
        console.error('No "method" chosen for claim or support abandon');
        return;
      }
  
      lbryProxy[method](abandonParams).then(successCallback, errorCallback);
    };
  }
  
  function doAbandonClaim(txid, nout, cb) {
    const outpoint = `${txid}:${nout}`;
  
    return (dispatch, getState) => {
      const state = getState();
      const myClaims = selectMyClaimsRaw(state);
      const mySupports = selectSupportsByOutpoint(state);
  
      // A user could be trying to abandon a support or one of their claims
      const claimToAbandon = myClaims.find(claim => claim.txid === txid && claim.nout === nout);
      const supportToAbandon = mySupports[outpoint];
  
      if (!claimToAbandon && !supportToAbandon) {
        console.error('No associated support or claim with txid: ', txid);
        return;
      }
  
      const data = claimToAbandon ? { claimId: claimToAbandon.claim_id } : { outpoint: `${supportToAbandon.txid}:${supportToAbandon.nout}` };
  
      const isClaim = !!claimToAbandon;
      const startedActionType = isClaim ? ABANDON_CLAIM_STARTED : ABANDON_SUPPORT_STARTED;
      const completedActionType = isClaim ? ABANDON_CLAIM_SUCCEEDED : ABANDON_SUPPORT_COMPLETED;
  
      dispatch({
        type: startedActionType,
        data
      });
  
      const errorCallback = () => {
        dispatch(doToast({
          message: isClaim ? 'Error abandoning your claim/support' : 'Error unlocking your tip',
          isError: true
        }));
        if (cb) cb(ERROR);
      };
  
      const successCallback = () => {
        dispatch({
          type: completedActionType,
          data
        });
        if (cb) cb(DONE);
  
        let abandonMessage;
        if (isClaim) {
          abandonMessage = __('Successfully abandoned your claim.');
        } else if (supportToAbandon) {
          abandonMessage = __('Successfully abandoned your support.');
        } else {
          abandonMessage = __('Successfully unlocked your tip!');
        }
  
        dispatch(doToast({
          message: abandonMessage
        }));
        dispatch(doFetchTxoPage());
      };
  
      const abandonParams = {
        txid,
        nout,
        blocking: true
      };
  
      let method;
      if (supportToAbandon) {
        method = 'support_abandon';
      } else if (claimToAbandon) {
        const { name: claimName } = claimToAbandon;
        method = claimName.startsWith('@') ? 'channel_abandon' : 'stream_abandon';
      }
  
      if (!method) {
        console.error('No "method" chosen for claim or support abandon');
        return;
      }
  
      lbryProxy[method](abandonParams).then(successCallback, errorCallback);
    };
  }
  
  function doFetchClaimsByChannel(uri, page = 1) {
    return dispatch => {
      dispatch({
        type: FETCH_CHANNEL_CLAIMS_STARTED,
        data: { uri, page }
      });
  
      lbryProxy.claim_search({
        channel: uri,
        valid_channel_signature: true,
        page: page || 1,
        order_by: ['release_time'],
        include_is_my_output: true,
        include_purchase_receipt: true
      }).then(result => {
        const { items: claims, total_items: claimsInChannel, page: returnedPage } = result;
  
        dispatch({
          type: FETCH_CHANNEL_CLAIMS_COMPLETED,
          data: {
            uri,
            claimsInChannel,
            claims: claims || [],
            page: returnedPage || undefined
          }
        });
      });
    };
  }
  
  function doClearChannelErrors() {
    return {
      type: CLEAR_CHANNEL_ERRORS
    };
  }
  
  function doCreateChannel(name, amount, optionalParams, cb) {
    return dispatch => {
      dispatch({
        type: CREATE_CHANNEL_STARTED
      });
  
      const createParams = {
        name,
        bid: creditsToString(amount),
        blocking: true
      };
  
      if (optionalParams) {
        if (optionalParams.title) {
          createParams.title = optionalParams.title;
        }
        if (optionalParams.coverUrl) {
          createParams.cover_url = optionalParams.coverUrl;
        }
        if (optionalParams.thumbnailUrl) {
          createParams.thumbnail_url = optionalParams.thumbnailUrl;
        }
        if (optionalParams.description) {
          createParams.description = optionalParams.description;
        }
        if (optionalParams.website) {
          createParams.website_url = optionalParams.website;
        }
        if (optionalParams.email) {
          createParams.email = optionalParams.email;
        }
        if (optionalParams.tags) {
          createParams.tags = optionalParams.tags.map(tag => tag.name);
        }
        if (optionalParams.languages) {
          createParams.languages = optionalParams.languages;
        }
      }
  
      return lbryProxy.channel_create(createParams)
      // outputs[0] is the certificate
      // outputs[1] is the change from the tx, not in the app currently
      .then(result => {
        const channelClaim = result.outputs[0];
        dispatch({
          type: CREATE_CHANNEL_COMPLETED,
          data: { channelClaim }
        });
        dispatch({
          type: UPDATE_PENDING_CLAIMS,
          data: {
            claims: [channelClaim]
          }
        });
        dispatch(doCheckPendingClaims(cb));
        return channelClaim;
      }).catch(error => {
        dispatch({
          type: CREATE_CHANNEL_FAILED,
          data: error.message
        });
      });
    };
  }
  
  function doUpdateChannel(params, cb) {
    return (dispatch, getState) => {
      dispatch({
        type: UPDATE_CHANNEL_STARTED
      });
      const state = getState();
      const myChannels = selectMyChannelClaims(state);
      const channelClaim = myChannels.find(myChannel => myChannel.claim_id === params.claim_id);
  
      const updateParams = {
        claim_id: params.claim_id,
        bid: creditsToString(params.amount),
        title: params.title,
        cover_url: params.coverUrl,
        thumbnail_url: params.thumbnailUrl,
        description: params.description,
        website_url: params.website,
        email: params.email,
        tags: [],
        replace: true,
        languages: params.languages || [],
        locations: [],
        blocking: true
      };
  
      if (params.tags) {
        updateParams.tags = params.tags.map(tag => tag.name);
      }
  
      // we'll need to remove these once we add locations/channels to channel page edit/create options
      if (channelClaim && channelClaim.value && channelClaim.value.locations) {
        updateParams.locations = channelClaim.value.locations;
      }
  
      return lbryProxy.channel_update(updateParams).then(result => {
        const channelClaim = result.outputs[0];
        dispatch({
          type: UPDATE_CHANNEL_COMPLETED,
          data: { channelClaim }
        });
        dispatch({
          type: UPDATE_PENDING_CLAIMS,
          data: {
            claims: [channelClaim]
          }
        });
        dispatch(doCheckPendingClaims(cb));
        return Boolean(result.outputs[0]);
      }).then().catch(error => {
        dispatch({
          type: UPDATE_CHANNEL_FAILED,
          data: error
        });
      });
    };
  }
  
  function doImportChannel(certificate) {
    return dispatch => {
      dispatch({
        type: IMPORT_CHANNEL_STARTED
      });
  
      return lbryProxy.channel_import({ channel_data: certificate }).then(() => {
        dispatch({
          type: IMPORT_CHANNEL_COMPLETED
        });
      }).catch(error => {
        dispatch({
          type: IMPORT_CHANNEL_FAILED,
          data: error
        });
      });
    };
  }
  
  function doFetchChannelListMine(page = 1, pageSize = 99999, resolve = true) {
    return dispatch => {
      dispatch({
        type: FETCH_CHANNEL_LIST_STARTED
      });
  
      const callback = response => {
        dispatch({
          type: FETCH_CHANNEL_LIST_COMPLETED,
          data: { claims: response.items }
        });
      };
  
      const failure = error => {
        dispatch({
          type: FETCH_CHANNEL_LIST_FAILED,
          data: error
        });
      };
  
      lbryProxy.channel_list({ page, page_size: pageSize, resolve }).then(callback, failure);
    };
  }
  
  function doFetchCollectionListMine(page = 1, pageSize = 99999) {
    return dispatch => {
      dispatch({
        type: FETCH_COLLECTION_LIST_STARTED
      });
  
      const callback = response => {
        const { items } = response;
        dispatch({
          type: FETCH_COLLECTION_LIST_COMPLETED,
          data: { claims: items }
        });
        dispatch(doFetchItemsInCollections({
          collectionIds: items.map(claim => claim.claim_id),
          page_size: 5
        }));
      };
  
      const failure = error => {
        dispatch({
          type: FETCH_COLLECTION_LIST_FAILED,
          data: error
        });
      };
  
      lbryProxy.collection_list({ page, page_size: pageSize, resolve_claims: 1, resolve: true }).then(callback, failure);
    };
  }
  
  function doClaimSearch(options = {
    no_totals: true,
    page_size: 10,
    page: 1
  }) {
    const query = createNormalizedClaimSearchKey(options);
    return (() => {
      var _ref2 = _asyncToGenerator$1(function* (dispatch) {
        dispatch({
          type: CLAIM_SEARCH_STARTED,
          data: { query: query }
        });
  
        const success = function (data) {
          const resolveInfo = {};
          const urls = [];
          data.items.forEach(function (stream) {
            resolveInfo[stream.canonical_url] = { stream };
            urls.push(stream.canonical_url);
          });
  
          dispatch({
            type: CLAIM_SEARCH_COMPLETED,
            data: {
              query,
              resolveInfo,
              urls,
              append: options.page && options.page !== 1,
              pageSize: options.page_size
            }
          });
          return resolveInfo;
        };
  
        const failure = function (err) {
          dispatch({
            type: CLAIM_SEARCH_FAILED,
            data: { query },
            error: err
          });
          return false;
        };
  
        return yield lbryProxy.claim_search(_extends$5({}, options, {
          include_purchase_receipt: true
        })).then(success, failure);
      });
  
      return function (_x2) {
        return _ref2.apply(this, arguments);
      };
    })();
  }
  
  function doRepost(options) {
    return dispatch => {
      return new Promise(resolve => {
        dispatch({
          type: CLAIM_REPOST_STARTED
        });
  
        function success(response) {
          const repostClaim = response.outputs[0];
          dispatch({
            type: CLAIM_REPOST_COMPLETED,
            data: {
              originalClaimId: options.claim_id,
              repostClaim
            }
          });
          dispatch({
            type: UPDATE_PENDING_CLAIMS,
            data: {
              claims: [repostClaim]
            }
          });
  
          dispatch(doFetchClaimListMine(1, 10));
          resolve(repostClaim);
        }
  
        function failure(error) {
          dispatch({
            type: CLAIM_REPOST_FAILED,
            data: {
              error: error.message
            }
          });
        }
  
        lbryProxy.stream_repost(options).then(success, failure);
      });
    };
  }
  
  function doCollectionPublish(options, localId) {
    return dispatch => {
      // $FlowFixMe
  
      const params = {
        name: options.name,
        bid: creditsToString(options.bid),
        title: options.title,
        thumbnail_url: options.thumbnail_url,
        description: options.description,
        tags: [],
        languages: options.languages || [],
        locations: [],
        blocking: true,
        claims: options.claims
      };
  
      if (options.tags) {
        params['tags'] = options.tags.map(tag => tag.name);
      }
  
      if (options.channel_id) {
        params['channel_id'] = options.channel_id;
      }
  
      return new Promise(resolve => {
        dispatch({
          type: COLLECTION_PUBLISH_STARTED
        });
  
        function success(response) {
          const collectionClaim = response.outputs[0];
          dispatch(batchActions({
            type: COLLECTION_PUBLISH_COMPLETED,
            data: { claimId: collectionClaim.claim_id }
          },
          // move unpublished collection to pending collection with new publish id
          // recent publish won't resolve this second. handle it in checkPending
          {
            type: UPDATE_PENDING_CLAIMS,
            data: {
              claims: [collectionClaim]
            }
          }));
          dispatch({
            type: COLLECTION_PENDING,
            data: { localId: localId, claimId: collectionClaim.claim_id }
          });
          dispatch(doCheckPendingClaims());
          dispatch(doFetchCollectionListMine(1, 10));
          return resolve(collectionClaim);
        }
  
        function failure(error) {
          dispatch({
            type: COLLECTION_PUBLISH_FAILED,
            data: {
              error: error.message
            }
          });
        }
  
        return lbryProxy.collection_create(params).then(success, failure);
      });
    };
  }
  
  function doCollectionPublishUpdate(options) {
    return dispatch => {
      // TODO: implement one click update
  
      const updateParams = {
        bid: creditsToString(options.bid),
        title: options.title,
        thumbnail_url: options.thumbnail_url,
        description: options.description,
        tags: [],
        languages: options.languages || [],
        locations: [],
        blocking: true,
        claim_id: options.claim_id,
        clear_claims: true
      };
  
      if (options.tags) {
        updateParams['tags'] = options.tags.map(tag => tag.name);
      }
  
      if (options.claims) {
        updateParams['claims'] = options.claims;
      }
      return new Promise(resolve => {
        dispatch({
          type: COLLECTION_PUBLISH_UPDATE_STARTED
        });
  
        function success(response) {
          const collectionClaim = response.outputs[0];
          dispatch({
            type: COLLECTION_PUBLISH_UPDATE_COMPLETED,
            data: {
              collectionClaim
            }
          });
          dispatch({
            type: COLLECTION_PENDING,
            data: { claimId: collectionClaim.claim_id }
          });
          dispatch({
            type: UPDATE_PENDING_CLAIMS,
            data: {
              claims: [collectionClaim]
            }
          });
          dispatch(doCheckPendingClaims());
          dispatch(doFetchCollectionListMine(1, 10));
          return resolve(collectionClaim);
        }
  
        function failure(error) {
          dispatch({
            type: COLLECTION_PUBLISH_UPDATE_FAILED,
            data: {
              error: error.message
            }
          });
        }
  
        return lbryProxy.collection_update(updateParams).then(success, failure);
      });
    };
  }
  
  function doCheckPublishNameAvailability(name) {
    return dispatch => {
      dispatch({
        type: CHECK_PUBLISH_NAME_STARTED
      });
  
      return lbryProxy.claim_list({ name: name }).then(result => {
        dispatch({
          type: CHECK_PUBLISH_NAME_COMPLETED
        });
        if (result.items.length) {
          dispatch({
            type: FETCH_CLAIM_LIST_MINE_COMPLETED,
            data: {
              result,
              resolve: false
            }
          });
        }
        return !(result && result.items && result.items.length);
      });
    };
  }
  
  function doClearRepostError() {
    return {
      type: CLEAR_REPOST_ERROR
    };
  }
  
  function doPurchaseList(page = 1, pageSize = PAGE_SIZE) {
    return dispatch => {
      dispatch({
        type: PURCHASE_LIST_STARTED
      });
  
      const success = result => {
        return dispatch({
          type: PURCHASE_LIST_COMPLETED,
          data: {
            result
          }
        });
      };
  
      const failure = error => {
        dispatch({
          type: PURCHASE_LIST_FAILED,
          data: {
            error: error.message
          }
        });
      };
  
      lbryProxy.purchase_list({
        page: page,
        page_size: pageSize,
        resolve: true
      }).then(success, failure);
    };
  }
  
  const doCheckPendingClaims = onConfirmed => (dispatch, getState) => {
    let claimCheckInterval;
  
    const checkClaimList = () => {
      const state = getState();
      const pendingIdSet = new Set(selectPendingIds(state));
      const pendingCollections = selectPendingCollections(state);
      lbryProxy.claim_list({ page: 1, page_size: 10 }).then(result => {
        const claims = result.items;
        const claimsToConfirm = [];
        claims.forEach(claim => {
          const { claim_id: claimId } = claim;
          if (claim.confirmations > 0 && pendingIdSet.has(claimId)) {
            pendingIdSet.delete(claimId);
            if (Object.keys(pendingCollections).includes(claim.claim_id)) {
              dispatch(doFetchItemsInCollection({ collectionId: claim.claim_id }));
              dispatch(doCollectionDelete(claim.claim_id, 'pending'));
            }
            claimsToConfirm.push(claim);
            if (onConfirmed) {
              onConfirmed(claim);
            }
          }
        });
        if (claimsToConfirm.length) {
          dispatch({
            type: UPDATE_CONFIRMED_CLAIMS,
            data: {
              claims: claimsToConfirm
            }
          });
        }
        return pendingIdSet.size;
      }).then(len => {
        if (!len) {
          clearInterval(claimCheckInterval);
        }
      });
    };
  
    claimCheckInterval = setInterval(() => {
      checkClaimList();
    }, 30000);
  };
  
  function _asyncToGenerator$2(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
  
  const getTimestamp = () => {
    return Math.floor(Date.now() / 1000);
  };
  
  const FETCH_BATCH_SIZE = 10;
  
  const doLocalCollectionCreate = (name, collectionItems, type, sourceId) => dispatch => {
    return dispatch({
      type: COLLECTION_NEW,
      data: {
        entry: {
          id: uuid.v4(), // start with a uuid, this becomes a claimId after publish
          name: name,
          updatedAt: getTimestamp(),
          items: collectionItems || [],
          sourceId: sourceId,
          type: type
        }
      }
    });
  };
  
  const doCollectionDelete = (id, colKey = undefined) => (dispatch, getState) => {
    const state = getState();
    const claim = makeSelectClaimForClaimId(id)(state);
    const collectionDelete = () => dispatch({
      type: COLLECTION_DELETE,
      data: {
        id: id,
        collectionKey: colKey
      }
    });
    if (claim && !colKey) {
      // could support "abandon, but keep" later
      const { txid, nout } = claim;
      return dispatch(doAbandonClaim(txid, nout, collectionDelete));
    }
    return collectionDelete();
  };
  
  // Given a collection, save its collectionId to be resolved and displayed in Library
  // export const doCollectionSave = (
  //   id: string,
  // ) => (dispatch: Dispatch) => {
  //   return dispatch({
  //     type: ACTIONS.COLLECTION_SAVE,
  //     data: {
  //       id: id,
  //     },
  //   });
  // };
  
  // Given a collection and name, copy it to a local private collection with a name
  // export const doCollectionCopy = (
  //   id: string,
  // ) => (dispatch: Dispatch) => {
  //   return dispatch({
  //     type: ACTIONS.COLLECTION_COPY,
  //     data: {
  //       id: id,
  //     },
  //   });
  // };
  
  const doFetchItemsInCollections = (resolveItemsOptions, resolveStartedCallback) => (() => {
    var _ref = _asyncToGenerator$2(function* (dispatch, getState) {
      let fetchItemsForCollectionClaim = (() => {
        var _ref2 = _asyncToGenerator$2(function* (claim, pageSize) {
          const totalItems = claim.value.claims && claim.value.claims.length;
          const claimId = claim.claim_id;
          const itemOrder = claim.value.claims;
  
          const sortResults = function (items, claimList) {
            const newItems = [];
            claimList.forEach(function (id) {
              const index = items.findIndex(function (i) {
                return i.claim_id === id;
              });
              if (index >= 0) {
                newItems.push(items[index]);
              }
            });
            /*
              This will return newItems[] of length less than total_items below
              if one or more of the claims has been abandoned. That's ok for now.
            */
            return newItems;
          };
  
          const mergeBatches = function (arrayOfResults, claimList) {
            const mergedResults = {
              items: [],
              total_items: 0
            };
            arrayOfResults.forEach(function (result) {
              mergedResults.items = mergedResults.items.concat(result.items);
              mergedResults.total_items = result.total_items;
            });
  
            mergedResults.items = sortResults(mergedResults.items, claimList);
            return mergedResults;
          };
  
          try {
            const batchSize = pageSize || FETCH_BATCH_SIZE;
            const batches = [];
  
            for (let i = 0; i < Math.ceil(totalItems / batchSize); i++) {
              batches[i] = lbryProxy.claim_search({
                claim_ids: claim.value.claims,
                page: i + 1,
                page_size: batchSize
              });
            }
            const itemsInBatches = yield Promise.all(batches);
            const result = mergeBatches(itemsInBatches, itemOrder);
  
            // $FlowFixMe
            const itemsById = { claimId: claimId };
            if (result.items) {
              itemsById.items = result.items;
            } else {
              itemsById.items = null;
            }
            return itemsById;
          } catch (e) {
            return {
              claimId: claimId,
              items: null
            };
          }
        });
  
        return function fetchItemsForCollectionClaim(_x3, _x4) {
          return _ref2.apply(this, arguments);
        };
      })();
  
      /*
      1) make sure all the collection claims are loaded into claims reducer, search/resolve if necessary.
      2) get the item claims for each
      3) format and make sure they're in the order as in the claim
      4) Build the collection objects and update collections reducer
      5) Update redux claims reducer
       */
      let state = getState();
      const { collectionIds, pageSize } = resolveItemsOptions;
  
      dispatch({
        type: COLLECTION_ITEMS_RESOLVE_STARTED,
        data: { ids: collectionIds }
      });
  
      if (resolveStartedCallback) resolveStartedCallback();
  
      const collectionIdsToSearch = collectionIds.filter(function (claimId) {
        return !state.claims.byId[claimId];
      });
  
      if (collectionIdsToSearch.length) {
        yield dispatch(doClaimSearch({ claim_ids: collectionIdsToSearch, page: 1, page_size: 9999 }));
      }
  
      const stateAfterClaimSearch = getState();
  
      function formatForClaimActions(resultClaimsByUri) {
        const formattedClaims = {};
        Object.entries(resultClaimsByUri).forEach(([uri, uriResolveInfo]) => {
          // Flow has terrible Object.entries support
          // https://github.com/facebook/flow/issues/2221
          if (uriResolveInfo) {
            let result = {};
            if (uriResolveInfo.value_type === 'channel') {
              result.channel = uriResolveInfo;
              // $FlowFixMe
              result.claimsInChannel = uriResolveInfo.meta.claims_in_channel;
              // ALSO SKIP COLLECTIONS
            } else if (uriResolveInfo.value_type === 'collection') {
              result.collection = uriResolveInfo;
            } else {
              result.stream = uriResolveInfo;
              if (uriResolveInfo.signing_channel) {
                result.channel = uriResolveInfo.signing_channel;
                result.claimsInChannel = uriResolveInfo.signing_channel.meta && uriResolveInfo.signing_channel.meta.claims_in_channel || 0;
              }
            }
            // $FlowFixMe
            formattedClaims[uri] = result;
          }
        });
        return formattedClaims;
      }
  
      const invalidCollectionIds = [];
      const promisedCollectionItemFetches = [];
      collectionIds.forEach(function (collectionId) {
        const claim = makeSelectClaimForClaimId(collectionId)(stateAfterClaimSearch);
        if (!claim) {
          invalidCollectionIds.push(collectionId);
        } else {
          promisedCollectionItemFetches.push(fetchItemsForCollectionClaim(claim, pageSize));
        }
      });
  
      // $FlowFixMe
      const collectionItemsById = yield Promise.all(promisedCollectionItemFetches);
  
      const newCollectionObjectsById = {};
      const resolvedItemsByUrl = {};
      collectionItemsById.forEach(function (entry) {
        // $FlowFixMe
        const collectionItems = entry.items;
        const collectionId = entry.claimId;
        if (collectionItems) {
          const claim = makeSelectClaimForClaimId(collectionId)(stateAfterClaimSearch);
  
          const editedCollection = makeSelectEditedCollectionForId(collectionId)(stateAfterClaimSearch);
          const { name, timestamp, value } = claim || {};
          const { title } = value;
          const valueTypes = new Set();
          const streamTypes = new Set();
  
          let newItems = [];
          let isPlaylist;
  
          if (collectionItems) {
            collectionItems.forEach(function (collectionItem) {
              newItems.push(collectionItem.permanent_url);
              valueTypes.add(collectionItem.value_type);
              if (collectionItem.value.stream_type) {
                streamTypes.add(collectionItem.value.stream_type);
              }
              resolvedItemsByUrl[collectionItem.canonical_url] = collectionItem;
            });
            isPlaylist = valueTypes.size === 1 && valueTypes.has('stream') && (streamTypes.size === 1 && (streamTypes.has('audio') || streamTypes.has('video')) || streamTypes.size === 2 && streamTypes.has('audio') && streamTypes.has('video'));
          }
  
          newCollectionObjectsById[collectionId] = {
            items: newItems,
            id: collectionId,
            name: title || name,
            itemCount: claim.value.claims.length,
            type: isPlaylist ? 'playlist' : 'collection',
            updatedAt: timestamp
          };
  
          if (editedCollection && timestamp > editedCollection['updatedAt']) {
            dispatch({
              type: COLLECTION_DELETE,
              data: {
                id: collectionId,
                collectionKey: 'edited'
              }
            });
          }
        } else {
          invalidCollectionIds.push(collectionId);
        }
      });
      const formattedClaimsByUri = formatForClaimActions(collectionItemsById);
  
      dispatch({
        type: RESOLVE_URIS_COMPLETED,
        data: { resolveInfo: formattedClaimsByUri }
      });
  
      dispatch({
        type: COLLECTION_ITEMS_RESOLVE_COMPLETED,
        data: {
          resolvedCollections: newCollectionObjectsById,
          failedCollectionIds: invalidCollectionIds
        }
      });
    });
  
    return function (_x, _x2) {
      return _ref.apply(this, arguments);
    };
  })();
  
  const doFetchItemsInCollection = (options, cb) => {
    const { collectionId, pageSize } = options;
    const newOptions = {
      collectionIds: [collectionId]
    };
    if (pageSize) newOptions.pageSize = pageSize;
    return doFetchItemsInCollections(newOptions, cb);
  };
  
  const doCollectionEdit = (collectionId, params) => (() => {
    var _ref3 = _asyncToGenerator$2(function* (dispatch, getState) {
      const state = getState();
      const collection = makeSelectCollectionForId(collectionId)(state);
      const editedCollection = makeSelectEditedCollectionForId(collectionId)(state);
      const unpublishedCollection = makeSelectUnpublishedCollectionForId(collectionId)(state);
      const publishedCollection = makeSelectPublishedCollectionForId(collectionId)(state); // needs to be published only
  
      const generateCollectionItemsFromSearchResult = function (results) {
        return Object.values(results)
        // $FlowFixMe
        .reduce(function (acc, cur) {
          let url;
          if (cur.stream) {
            url = cur.stream.permanent_url;
          } else if (cur.channel) {
            url = cur.channel.permanent_url;
          } else if (cur.collection) {
            url = cur.collection.permanent_url;
          } else {
            return acc;
          }
          acc.push(url);
          return acc;
        }, []);
      };
  
      if (!collection) {
        return dispatch({
          type: COLLECTION_ERROR,
          data: {
            message: 'collection does not exist'
          }
        });
      }
  
      let currentItems = collection.items ? collection.items.concat() : [];
      const { claims: passedClaims, order, claimIds, replace, remove, type } = params;
  
      const collectionType = type || collection.type;
      let newItems = currentItems;
  
      if (passedClaims) {
        if (remove) {
          const passedUrls = passedClaims.map(function (claim) {
            return claim.permanent_url;
          });
          // $FlowFixMe // need this?
          newItems = currentItems.filter(function (item) {
            return !passedUrls.includes(item);
          });
        } else {
          passedClaims.forEach(function (claim) {
            return newItems.push(claim.permanent_url);
          });
        }
      }
  
      if (claimIds) {
        const batches = [];
        if (claimIds.length > 50) {
          for (let i = 0; i < Math.ceil(claimIds.length / 50); i++) {
            batches[i] = claimIds.slice(i * 50, (i + 1) * 50);
          }
        } else {
          batches[0] = claimIds;
        }
        const resultArray = yield Promise.all(batches.map(function (batch) {
          let options = { claim_ids: batch, page: 1, page_size: 50 };
          return dispatch(doClaimSearch(options));
        }));
  
        const searchResults = Object.assign({}, ...resultArray);
  
        if (replace) {
          newItems = generateCollectionItemsFromSearchResult(searchResults);
        } else {
          newItems = currentItems.concat(generateCollectionItemsFromSearchResult(searchResults));
        }
      }
  
      if (order) {
        const [movedItem] = currentItems.splice(order.from, 1);
        currentItems.splice(order.to, 0, movedItem);
      }
  
      // console.log('p&e', publishedCollection.items, newItems, publishedCollection.items.join(','), newItems.join(','))
      if (editedCollection) {
        // delete edited if newItems are the same as publishedItems
        if (publishedCollection.items.join(',') === newItems.join(',')) {
          dispatch({
            type: COLLECTION_DELETE,
            data: {
              id: collectionId,
              collectionKey: 'edited'
            }
          });
        } else {
          dispatch({
            type: COLLECTION_EDIT,
            data: {
              id: collectionId,
              collectionKey: 'edited',
              collection: {
                items: newItems,
                id: collectionId,
                name: params.name || collection.name,
                updatedAt: getTimestamp(),
                type: collectionType
              }
            }
          });
        }
      } else if (publishedCollection) {
        dispatch({
          type: COLLECTION_EDIT,
          data: {
            id: collectionId,
            collectionKey: 'edited',
            collection: {
              items: newItems,
              id: collectionId,
              name: params.name || collection.name,
              updatedAt: getTimestamp(),
              type: collectionType
            }
          }
        });
      } else if (BUILTIN_LISTS.includes(collectionId)) {
        dispatch({
          type: COLLECTION_EDIT,
          data: {
            id: collectionId,
            collectionKey: 'builtin',
            collection: {
              items: newItems,
              id: collectionId,
              name: params.name || collection.name,
              updatedAt: getTimestamp(),
              type: collectionType
            }
          }
        });
      } else if (unpublishedCollection) {
        dispatch({
          type: COLLECTION_EDIT,
          data: {
            id: collectionId,
            collectionKey: 'unpublished',
            collection: {
              items: newItems,
              id: collectionId,
              name: params.name || collection.name,
              updatedAt: getTimestamp(),
              type: collectionType
            }
          }
        });
      }
      return true;
    });
  
    return function (_x5, _x6) {
      return _ref3.apply(this, arguments);
    };
  })();
  
  const selectState$3 = state => state.fileInfo || {};
  
  const selectFileInfosByOutpoint = reselect.createSelector(selectState$3, state => state.byOutpoint || {});
  
  const selectIsFetchingFileList = reselect.createSelector(selectState$3, state => state.isFetchingFileList);
  
  const selectIsFetchingFileListDownloadedOrPublished = reselect.createSelector(selectIsFetchingFileList, selectIsFetchingClaimListMine, (isFetchingFileList, isFetchingClaimListMine) => isFetchingFileList || isFetchingClaimListMine);
  
  const makeSelectFileInfoForUri = uri => reselect.createSelector(selectClaimsByUri, selectFileInfosByOutpoint, (claims, byOutpoint) => {
    const claim = claims[uri];
    const outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined;
    return outpoint ? byOutpoint[outpoint] : undefined;
  });
  
  const selectDownloadingByOutpoint = reselect.createSelector(selectState$3, state => state.downloadingByOutpoint || {});
  
  const makeSelectDownloadingForUri = uri => reselect.createSelector(selectDownloadingByOutpoint, makeSelectFileInfoForUri(uri), (byOutpoint, fileInfo) => {
    if (!fileInfo) return false;
    return byOutpoint[fileInfo.outpoint];
  });
  
  const selectUrisLoading = reselect.createSelector(selectState$3, state => state.fetching || {});
  
  const makeSelectLoadingForUri = uri => reselect.createSelector(selectUrisLoading, makeSelectClaimForUri(uri), (fetchingByOutpoint, claim) => {
    if (!claim) {
      return false;
    }
  
    const { txid, nout } = claim;
    const outpoint = `${txid}:${nout}`;
    const isFetching = fetchingByOutpoint[outpoint];
    return isFetching;
  });
  
  const selectFileInfosDownloaded = reselect.createSelector(selectFileInfosByOutpoint, selectMyClaims, (byOutpoint, myClaims) => Object.values(byOutpoint).reverse().filter(fileInfo => {
    const myClaimIds = myClaims.map(claim => claim.claim_id);
  
    return fileInfo && myClaimIds.indexOf(fileInfo.claim_id) === -1 && (fileInfo.completed || fileInfo.written_bytes > 0 || fileInfo.blobs_completed > 0);
  }));
  
  // export const selectFileInfoForUri = (state, props) => {
  //   const claims = selectClaimsByUri(state),
  //     claim = claims[props.uri],
  //     fileInfos = selectAllFileInfos(state),
  //     outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined;
  
  //   return outpoint && fileInfos ? fileInfos[outpoint] : undefined;
  // };
  
  const selectDownloadingFileInfos = reselect.createSelector(selectDownloadingByOutpoint, selectFileInfosByOutpoint, (downloadingByOutpoint, fileInfosByOutpoint) => {
    const outpoints = Object.keys(downloadingByOutpoint);
    const fileInfos = [];
  
    outpoints.forEach(outpoint => {
      const fileInfo = fileInfosByOutpoint[outpoint];
  
      if (fileInfo) fileInfos.push(fileInfo);
    });
  
    return fileInfos;
  });
  
  const selectTotalDownloadProgress = reselect.createSelector(selectDownloadingFileInfos, fileInfos => {
    const progress = [];
  
    fileInfos.forEach(fileInfo => {
      progress.push(fileInfo.written_bytes / fileInfo.total_bytes * 100);
    });
  
    const totalProgress = progress.reduce((a, b) => a + b, 0);
  
    if (fileInfos.length > 0) return totalProgress / fileInfos.length / 100.0;
    return -1;
  });
  
  const selectFileListPublishedSort = reselect.createSelector(selectState$3, state => state.fileListPublishedSort);
  
  const selectFileListDownloadedSort = reselect.createSelector(selectState$3, state => state.fileListDownloadedSort);
  
  const selectDownloadedUris = reselect.createSelector(selectFileInfosDownloaded,
  // We should use permament_url but it doesn't exist in file_list
  info => info.slice().map(claim => `lbry://${claim.claim_name}#${claim.claim_id}`));
  
  const makeSelectMediaTypeForUri = uri => reselect.createSelector(makeSelectFileInfoForUri(uri), makeSelectContentTypeForUri(uri), (fileInfo, contentType) => {
    if (!fileInfo && !contentType) {
      return undefined;
    }
  
    const fileName = fileInfo && fileInfo.file_name;
    return lbryProxy.getMediaType(contentType, fileName);
  });
  
  const makeSelectUriIsStreamable = uri => reselect.createSelector(makeSelectMediaTypeForUri(uri), mediaType => {
    const isStreamable = ['audio', 'video', 'image'].indexOf(mediaType) !== -1;
    return isStreamable;
  });
  
  const makeSelectDownloadPathForUri = uri => reselect.createSelector(makeSelectFileInfoForUri(uri), fileInfo => {
    return fileInfo && fileInfo.download_path;
  });
  
  const makeSelectFilePartlyDownloaded = uri => reselect.createSelector(makeSelectFileInfoForUri(uri), fileInfo => {
    if (!fileInfo) {
      return false;
    }
  
    return fileInfo.written_bytes > 0 || fileInfo.blobs_completed > 0;
  });
  
  const makeSelectFileNameForUri = uri => reselect.createSelector(makeSelectFileInfoForUri(uri), fileInfo => {
    return fileInfo && fileInfo.file_name;
  });
  
  const selectDownloadUrlsCount = reselect.createSelector(selectDownloadedUris, uris => uris.length);
  
  function filterFileInfos(fileInfos, query) {
    if (query) {
      const queryMatchRegExp = new RegExp(query, 'i');
      return fileInfos.filter(fileInfo => {
        const { metadata } = fileInfo;
  
        return metadata.title && metadata.title.match(queryMatchRegExp) || fileInfo.channel_name && fileInfo.channel_name.match(queryMatchRegExp) || fileInfo.claim_name && fileInfo.claim_name.match(queryMatchRegExp);
      });
    }
  
    return fileInfos;
  }
  
  const makeSelectSearchDownloadUrlsForPage = (query, page = 1) => reselect.createSelector(selectFileInfosDownloaded, fileInfos => {
    const matchingFileInfos = filterFileInfos(fileInfos, query);
    const start = (Number(page) - 1) * Number(PAGE_SIZE);
    const end = Number(page) * Number(PAGE_SIZE);
  
    return matchingFileInfos && matchingFileInfos.length ? matchingFileInfos.slice(start, end).map(fileInfo => buildURI({
      streamName: fileInfo.claim_name,
      channelName: fileInfo.channel_name,
      channelClaimId: fileInfo.channel_claim_id
    })) : [];
  });
  
  const makeSelectSearchDownloadUrlsCount = query => reselect.createSelector(selectFileInfosDownloaded, fileInfos => {
    return fileInfos && fileInfos.length ? filterFileInfos(fileInfos, query).length : 0;
  });
  
  const makeSelectStreamingUrlForUri = uri => reselect.createSelector(makeSelectFileInfoForUri(uri), fileInfo => {
    return fileInfo && fileInfo.streaming_url;
  });
  
  //      
  
  function doFileGet(uri, saveFile = true, onSuccess) {
    return (dispatch, getState) => {
      const state = getState();
      const { nout, txid } = makeSelectClaimForUri(uri)(state);
      const outpoint = `${txid}:${nout}`;
  
      dispatch({
        type: FETCH_FILE_INFO_STARTED,
        data: {
          outpoint
        }
      });
  
      // set save_file argument to True to save the file (old behaviour)
      lbryProxy.get({ uri, save_file: saveFile }).then(streamInfo => {
        const timeout = streamInfo === null || typeof streamInfo !== 'object' || streamInfo.error === 'Timeout';
        if (timeout) {
          dispatch({
            type: FETCH_FILE_INFO_FAILED,
            data: { outpoint }
          });
  
          dispatch(doToast({ message: `File timeout for uri ${uri}`, isError: true }));
        } else {
          if (streamInfo.purchase_receipt || streamInfo.content_fee) {
            dispatch({
              type: PURCHASE_URI_COMPLETED,
              data: { uri, purchaseReceipt: streamInfo.purchase_receipt || streamInfo.content_fee }
            });
          }
          dispatch({
            type: FETCH_FILE_INFO_COMPLETED,
            data: {
              fileInfo: streamInfo,
              outpoint: outpoint
            }
          });
  
          if (onSuccess) {
            onSuccess(streamInfo);
          }
        }
      }).catch(error => {
        dispatch({
          type: PURCHASE_URI_FAILED,
          data: { uri, error }
        });
  
        dispatch({
          type: FETCH_FILE_INFO_FAILED,
          data: { outpoint }
        });
  
        dispatch(doToast({
          message: `Failed to view ${uri}, please try again. If this problem persists, visit https://lbry.com/faq/support for support.`,
          isError: true
        }));
      });
    };
  }
  
  function doPurchaseUri(uri, costInfo, saveFile = true, onSuccess) {
    return (dispatch, getState) => {
      dispatch({
        type: PURCHASE_URI_STARTED,
        data: { uri }
      });
  
      const state = getState();
      const balance = selectBalance(state);
      const fileInfo = makeSelectFileInfoForUri(uri)(state);
      const downloadingByOutpoint = selectDownloadingByOutpoint(state);
      const alreadyDownloading = fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];
      const alreadyStreaming = makeSelectStreamingUrlForUri(uri)(state);
  
      if (!saveFile && (alreadyDownloading || alreadyStreaming)) {
        dispatch({
          type: PURCHASE_URI_FAILED,
          data: { uri, error: `Already fetching uri: ${uri}` }
        });
  
        if (onSuccess) {
          onSuccess(fileInfo);
        }
  
        return;
      }
  
      const { cost } = costInfo;
      if (parseFloat(cost) > balance) {
        dispatch({
          type: PURCHASE_URI_FAILED,
          data: { uri, error: 'Insufficient credits' }
        });
        return;
      }
  
      dispatch(doFileGet(uri, saveFile, onSuccess));
    };
  }
  
  function doClearPurchasedUriSuccess() {
    return {
      type: CLEAR_PURCHASED_URI_SUCCESS
    };
  }
  
  function doFetchFileInfo(uri) {
    return (dispatch, getState) => {
      const state = getState();
      const claim = selectClaimsByUri(state)[uri];
      const outpoint = claim ? `${claim.txid}:${claim.nout}` : null;
      const alreadyFetching = !!selectUrisLoading(state)[uri];
  
      if (!alreadyFetching) {
        dispatch({
          type: FETCH_FILE_INFO_STARTED,
          data: {
            outpoint
          }
        });
  
        lbryProxy.file_list({ outpoint, full_status: true, page: 1, page_size: 1 }).then(result => {
          const { items: fileInfos } = result;
          const fileInfo = fileInfos[0];
  
          dispatch({
            type: FETCH_FILE_INFO_COMPLETED,
            data: {
              outpoint,
              fileInfo: fileInfo || null
            }
          });
        });
      }
    };
  }
  
  function doFileList(page = 1, pageSize = 99999) {
    return (dispatch, getState) => {
      const state = getState();
      const isFetching = selectIsFetchingFileList(state);
  
      if (!isFetching) {
        dispatch({
          type: FILE_LIST_STARTED
        });
  
        lbryProxy.file_list({ page, page_size: pageSize }).then(result => {
          const { items: fileInfos } = result;
          dispatch({
            type: FILE_LIST_SUCCEEDED,
            data: {
              fileInfos: fileInfos
            }
          });
        });
      }
    };
  }
  
  function doFetchFileInfos() {
    return (dispatch, getState) => {
      const state = getState();
      const isFetchingFileInfo = selectIsFetchingFileList(state);
      if (!isFetchingFileInfo) dispatch(doFileList());
    };
  }
  
  function doSetFileListSort(page, value) {
    return {
      type: SET_FILE_LIST_SORT,
      data: { page, value }
    };
  }
  
  var _extends$6 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  function _objectWithoutProperties$2(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
  
  const selectState$4 = state => state.publish || {};
  
  // Is the current uri the same as the uri they clicked "edit" on
  const selectIsStillEditing = reselect.createSelector(selectState$4, publishState => {
    const { editingURI, uri } = publishState;
  
    if (!editingURI || !uri) {
      return false;
    }
  
    const {
      isChannel: currentIsChannel,
      streamName: currentClaimName,
      channelName: currentContentName
    } = parseURI(uri);
    const {
      isChannel: editIsChannel,
      streamName: editClaimName,
      channelName: editContentName
    } = parseURI(editingURI);
  
    // Depending on the previous/current use of a channel, we need to compare different things
    // ex: going from a channel to anonymous, the new uri won't return contentName, so we need to use claimName
    const currentName = currentIsChannel ? currentContentName : currentClaimName;
    const editName = editIsChannel ? editContentName : editClaimName;
    return currentName === editName;
  });
  
  const selectPublishFormValues = reselect.createSelector(selectState$4, state => state.settings, selectIsStillEditing, (publishState, settingsState, isStillEditing) => {
    const { languages } = publishState,
          formValues = _objectWithoutProperties$2(publishState, ['languages']);
    const language = languages && languages.length && languages[0];
    const { clientSettings } = settingsState;
    const { language: languageSet } = clientSettings;
  
    let actualLanguage;
    // Sets default if editing a claim with a set language
    if (!language && isStillEditing && languageSet) {
      actualLanguage = languageSet;
    } else {
      actualLanguage = language || languageSet || 'en';
    }
  
    return _extends$6({}, formValues, { language: actualLanguage });
  });
  const makeSelectPublishFormValue = item => reselect.createSelector(selectState$4, state => state[item]);
  
  const selectMyClaimForUri = reselect.createSelector(selectPublishFormValues, selectIsStillEditing, selectClaimsById, selectMyClaimsWithoutChannels, ({ editingURI, uri }, isStillEditing, claimsById, myClaims) => {
    const { channelName: contentName, streamName: claimName } = parseURI(uri);
    const { streamClaimId: editClaimId } = parseURI(editingURI);
  
    // If isStillEditing
    // They clicked "edit" from the file page
    // They haven't changed the channel/name after clicking edit
    // Get the claim so they can edit without re-uploading a new file
    return isStillEditing ? claimsById[editClaimId] : myClaims.find(claim => !contentName ? claim.name === claimName : claim.name === contentName || claim.name === claimName);
  });
  
  const selectIsResolvingPublishUris = reselect.createSelector(selectState$4, selectResolvingUris, ({ uri, name }, resolvingUris) => {
    if (uri) {
      const isResolvingUri = resolvingUris.includes(uri);
      const { isChannel } = parseURI(uri);
  
      let isResolvingShortUri;
      if (isChannel && name) {
        const shortUri = buildURI({ streamName: name });
        isResolvingShortUri = resolvingUris.includes(shortUri);
      }
  
      return isResolvingUri || isResolvingShortUri;
    }
  
    return false;
  });
  
  const selectTakeOverAmount = reselect.createSelector(selectState$4, selectMyClaimForUri, selectClaimsByUri, ({ name }, myClaimForUri, claimsByUri) => {
    if (!name) {
      return null;
    }
  
    // We only care about the winning claim for the short uri
    const shortUri = buildURI({ streamName: name });
    const claimForShortUri = claimsByUri[shortUri];
  
    if (!myClaimForUri && claimForShortUri) {
      return claimForShortUri.meta.effective_amount;
    } else if (myClaimForUri && claimForShortUri) {
      // https://github.com/lbryio/lbry/issues/1476
      // We should check the current effective_amount on my claim to see how much additional lbc
      // is needed to win the claim. Currently this is not possible during a takeover.
      // With this, we could say something like, "You have x lbc in support, if you bid y additional LBC you will control the claim"
      // For now just ignore supports. We will just show the winning claim's bid amount
      return claimForShortUri.meta.effective_amount || claimForShortUri.amount;
    }
  
    return null;
  });
  
  var _extends$7 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  function _asyncToGenerator$3(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
  
  const doResetThumbnailStatus = () => dispatch => {
    dispatch({
      type: UPDATE_PUBLISH_FORM,
      data: {
        thumbnailPath: '',
        thumbnailError: undefined
      }
    });
  
    return fetch(SPEECH_STATUS).then(res => res.json()).then(status => {
      if (status.disabled) {
        throw Error();
      }
  
      return dispatch({
        type: UPDATE_PUBLISH_FORM,
        data: {
          uploadThumbnailStatus: READY,
          thumbnail: ''
        }
      });
    }).catch(() => dispatch({
      type: UPDATE_PUBLISH_FORM,
      data: {
        uploadThumbnailStatus: API_DOWN,
        thumbnail: ''
      }
    }));
  };
  
  const doClearPublish = () => dispatch => {
    dispatch({ type: CLEAR_PUBLISH });
    return dispatch(doResetThumbnailStatus());
  };
  
  const doUpdatePublishForm = publishFormValue => dispatch => dispatch({
    type: UPDATE_PUBLISH_FORM,
    data: _extends$7({}, publishFormValue)
  });
  
  const doUploadThumbnail = (filePath, thumbnailBlob, fsAdapter, fs, path) => dispatch => {
    const downMessage = __('Thumbnail upload service may be down, try again later.');
    let thumbnail, fileExt, fileName, fileType;
  
    const makeid = () => {
      let text = '';
      const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
      for (let i = 0; i < 24; i += 1) text += possible.charAt(Math.floor(Math.random() * 62));
      return text;
    };
  
    const uploadError = (error = '') => {
      dispatch(batchActions({
        type: UPDATE_PUBLISH_FORM,
        data: {
          uploadThumbnailStatus: READY,
          thumbnail: '',
          nsfw: false
        }
      }, doError(error)));
    };
  
    dispatch({
      type: UPDATE_PUBLISH_FORM,
      data: {
        thumbnailError: undefined
      }
    });
  
    const doUpload = data => {
      return fetch(SPEECH_PUBLISH, {
        method: 'POST',
        body: data
      }).then(res => res.text()).then(text => text.length ? JSON.parse(text) : {}).then(json => {
        return json.success ? dispatch({
          type: UPDATE_PUBLISH_FORM,
          data: {
            uploadThumbnailStatus: COMPLETE,
            thumbnail: json.data.serveUrl
          }
        }) : uploadError(json.message || downMessage);
      }).catch(err => {
        let message = err.message;
  
        // This sucks but ¯\_(ツ)_/¯
        if (message === 'Failed to fetch') {
          message = downMessage;
        }
  
        uploadError(message);
      });
    };
  
    dispatch({
      type: UPDATE_PUBLISH_FORM,
      data: { uploadThumbnailStatus: IN_PROGRESS }
    });
  
    if (fsAdapter && fsAdapter.readFile && filePath) {
      fsAdapter.readFile(filePath, 'base64').then(base64Image => {
        fileExt = 'png';
        fileName = 'thumbnail.png';
        fileType = 'image/png';
  
        const data = new FormData();
        const name = makeid();
        data.append('name', name);
        // $FlowFixMe
        data.append('file', { uri: 'file://' + filePath, type: fileType, name: fileName });
        return doUpload(data);
      });
    } else {
      if (filePath && fs && path) {
        thumbnail = fs.readFileSync(filePath);
        fileExt = path.extname(filePath);
        fileName = path.basename(filePath);
        fileType = `image/${fileExt.slice(1)}`;
      } else if (thumbnailBlob) {
        fileExt = `.${thumbnailBlob.type && thumbnailBlob.type.split('/')[1]}`;
        fileName = thumbnailBlob.name;
        fileType = thumbnailBlob.type;
      } else {
        return null;
      }
  
      const data = new FormData();
      const name = makeid();
      const file = thumbnailBlob || thumbnail && new File([thumbnail], fileName, { type: fileType });
      data.append('name', name);
      // $FlowFixMe
      data.append('file', file);
      return doUpload(data);
    }
  };
  
  const doPrepareEdit = (claim, uri, fileInfo, fs) => dispatch => {
    const { name, amount, value = {} } = claim;
    const channelName = claim && claim.signing_channel && claim.signing_channel.name || null;
    const {
      author,
      description,
      // use same values as default state
      // fee will be undefined for free content
      fee = {
        amount: '0',
        currency: 'LBC'
      },
      languages,
      release_time,
      license,
      license_url: licenseUrl,
      thumbnail,
      title,
      tags
    } = value;
  
    const publishData = {
      name,
      bid: amount,
      contentIsFree: fee.amount === '0',
      author,
      description,
      fee,
      languages,
      releaseTime: release_time,
      releaseTimeEdited: undefined,
      thumbnail: thumbnail ? thumbnail.url : null,
      title,
      uri,
      uploadThumbnailStatus: thumbnail ? MANUAL : undefined,
      licenseUrl,
      nsfw: isClaimNsfw(claim),
      tags: tags ? tags.map(tag => ({ name: tag })) : []
    };
  
    // Make sure custom licenses are mapped properly
    // If the license isn't one of the standard licenses, map the custom license and description/url
    if (!CC_LICENSES.some(({ value }) => value === license)) {
      if (!license || license === NONE || license === PUBLIC_DOMAIN) {
        publishData.licenseType = license;
      } else if (license && !licenseUrl && license !== NONE) {
        publishData.licenseType = COPYRIGHT;
      } else {
        publishData.licenseType = OTHER;
      }
  
      publishData.otherLicenseDescription = license;
    } else {
      publishData.licenseType = license;
    }
    if (channelName) {
      publishData['channel'] = channelName;
    }
  
    dispatch({ type: DO_PREPARE_EDIT, data: publishData });
  };
  
  const doPublish = (success, fail, preview) => (dispatch, getState) => {
    if (!preview) {
      dispatch({ type: PUBLISH_START });
    }
  
    const state = getState();
    const myClaimForUri = selectMyClaimForUri(state);
    const myChannels = selectMyChannelClaims(state);
    const myClaims = selectMyClaimsWithoutChannels(state);
    // get redux publish form
    const publishData = selectPublishFormValues(state);
  
    // destructure the data values
    const {
      name,
      bid,
      filePath,
      description,
      language,
      releaseTimeEdited,
      license,
      licenseUrl,
      useLBRYUploader,
      licenseType,
      otherLicenseDescription,
      thumbnail,
      channel,
      title,
      contentIsFree,
      fee,
      uri,
      tags,
      locations,
      optimize,
      isLivestreamPublish,
      remoteFileUrl
    } = publishData;
  
    // Handle scenario where we have a claim that has the same name as a channel we are publishing with.
    const myClaimForUriEditing = myClaimForUri && myClaimForUri.name === name ? myClaimForUri : null;
  
    let publishingLicense;
    switch (licenseType) {
      case COPYRIGHT:
      case OTHER:
        publishingLicense = otherLicenseDescription;
        break;
      default:
        publishingLicense = licenseType;
    }
  
    // get the claim id from the channel name, we will use that instead
    const namedChannelClaim = myChannels ? myChannels.find(myChannel => myChannel.name === channel) : null;
    const channelId = namedChannelClaim ? namedChannelClaim.claim_id : '';
  
    const publishPayload = {
      name,
      title,
      description,
      locations: [],
      bid: creditsToString(bid),
      languages: [language],
      tags: tags && tags.map(tag => tag.name),
      thumbnail_url: thumbnail,
      blocking: true,
      preview: false
    };
    // Temporary solution to keep the same publish flow with the new tags api
    // Eventually we will allow users to enter their own tags on publish
    // `nsfw` will probably be removed
    if (remoteFileUrl) {
      publishPayload.remote_url = remoteFileUrl;
    }
  
    if (publishingLicense) {
      publishPayload.license = publishingLicense;
    }
  
    if (licenseUrl) {
      publishPayload.license_url = licenseUrl;
    }
  
    if (thumbnail) {
      publishPayload.thumbnail_url = thumbnail;
    }
  
    if (useLBRYUploader) {
      publishPayload.tags.push('lbry-first');
    }
  
    // Set release time to curret date. On edits, keep original release/transaction time as release_time
    if (releaseTimeEdited) {
      publishPayload.release_time = releaseTimeEdited;
    } else if (myClaimForUriEditing && myClaimForUriEditing.value.release_time) {
      publishPayload.release_time = Number(myClaimForUri.value.release_time);
    } else if (myClaimForUriEditing && myClaimForUriEditing.timestamp) {
      publishPayload.release_time = Number(myClaimForUriEditing.timestamp);
    } else {
      publishPayload.release_time = Number(Math.round(Date.now() / 1000));
    }
  
    if (channelId) {
      publishPayload.channel_id = channelId;
    }
  
    if (myClaimForUriEditing && myClaimForUriEditing.value && myClaimForUriEditing.value.locations) {
      publishPayload.locations = myClaimForUriEditing.value.locations;
    }
  
    if (!contentIsFree && fee && fee.currency && Number(fee.amount) > 0) {
      publishPayload.fee_currency = fee.currency;
      publishPayload.fee_amount = creditsToString(fee.amount);
    }
  
    if (optimize) {
      publishPayload.optimize_file = true;
    }
  
    // Only pass file on new uploads, not metadata only edits.
    // The sdk will figure it out
    if (filePath && !isLivestreamPublish) publishPayload.file_path = filePath;
  
    if (preview) {
      publishPayload.preview = true;
      publishPayload.optimize_file = false;
  
      return lbryProxy.publish(publishPayload).then(previewResponse => {
        return preview(previewResponse);
      }, fail);
    }
  
    return lbryProxy.publish(publishPayload).then(response => {
      if (!useLBRYUploader) {
        return success(response);
      }
  
      // $FlowFixMe
      publishPayload.permanent_url = response.outputs[0].permanent_url;
  
      return lbryFirstProxy.upload(publishPayload).then(() => {
        // Return original publish response so app treats it like a normal publish
        return success(response);
      }).catch(error => {
        return success(response, error);
      });
    }, fail);
  };
  
  // Calls file_list until any reflecting files are done
  const doCheckReflectingFiles = () => (dispatch, getState) => {
    const state = getState();
    const { checkingReflector } = state.claims;
    let reflectorCheckInterval;
  
    const checkFileList = (() => {
      var _ref = _asyncToGenerator$3(function* () {
        const state = getState();
        const reflectingById = selectReflectingById(state);
        const ids = Object.keys(reflectingById);
  
        const newReflectingById = {};
        const promises = [];
        // TODO: just use file_list({claim_id: Array<claimId>})
        if (Object.keys(reflectingById).length) {
          ids.forEach(function (claimId) {
            promises.push(lbryProxy.file_list({ claim_id: claimId }));
          });
  
          Promise.all(promises).then(function (results) {
            results.forEach(function (res) {
              if (res.items[0]) {
                const fileListItem = res.items[0];
                const fileClaimId = fileListItem.claim_id;
                const {
                  is_fully_reflected: done,
                  uploading_to_reflector: uploading,
                  reflector_progress: progress
                } = fileListItem;
                if (uploading) {
                  newReflectingById[fileClaimId] = {
                    fileListItem: fileListItem,
                    progress,
                    stalled: !done && !uploading
                  };
                }
              }
            });
          }).then(function () {
            dispatch({
              type: UPDATE_FILES_REFLECTING,
              data: newReflectingById
            });
            if (!Object.keys(newReflectingById).length) {
              dispatch({
                type: TOGGLE_CHECKING_REFLECTING,
                data: false
              });
              clearInterval(reflectorCheckInterval);
            }
          });
        } else {
          dispatch({
            type: TOGGLE_CHECKING_REFLECTING,
            data: false
          });
          clearInterval(reflectorCheckInterval);
        }
      });
  
      return function checkFileList() {
        return _ref.apply(this, arguments);
      };
    })();
    // do it once...
    checkFileList();
    // then start the interval if it's not already started
    if (!checkingReflector) {
      dispatch({
        type: TOGGLE_CHECKING_REFLECTING,
        data: true
      });
      reflectorCheckInterval = setInterval(() => {
        checkFileList();
      }, 5000);
    }
  };
  
  //      
  
  function savePosition(claimId, outpoint, position) {
    return dispatch => {
      dispatch({
        type: SET_CONTENT_POSITION,
        data: { claimId, outpoint, position }
      });
    };
  }
  
  //      
  
  function parseQueryParams(queryString) {
    if (queryString === '') return {};
    const parts = queryString.split('?').pop().split('&').map(p => p.split('='));
  
    const params = {};
    parts.forEach(array => {
      const [first, second] = array;
      params[first] = second;
    });
    return params;
  }
  
  function toQueryString(params) {
    if (!params) return '';
  
    const parts = [];
    Object.keys(params).forEach(key => {
      if (Object.prototype.hasOwnProperty.call(params, key) && params[key]) {
        parts.push(`${key}=${params[key]}`);
      }
    });
  
    return parts.join('&');
  }
  
  var _extends$8 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  /*
  new claim = { ...maybeResolvedClaim, ...pendingClaim, meta: maybeResolvedClaim['meta'] }
   */
  
  function mergeClaims(maybeResolved, pending) {
    return _extends$8({}, maybeResolved, pending, { meta: maybeResolved.meta });
  }
  
  var _extends$9 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  function _objectWithoutProperties$3(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
  
  const reducers = {};
  const defaultState = {
    byId: {},
    claimsByUri: {},
    paginatedClaimsByChannel: {},
    channelClaimCounts: {},
    fetchingChannelClaims: {},
    resolvingUris: [],
    myChannelClaims: undefined,
    myCollectionClaims: [],
    myClaims: undefined,
    myPurchases: undefined,
    myPurchasesPageNumber: undefined,
    myPurchasesPageTotalResults: undefined,
    purchaseUriSuccess: false,
    fetchingMyPurchases: false,
    fetchingMyPurchasesError: undefined,
    fetchingMyChannels: false,
    fetchingMyCollections: false,
    abandoningById: {},
    pendingIds: [],
    reflectingById: {},
    claimSearchError: false,
    claimSearchByQuery: {},
    claimSearchByQueryLastPageReached: {},
    fetchingClaimSearchByQuery: {},
    updateChannelError: '',
    updateCollectionError: '',
    updatingChannel: false,
    creatingChannel: false,
    createChannelError: undefined,
    updatingCollection: false,
    creatingCollection: false,
    createCollectionError: undefined,
    pendingChannelImport: false,
    repostLoading: false,
    repostError: undefined,
    fetchingClaimListMinePageError: undefined,
    myClaimsPageResults: [],
    myClaimsPageNumber: undefined,
    myClaimsPageTotalResults: undefined,
    isFetchingClaimListMine: false,
    isFetchingMyPurchases: false,
    isCheckingNameForPublish: false,
    checkingPending: false,
    checkingReflecting: false
  };
  
  function handleClaimAction(state, action) {
    const { resolveInfo } = action.data;
  
    const byUri = Object.assign({}, state.claimsByUri);
    const byId = Object.assign({}, state.byId);
    const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
    const pendingIds = state.pendingIds;
    let newResolvingUrls = new Set(state.resolvingUris);
    let myClaimIds = new Set(state.myClaims);
  
    Object.entries(resolveInfo).forEach(([url, resolveResponse]) => {
      // $FlowFixMe
      const { claimsInChannel, stream, channel: channelFromResolve, collection } = resolveResponse;
      const channel = channelFromResolve || stream && stream.signing_channel;
  
      if (stream) {
        if (pendingIds.includes(stream.claim_id)) {
          byId[stream.claim_id] = mergeClaims(stream, byId[stream.claim_id]);
        } else {
          byId[stream.claim_id] = stream;
        }
        byUri[url] = stream.claim_id;
  
        // If url isn't a canonical_url, make sure that is added too
        byUri[stream.canonical_url] = stream.claim_id;
  
        // Also add the permanent_url here until lighthouse returns canonical_url for search results
        byUri[stream.permanent_url] = stream.claim_id;
        newResolvingUrls.delete(stream.canonical_url);
        newResolvingUrls.delete(stream.permanent_url);
  
        if (stream.is_my_output) {
          myClaimIds.add(stream.claim_id);
        }
      }
  
      if (channel && channel.claim_id) {
        if (!stream) {
          byUri[url] = channel.claim_id;
        }
  
        if (claimsInChannel) {
          channelClaimCounts[url] = claimsInChannel;
          channelClaimCounts[channel.canonical_url] = claimsInChannel;
        }
  
        if (pendingIds.includes(channel.claim_id)) {
          byId[channel.claim_id] = mergeClaims(channel, byId[channel.claim_id]);
        } else {
          byId[channel.claim_id] = channel;
        }
  
        byUri[channel.permanent_url] = channel.claim_id;
        byUri[channel.canonical_url] = channel.claim_id;
        newResolvingUrls.delete(channel.canonical_url);
        newResolvingUrls.delete(channel.permanent_url);
      }
  
      if (collection) {
        if (pendingIds.includes(collection.claim_id)) {
          byId[collection.claim_id] = mergeClaims(collection, byId[collection.claim_id]);
        } else {
          byId[collection.claim_id] = collection;
        }
        byUri[url] = collection.claim_id;
        byUri[collection.canonical_url] = collection.claim_id;
        byUri[collection.permanent_url] = collection.claim_id;
        newResolvingUrls.delete(collection.canonical_url);
        newResolvingUrls.delete(collection.permanent_url);
  
        if (collection.is_my_output) {
          myClaimIds.add(collection.claim_id);
        }
      }
  
      newResolvingUrls.delete(url);
      if (!stream && !channel && !collection && !pendingIds.includes(byUri[url])) {
        byUri[url] = null;
      }
    });
  
    return Object.assign({}, state, {
      byId,
      claimsByUri: byUri,
      channelClaimCounts,
      resolvingUris: Array.from(newResolvingUrls),
      myClaims: Array.from(myClaimIds)
    });
  }
  
  reducers[RESOLVE_URIS_STARTED] = (state, action) => {
    const { uris } = action.data;
  
    const oldResolving = state.resolvingUris || [];
    const newResolving = oldResolving.slice();
  
    uris.forEach(uri => {
      if (!newResolving.includes(uri)) {
        newResolving.push(uri);
      }
    });
  
    return Object.assign({}, state, {
      resolvingUris: newResolving
    });
  };
  
  reducers[RESOLVE_URIS_COMPLETED] = (state, action) => {
    return _extends$9({}, handleClaimAction(state, action));
  };
  
  reducers[FETCH_CLAIM_LIST_MINE_STARTED] = state => Object.assign({}, state, {
    isFetchingClaimListMine: true
  });
  
  reducers[FETCH_CLAIM_LIST_MINE_COMPLETED] = (state, action) => {
    const { result, resolve } = action.data;
    const claims = result.items;
    const page = result.page;
    const totalItems = result.total_items;
  
    const byId = Object.assign({}, state.byId);
    const byUri = Object.assign({}, state.claimsByUri);
    const pendingIds = state.pendingIds || [];
    let myClaimIds = new Set(state.myClaims);
    let urlsForCurrentPage = [];
  
    const pendingIdSet = new Set(pendingIds);
  
    claims.forEach(claim => {
      const { permanent_url: permanentUri, claim_id: claimId } = claim;
      if (claim.type && claim.type.match(/claim|update/)) {
        urlsForCurrentPage.push(permanentUri);
        if (claim.confirmations < 1) {
          pendingIdSet.add(claimId);
        } else if (!resolve && pendingIdSet.has(claimId) && claim.confirmations > 0) {
          pendingIdSet.delete(claimId);
        }
        if (pendingIds.includes(claimId)) {
          byId[claimId] = mergeClaims(claim, byId[claimId]);
        } else {
          byId[claimId] = claim;
        }
        byUri[permanentUri] = claimId;
        myClaimIds.add(claimId);
      }
    });
  
    return Object.assign({}, state, {
      isFetchingClaimListMine: false,
      myClaims: Array.from(myClaimIds),
      byId,
      pendingIds: Array.from(pendingIdSet),
      claimsByUri: byUri,
      myClaimsPageResults: urlsForCurrentPage,
      myClaimsPageNumber: page,
      myClaimsPageTotalResults: totalItems
    });
  };
  
  reducers[FETCH_CHANNEL_LIST_STARTED] = state => Object.assign({}, state, { fetchingMyChannels: true });
  
  reducers[FETCH_CHANNEL_LIST_COMPLETED] = (state, action) => {
    const { claims } = action.data;
    const myClaims = state.myClaims || [];
    let myClaimIds = new Set(state.myClaims);
    const pendingIds = state.pendingIds || [];
    let myChannelClaims;
    const byId = Object.assign({}, state.byId);
    const byUri = Object.assign({}, state.claimsByUri);
    const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
  
    if (!claims.length) {
      // $FlowFixMe
      myChannelClaims = null;
    } else {
      myChannelClaims = new Set(state.myChannelClaims);
      claims.forEach(claim => {
        const { claims_in_channel: claimsInChannel } = claim.meta;
        const { canonical_url: canonicalUrl, permanent_url: permanentUrl, claim_id: claimId } = claim;
  
        byUri[canonicalUrl] = claimId;
        byUri[permanentUrl] = claimId;
        channelClaimCounts[canonicalUrl] = claimsInChannel;
        channelClaimCounts[permanentUrl] = claimsInChannel;
  
        // $FlowFixMe
        myChannelClaims.add(claimId);
        if (!pendingIds.some(c => c === claimId)) {
          byId[claimId] = claim;
        }
        myClaimIds.add(claimId);
      });
    }
  
    return Object.assign({}, state, {
      byId,
      claimsByUri: byUri,
      channelClaimCounts,
      fetchingMyChannels: false,
      myChannelClaims: myChannelClaims ? Array.from(myChannelClaims) : null,
      myClaims: myClaimIds ? Array.from(myClaimIds) : null
    });
  };
  
  reducers[FETCH_CHANNEL_LIST_FAILED] = (state, action) => {
    return Object.assign({}, state, {
      fetchingMyChannels: false
    });
  };
  
  reducers[FETCH_COLLECTION_LIST_STARTED] = state => _extends$9({}, state, {
    fetchingMyCollections: true
  });
  
  reducers[FETCH_COLLECTION_LIST_COMPLETED] = (state, action) => {
    const { claims } = action.data;
    const myClaims = state.myClaims || [];
    let myClaimIds = new Set(myClaims);
    const pendingIds = state.pendingIds || [];
    let myCollectionClaimsSet = new Set([]);
    const byId = Object.assign({}, state.byId);
    const byUri = Object.assign({}, state.claimsByUri);
  
    if (claims.length) {
      myCollectionClaimsSet = new Set(state.myCollectionClaims);
      claims.forEach(claim => {
        const { canonical_url: canonicalUrl, permanent_url: permanentUrl, claim_id: claimId } = claim;
  
        byUri[canonicalUrl] = claimId;
        byUri[permanentUrl] = claimId;
  
        // $FlowFixMe
        myCollectionClaimsSet.add(claimId);
        // we don't want to overwrite a pending result with a resolve
        if (!pendingIds.some(c => c === claimId)) {
          byId[claimId] = claim;
        }
        myClaimIds.add(claimId);
      });
    }
  
    return _extends$9({}, state, {
      byId,
      claimsByUri: byUri,
      fetchingMyCollections: false,
      myCollectionClaims: Array.from(myCollectionClaimsSet),
      myClaims: myClaimIds ? Array.from(myClaimIds) : null
    });
  };
  
  reducers[FETCH_COLLECTION_LIST_FAILED] = state => {
    return _extends$9({}, state, { fetchingMyCollections: false });
  };
  
  reducers[FETCH_CHANNEL_CLAIMS_STARTED] = (state, action) => {
    const { uri, page } = action.data;
    const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims);
  
    fetchingChannelClaims[uri] = page;
  
    return Object.assign({}, state, {
      fetchingChannelClaims,
      currentChannelPage: page
    });
  };
  
  reducers[FETCH_CHANNEL_CLAIMS_COMPLETED] = (state, action) => {
    const {
      uri,
      claims,
      claimsInChannel,
      page,
      totalPages
    } = action.data;
  
    // byChannel keeps claim_search relevant results by page. If the total changes, erase it.
    const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
  
    const paginatedClaimsByChannel = Object.assign({}, state.paginatedClaimsByChannel);
    // check if count has changed - that means cached pagination will be wrong, so clear it
    const previousCount = paginatedClaimsByChannel[uri] && paginatedClaimsByChannel[uri]['itemCount'];
    const byChannel = claimsInChannel === previousCount ? Object.assign({}, paginatedClaimsByChannel[uri]) : {};
    const allClaimIds = new Set(byChannel.all);
    const currentPageClaimIds = [];
    const byId = Object.assign({}, state.byId);
    const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims);
    const claimsByUri = Object.assign({}, state.claimsByUri);
  
    if (claims !== undefined) {
      claims.forEach(claim => {
        allClaimIds.add(claim.claim_id);
        currentPageClaimIds.push(claim.claim_id);
        byId[claim.claim_id] = claim;
        claimsByUri[claim.canonical_url] = claim.claim_id;
      });
    }
  
    byChannel.all = allClaimIds;
    byChannel.pageCount = totalPages;
    byChannel.itemCount = claimsInChannel;
    byChannel[page] = currentPageClaimIds;
    paginatedClaimsByChannel[uri] = byChannel;
    delete fetchingChannelClaims[uri];
  
    return Object.assign({}, state, {
      paginatedClaimsByChannel,
      byId,
      fetchingChannelClaims,
      claimsByUri,
      channelClaimCounts,
      currentChannelPage: page
    });
  };
  
  reducers[ABANDON_CLAIM_STARTED] = (state, action) => {
    const { claimId } = action.data;
    const abandoningById = Object.assign({}, state.abandoningById);
  
    abandoningById[claimId] = true;
  
    return Object.assign({}, state, {
      abandoningById
    });
  };
  
  reducers[UPDATE_PENDING_CLAIMS] = (state, action) => {
    const { claims: pendingClaims } = action.data;
    const byId = Object.assign({}, state.byId);
    const byUri = Object.assign({}, state.claimsByUri);
    const pendingIds = state.pendingIds;
    const pendingIdSet = new Set(pendingIds);
    let myClaimIds = new Set(state.myClaims);
    const myChannelClaims = new Set(state.myChannelClaims);
  
    // $FlowFixMe
    pendingClaims.forEach(claim => {
      let newClaim;
      const { permanent_url: uri, claim_id: claimId, type, value_type: valueType } = claim;
      pendingIdSet.add(claimId);
      const oldClaim = byId[claimId];
      if (oldClaim && oldClaim.canonical_url) {
        newClaim = mergeClaims(oldClaim, claim);
      } else {
        newClaim = claim;
      }
      if (valueType === 'channel') {
        myChannelClaims.add(claimId);
      }
  
      if (type && type.match(/claim|update/)) {
        byId[claimId] = newClaim;
        byUri[uri] = claimId;
      }
      myClaimIds.add(claimId);
    });
    return Object.assign({}, state, {
      myClaims: Array.from(myClaimIds),
      byId,
      myChannelClaims: Array.from(myChannelClaims),
      claimsByUri: byUri,
      pendingIds: Array.from(pendingIdSet)
    });
  };
  
  reducers[UPDATE_CONFIRMED_CLAIMS] = (state, action) => {
    const { claims: confirmedClaims } = action.data;
    const byId = Object.assign({}, state.byId);
    const byUri = Object.assign({}, state.claimsByUri);
    const pendingIds = state.pendingIds;
    const pendingIdSet = new Set(pendingIds);
  
    confirmedClaims.forEach(claim => {
      const { permanent_url: permanentUri, claim_id: claimId, type } = claim;
      let newClaim = claim;
      const oldClaim = byId[claimId];
      if (oldClaim && oldClaim.canonical_url) {
        newClaim = mergeClaims(oldClaim, claim);
      }
      if (type && type.match(/claim|update|channel/)) {
        byId[claimId] = newClaim;
        pendingIdSet.delete(claimId);
      }
    });
    return Object.assign({}, state, {
      pendingIds: Array.from(pendingIdSet),
      byId,
      claimsByUri: byUri
    });
  };
  
  reducers[ABANDON_CLAIM_SUCCEEDED] = (state, action) => {
    const { claimId } = action.data;
    const byId = Object.assign({}, state.byId);
    const newMyClaims = state.myClaims ? state.myClaims.slice() : [];
    const newMyChannelClaims = state.myChannelClaims ? state.myChannelClaims.slice() : [];
    const claimsByUri = Object.assign({}, state.claimsByUri);
    const newMyCollectionClaims = state.myCollectionClaims ? state.myCollectionClaims.slice() : [];
  
    Object.keys(claimsByUri).forEach(uri => {
      if (claimsByUri[uri] === claimId) {
        delete claimsByUri[uri];
      }
    });
    const myClaims = newMyClaims.filter(i => i !== claimId);
    const myChannelClaims = newMyChannelClaims.filter(i => i !== claimId);
    const myCollectionClaims = newMyCollectionClaims.filter(i => i !== claimId);
  
    delete byId[claimId];
  
    return Object.assign({}, state, {
      myClaims,
      myChannelClaims,
      myCollectionClaims,
      byId,
      claimsByUri
    });
  };
  
  reducers[CLEAR_CHANNEL_ERRORS] = state => _extends$9({}, state, {
    createChannelError: null,
    updateChannelError: null
  });
  
  reducers[CREATE_CHANNEL_STARTED] = state => _extends$9({}, state, {
    creatingChannel: true,
    createChannelError: null
  });
  
  reducers[CREATE_CHANNEL_COMPLETED] = (state, action) => {
    return Object.assign({}, state, {
      creatingChannel: false
    });
  };
  
  reducers[CREATE_CHANNEL_FAILED] = (state, action) => {
    return Object.assign({}, state, {
      creatingChannel: false,
      createChannelError: action.data
    });
  };
  
  reducers[UPDATE_CHANNEL_STARTED] = (state, action) => {
    return Object.assign({}, state, {
      updateChannelError: '',
      updatingChannel: true
    });
  };
  
  reducers[UPDATE_CHANNEL_COMPLETED] = (state, action) => {
    return Object.assign({}, state, {
      updateChannelError: '',
      updatingChannel: false
    });
  };
  
  reducers[UPDATE_CHANNEL_FAILED] = (state, action) => {
    return Object.assign({}, state, {
      updateChannelError: action.data.message,
      updatingChannel: false
    });
  };
  
  reducers[CLEAR_COLLECTION_ERRORS] = state => _extends$9({}, state, {
    createCollectionError: null,
    updateCollectionError: null
  });
  
  reducers[COLLECTION_PUBLISH_STARTED] = state => _extends$9({}, state, {
    creatingCollection: true,
    createCollectionError: null
  });
  
  reducers[COLLECTION_PUBLISH_COMPLETED] = (state, action) => {
    const myCollections = state.myCollectionClaims || [];
    const myClaims = state.myClaims || [];
    const { claimId } = action.data;
    let myClaimIds = new Set(myClaims);
    let myCollectionClaimsSet = new Set(myCollections);
    myClaimIds.add(claimId);
    myCollectionClaimsSet.add(claimId);
    return Object.assign({}, state, {
      creatingCollection: false,
      myClaims: Array.from(myClaimIds),
      myCollectionClaims: Array.from(myCollectionClaimsSet)
    });
  };
  
  reducers[COLLECTION_PUBLISH_FAILED] = (state, action) => {
    return Object.assign({}, state, {
      creatingCollection: false,
      createCollectionError: action.data.error
    });
  };
  
  reducers[COLLECTION_PUBLISH_UPDATE_STARTED] = (state, action) => {
    return Object.assign({}, state, {
      updateCollectionError: '',
      updatingCollection: true
    });
  };
  
  reducers[COLLECTION_PUBLISH_UPDATE_COMPLETED] = (state, action) => {
    return Object.assign({}, state, {
      updateCollectionError: '',
      updatingCollection: false
    });
  };
  
  reducers[COLLECTION_PUBLISH_UPDATE_FAILED] = (state, action) => {
    return Object.assign({}, state, {
      updateCollectionError: action.data.error,
      updatingCollection: false
    });
  };
  
  reducers[IMPORT_CHANNEL_STARTED] = state => Object.assign({}, state, { pendingChannelImports: true });
  
  reducers[IMPORT_CHANNEL_COMPLETED] = state => Object.assign({}, state, { pendingChannelImports: false });
  
  reducers[CLAIM_SEARCH_STARTED] = (state, action) => {
    const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
    fetchingClaimSearchByQuery[action.data.query] = true;
  
    return Object.assign({}, state, {
      fetchingClaimSearchByQuery
    });
  };
  
  reducers[CLAIM_SEARCH_COMPLETED] = (state, action) => {
    const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
    const claimSearchByQuery = Object.assign({}, state.claimSearchByQuery);
    const claimSearchByQueryLastPageReached = Object.assign({}, state.claimSearchByQueryLastPageReached);
    const { append, query, urls, pageSize } = action.data;
  
    if (append) {
      // todo: check for duplicate urls when concatenating?
      claimSearchByQuery[query] = claimSearchByQuery[query] && claimSearchByQuery[query].length ? claimSearchByQuery[query].concat(urls) : urls;
    } else {
      claimSearchByQuery[query] = urls;
    }
  
    // the returned number of urls is less than the page size, so we're on the last page
    claimSearchByQueryLastPageReached[query] = urls.length < pageSize;
  
    delete fetchingClaimSearchByQuery[query];
  
    return Object.assign({}, state, _extends$9({}, handleClaimAction(state, action), {
      claimSearchByQuery,
      claimSearchByQueryLastPageReached,
      fetchingClaimSearchByQuery
    }));
  };
  
  reducers[CLAIM_SEARCH_FAILED] = (state, action) => {
    const { query } = action.data;
    const claimSearchByQuery = Object.assign({}, state.claimSearchByQuery);
    const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
    const claimSearchByQueryLastPageReached = Object.assign({}, state.claimSearchByQueryLastPageReached);
  
    delete fetchingClaimSearchByQuery[query];
  
    if (claimSearchByQuery[query] && claimSearchByQuery[query].length !== 0) {
      claimSearchByQueryLastPageReached[query] = true;
    } else {
      claimSearchByQuery[query] = null;
    }
  
    return Object.assign({}, state, {
      fetchingClaimSearchByQuery,
      claimSearchByQuery,
      claimSearchByQueryLastPageReached
    });
  };
  
  reducers[CLAIM_REPOST_STARTED] = state => {
    return _extends$9({}, state, {
      repostLoading: true,
      repostError: null
    });
  };
  reducers[CLAIM_REPOST_COMPLETED] = (state, action) => {
    const { originalClaimId, repostClaim } = action.data;
    const byId = _extends$9({}, state.byId);
    const claimsByUri = _extends$9({}, state.claimsByUri);
    const claimThatWasReposted = byId[originalClaimId];
  
    const repostStub = _extends$9({}, repostClaim, { reposted_claim: claimThatWasReposted });
    byId[repostStub.claim_id] = repostStub;
    claimsByUri[repostStub.permanent_url] = repostStub.claim_id;
  
    return _extends$9({}, state, {
      byId,
      claimsByUri,
      repostLoading: false,
      repostError: null
    });
  };
  reducers[CLAIM_REPOST_FAILED] = (state, action) => {
    const { error } = action.data;
  
    return _extends$9({}, state, {
      repostLoading: false,
      repostError: error
    });
  };
  reducers[CLEAR_REPOST_ERROR] = state => {
    return _extends$9({}, state, {
      repostError: null
    });
  };
  reducers[ADD_FILES_REFLECTING] = (state, action) => {
    const pendingClaim = action.data;
    const { reflectingById } = state;
    const claimId = pendingClaim && pendingClaim.claim_id;
  
    reflectingById[claimId] = { fileListItem: pendingClaim, progress: 0, stalled: false };
  
    return Object.assign({}, state, _extends$9({}, state, {
      reflectingById: reflectingById
    }));
  };
  reducers[UPDATE_FILES_REFLECTING] = (state, action) => {
    const newReflectingById = action.data;
  
    return Object.assign({}, state, _extends$9({}, state, {
      reflectingById: newReflectingById
    }));
  };
  reducers[TOGGLE_CHECKING_REFLECTING] = (state, action) => {
    const checkingReflecting = action.data;
  
    return Object.assign({}, state, _extends$9({}, state, {
      checkingReflecting
    }));
  };
  reducers[TOGGLE_CHECKING_PENDING] = (state, action) => {
    const checking = action.data;
  
    return Object.assign({}, state, _extends$9({}, state, {
      checkingPending: checking
    }));
  };
  
  reducers[PURCHASE_LIST_STARTED] = state => {
    return _extends$9({}, state, {
      fetchingMyPurchases: true,
      fetchingMyPurchasesError: null
    });
  };
  
  reducers[PURCHASE_LIST_COMPLETED] = (state, action) => {
    const { result } = action.data;
    const page = result.page;
    const totalItems = result.total_items;
  
    let byId = Object.assign({}, state.byId);
    let byUri = Object.assign({}, state.claimsByUri);
    let urlsForCurrentPage = [];
  
    result.items.forEach(item => {
      if (!item.claim) {
        // Abandoned claim
        return;
      }
  
      const { claim } = item,
            purchaseInfo = _objectWithoutProperties$3(item, ['claim']);
      claim.purchase_receipt = purchaseInfo;
      const claimId = claim.claim_id;
      const uri = claim.canonical_url;
  
      byId[claimId] = claim;
      byUri[uri] = claimId;
      urlsForCurrentPage.push(uri);
    });
  
    return Object.assign({}, state, {
      byId,
      claimsByUri: byUri,
      myPurchases: urlsForCurrentPage,
      myPurchasesPageNumber: page,
      myPurchasesPageTotalResults: totalItems,
      fetchingMyPurchases: false
    });
  };
  
  reducers[PURCHASE_LIST_FAILED] = (state, action) => {
    const { error } = action.data;
  
    return _extends$9({}, state, {
      fetchingMyPurchases: false,
      fetchingMyPurchasesError: error
    });
  };
  
  reducers[PURCHASE_URI_COMPLETED] = (state, action) => {
    const { uri, purchaseReceipt } = action.data;
  
    let byId = Object.assign({}, state.byId);
    let byUri = Object.assign({}, state.claimsByUri);
    let myPurchases = state.myPurchases ? state.myPurchases.slice() : [];
  
    const claimId = byUri[uri];
    if (claimId) {
      let claim = byId[claimId];
      claim.purchase_receipt = purchaseReceipt;
    }
  
    myPurchases.push(uri);
  
    return _extends$9({}, state, {
      byId,
      myPurchases,
      purchaseUriSuccess: true
    });
  };
  
  reducers[PURCHASE_URI_FAILED] = state => {
    return _extends$9({}, state, {
      purchaseUriSuccess: false
    });
  };
  
  reducers[CLEAR_PURCHASED_URI_SUCCESS] = state => {
    return _extends$9({}, state, {
      purchaseUriSuccess: false
    });
  };
  
  function claimsReducer(state = defaultState, action) {
    const handler = reducers[action.type];
    if (handler) return handler(state, action);
    return state;
  }
  
  var _extends$a = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  const reducers$1 = {};
  const defaultState$1 = {
    positions: {}
  };
  
  reducers$1[SET_CONTENT_POSITION] = (state, action) => {
    const { claimId, outpoint, position } = action.data;
    return _extends$a({}, state, {
      positions: _extends$a({}, state.positions, {
        [claimId]: _extends$a({}, state.positions[claimId], {
          [outpoint]: position
        })
      })
    });
  };
  
  function contentReducer(state = defaultState$1, action) {
    const handler = reducers$1[action.type];
    if (handler) return handler(state, action);
    return state;
  }
  
  const reducers$2 = {};
  const defaultState$2 = {
    fileListPublishedSort: DATE_NEW,
    fileListDownloadedSort: DATE_NEW
  };
  
  reducers$2[FILE_LIST_STARTED] = state => Object.assign({}, state, {
    isFetchingFileList: true
  });
  
  reducers$2[FILE_LIST_SUCCEEDED] = (state, action) => {
    const { fileInfos } = action.data;
    const newByOutpoint = Object.assign({}, state.byOutpoint);
    const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
  
    fileInfos.forEach(fileInfo => {
      const { outpoint } = fileInfo;
  
      if (outpoint) newByOutpoint[fileInfo.outpoint] = fileInfo;
    });
  
    return Object.assign({}, state, {
      isFetchingFileList: false,
      byOutpoint: newByOutpoint,
      pendingByOutpoint
    });
  };
  
  reducers$2[FETCH_FILE_INFO_STARTED] = (state, action) => {
    const { outpoint } = action.data;
    const newFetching = Object.assign({}, state.fetching);
  
    newFetching[outpoint] = true;
  
    return Object.assign({}, state, {
      fetching: newFetching
    });
  };
  
  reducers$2[FETCH_FILE_INFO_COMPLETED] = (state, action) => {
    const { fileInfo, outpoint } = action.data;
  
    const newByOutpoint = Object.assign({}, state.byOutpoint);
    const newFetching = Object.assign({}, state.fetching);
  
    newByOutpoint[outpoint] = fileInfo;
    delete newFetching[outpoint];
  
    return Object.assign({}, state, {
      byOutpoint: newByOutpoint,
      fetching: newFetching
    });
  };
  
  reducers$2[FETCH_FILE_INFO_FAILED] = (state, action) => {
    const { outpoint } = action.data;
    const newFetching = Object.assign({}, state.fetching);
    delete newFetching[outpoint];
  
    return Object.assign({}, state, {
      fetching: newFetching
    });
  };
  
  reducers$2[DOWNLOADING_STARTED] = (state, action) => {
    const { uri, outpoint, fileInfo } = action.data;
  
    const newByOutpoint = Object.assign({}, state.byOutpoint);
    const newDownloading = Object.assign({}, state.downloadingByOutpoint);
  
    newDownloading[outpoint] = true;
    newByOutpoint[outpoint] = fileInfo;
  
    return Object.assign({}, state, {
      downloadingByOutpoint: newDownloading,
      byOutpoint: newByOutpoint
    });
  };
  
  reducers$2[DOWNLOADING_PROGRESSED] = (state, action) => {
    const { outpoint, fileInfo } = action.data;
  
    const newByOutpoint = Object.assign({}, state.byOutpoint);
    const newDownloading = Object.assign({}, state.downloadingByOutpoint);
  
    newByOutpoint[outpoint] = fileInfo;
    newDownloading[outpoint] = true;
  
    return Object.assign({}, state, {
      byOutpoint: newByOutpoint,
      downloadingByOutpoint: newDownloading
    });
  };
  
  reducers$2[DOWNLOADING_CANCELED] = (state, action) => {
    const { uri, outpoint } = action.data;
  
    const newDownloading = Object.assign({}, state.downloadingByOutpoint);
    delete newDownloading[outpoint];
  
    return Object.assign({}, state, {
      downloadingByOutpoint: newDownloading
    });
  };
  
  reducers$2[DOWNLOADING_COMPLETED] = (state, action) => {
    const { outpoint, fileInfo } = action.data;
  
    const newByOutpoint = Object.assign({}, state.byOutpoint);
    const newDownloading = Object.assign({}, state.downloadingByOutpoint);
  
    newByOutpoint[outpoint] = fileInfo;
    delete newDownloading[outpoint];
  
    return Object.assign({}, state, {
      byOutpoint: newByOutpoint,
      downloadingByOutpoint: newDownloading
    });
  };
  
  reducers$2[FILE_DELETE] = (state, action) => {
    const { outpoint } = action.data;
  
    const newByOutpoint = Object.assign({}, state.byOutpoint);
    const downloadingByOutpoint = Object.assign({}, state.downloadingByOutpoint);
  
    delete newByOutpoint[outpoint];
    delete downloadingByOutpoint[outpoint];
  
    return Object.assign({}, state, {
      byOutpoint: newByOutpoint,
      downloadingByOutpoint
    });
  };
  
  reducers$2[SET_FILE_LIST_SORT] = (state, action) => {
    const pageSortStates = {
      [PUBLISHED]: 'fileListPublishedSort',
      [DOWNLOADED]: 'fileListDownloadedSort'
    };
    const pageSortState = pageSortStates[action.data.page];
    const { value } = action.data;
  
    return Object.assign({}, state, {
      [pageSortState]: value
    });
  };
  
  function fileInfoReducer(state = defaultState$2, action) {
    const handler = reducers$2[action.type];
    if (handler) return handler(state, action);
    return state;
  }
  
  // util for creating reducers
  // based off of redux-actions
  // https://redux-actions.js.org/docs/api/handleAction.html#handleactions
  
  // eslint-disable-next-line import/prefer-default-export
  const handleActions = (actionMap, defaultState) => (state = defaultState, action) => {
    const handler = actionMap[action.type];
  
    if (handler) {
      const newState = handler(state, action);
      return Object.assign({}, state, newState);
    }
  
    // just return the original state if no handler
    // returning a copy here breaks redux-persist
    return state;
  };
  
  var _extends$b = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  const defaultState$3 = {
    notifications: [],
    toasts: [],
    errors: []
  };
  
  const notificationsReducer = handleActions({
    // Toasts
    [CREATE_TOAST]: (state, action) => {
      const toast = action.data;
      const newToasts = state.toasts.slice();
      newToasts.push(toast);
  
      return _extends$b({}, state, {
        toasts: newToasts
      });
    },
    [DISMISS_TOAST]: state => {
      const newToasts = state.toasts.slice();
      newToasts.shift();
  
      return _extends$b({}, state, {
        toasts: newToasts
      });
    },
  
    // Notifications
    [CREATE_NOTIFICATION]: (state, action) => {
      const notification = action.data;
      const newNotifications = state.notifications.slice();
      newNotifications.push(notification);
  
      return _extends$b({}, state, {
        notifications: newNotifications
      });
    },
    // Used to mark notifications as read/dismissed
    [EDIT_NOTIFICATION]: (state, action) => {
      const { notification } = action.data;
      let notifications = state.notifications.slice();
  
      notifications = notifications.map(pastNotification => pastNotification.id === notification.id ? notification : pastNotification);
  
      return _extends$b({}, state, {
        notifications
      });
    },
    [DELETE_NOTIFICATION]: (state, action) => {
      const { id } = action.data;
      let newNotifications = state.notifications.slice();
      newNotifications = newNotifications.filter(notification => notification.id !== id);
  
      return _extends$b({}, state, {
        notifications: newNotifications
      });
    },
  
    // Errors
    [CREATE_ERROR]: (state, action) => {
      const error = action.data;
      const newErrors = state.errors.slice();
      newErrors.push(error);
  
      return _extends$b({}, state, {
        errors: newErrors
      });
    },
    [DISMISS_ERROR]: state => {
      const newErrors = state.errors.slice();
      newErrors.shift();
  
      return _extends$b({}, state, {
        errors: newErrors
      });
    }
  }, defaultState$3);
  
  var _extends$c = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  function _objectWithoutProperties$4(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
  
  const defaultState$4 = {
    editingURI: undefined,
    fileText: '',
    filePath: undefined,
    fileDur: 0,
    fileSize: 0,
    fileVid: false,
    remoteFileUrl: undefined,
    contentIsFree: true,
    fee: {
      amount: 1,
      currency: 'LBC'
    },
    title: '',
    thumbnail_url: '',
    thumbnailPath: '',
    uploadThumbnailStatus: API_DOWN,
    thumbnailError: undefined,
    description: '',
    language: '',
    releaseTime: undefined,
    releaseTimeEdited: undefined,
    nsfw: false,
    channel: CHANNEL_ANONYMOUS,
    channelId: '',
    name: '',
    nameError: undefined,
    bid: 0.01,
    bidError: undefined,
    licenseType: 'None',
    otherLicenseDescription: 'All rights reserved',
    licenseUrl: '',
    tags: [],
    publishing: false,
    publishSuccess: false,
    publishError: undefined,
    optimize: false,
    useLBRYUploader: false
  };
  
  const publishReducer = handleActions({
    [UPDATE_PUBLISH_FORM]: (state, action) => {
      const { data } = action;
      return _extends$c({}, state, data);
    },
    [CLEAR_PUBLISH]: state => _extends$c({}, defaultState$4, {
      uri: undefined,
      channel: state.channel,
      bid: state.bid,
      optimize: state.optimize,
      language: state.language
    }),
    [PUBLISH_START]: state => _extends$c({}, state, {
      publishing: true,
      publishSuccess: false
    }),
    [PUBLISH_FAIL]: state => _extends$c({}, state, {
      publishing: false
    }),
    [PUBLISH_SUCCESS]: state => _extends$c({}, state, {
      publishing: false,
      publishSuccess: true
    }),
    [DO_PREPARE_EDIT]: (state, action) => {
      const publishData = _objectWithoutProperties$4(action.data, []);
      const { channel, name, uri } = publishData;
  
      // The short uri is what is presented to the user
      // The editingUri is the full uri with claim id
      const shortUri = buildURI({
        channelName: channel,
        streamName: name
      });
  
      return _extends$c({}, defaultState$4, publishData, {
        editingURI: uri,
        uri: shortUri
      });
    }
  }, defaultState$4);
  
  var _extends$d = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  const buildDraftTransaction = () => ({
    amount: undefined,
    address: undefined
  });
  
  // TODO: Split into common success and failure types
  // See details in https://github.com/lbryio/lbry/issues/1307
  
  
  const defaultState$5 = {
    balance: undefined,
    totalBalance: undefined,
    reservedBalance: undefined,
    claimsBalance: undefined,
    supportsBalance: undefined,
    tipsBalance: undefined,
    latestBlock: undefined,
    transactions: {},
    fetchingTransactions: false,
    fetchingTransactionsError: undefined,
    supports: {},
    fetchingSupports: false,
    abandoningSupportsByOutpoint: {},
    gettingNewAddress: false,
    draftTransaction: buildDraftTransaction(),
    sendingSupport: false,
    walletIsEncrypted: false,
    walletEncryptPending: false,
    walletEncryptSucceded: null,
    walletEncryptResult: null,
    walletDecryptPending: false,
    walletDecryptSucceded: null,
    walletDecryptResult: null,
    walletUnlockPending: false,
    walletUnlockSucceded: null,
    walletUnlockResult: null,
    walletLockPending: false,
    walletLockSucceded: null,
    walletLockResult: null,
    transactionListFilter: 'all',
    walletReconnecting: false,
    txoFetchParams: {},
    utxoCounts: {},
    fetchingUtxoCounts: false,
    fetchingUtxoError: undefined,
    consolidatingUtxos: false,
    pendingConsolidateTxid: null,
    massClaimingTips: false,
    pendingMassClaimTxid: null,
    txoPage: {},
    fetchId: '',
    fetchingTxos: false,
    fetchingTxosError: undefined,
    pendingSupportTransactions: {},
    pendingTxos: [],
  
    abandonClaimSupportError: undefined
  };
  
  const walletReducer = handleActions({
    [FETCH_TRANSACTIONS_STARTED]: state => _extends$d({}, state, {
      fetchingTransactions: true
    }),
  
    [FETCH_TRANSACTIONS_COMPLETED]: (state, action) => {
      const byId = _extends$d({}, state.transactions);
  
      const { transactions } = action.data;
      transactions.forEach(transaction => {
        byId[transaction.txid] = transaction;
      });
  
      return _extends$d({}, state, {
        transactions: byId,
        fetchingTransactions: false
      });
    },
  
    [FETCH_TXO_PAGE_STARTED]: (state, action) => {
      return _extends$d({}, state, {
        fetchId: action.data,
        fetchingTxos: true,
        fetchingTxosError: undefined
      });
    },
  
    [FETCH_TXO_PAGE_COMPLETED]: (state, action) => {
      if (state.fetchId !== action.data.fetchId) {
        // Leave 'state' and 'fetchingTxos' alone. The latter would ensure
        // the spiner would continue spinning for the latest transaction.
        return _extends$d({}, state);
      }
  
      return _extends$d({}, state, {
        txoPage: action.data.result,
        fetchId: '',
        fetchingTxos: false
      });
    },
  
    [FETCH_TXO_PAGE_FAILED]: (state, action) => {
      return _extends$d({}, state, {
        txoPage: {},
        fetchId: '',
        fetchingTxos: false,
        fetchingTxosError: action.data
      });
    },
    [FETCH_UTXO_COUNT_STARTED]: state => {
      return _extends$d({}, state, {
        fetchingUtxoCounts: true,
        fetchingUtxoError: undefined
      });
    },
  
    [FETCH_UTXO_COUNT_COMPLETED]: (state, action) => {
      return _extends$d({}, state, {
        utxoCounts: action.data,
        fetchingUtxoCounts: false
      });
    },
    [FETCH_UTXO_COUNT_FAILED]: (state, action) => {
      return _extends$d({}, state, {
        utxoCounts: {},
        fetchingUtxoCounts: false,
        fetchingUtxoError: action.data
      });
    },
    [DO_UTXO_CONSOLIDATE_STARTED]: state => {
      return _extends$d({}, state, {
        consolidatingUtxos: true
      });
    },
  
    [DO_UTXO_CONSOLIDATE_COMPLETED]: (state, action) => {
      const { txid } = action.data;
      return _extends$d({}, state, {
        consolidatingUtxos: false,
        pendingConsolidateTxid: txid
      });
    },
  
    [DO_UTXO_CONSOLIDATE_FAILED]: (state, action) => {
      return _extends$d({}, state, {
        consolidatingUtxos: false
      });
    },
  
    [TIP_CLAIM_MASS_STARTED]: state => {
      return _extends$d({}, state, {
        massClaimingTips: true
      });
    },
  
    [TIP_CLAIM_MASS_COMPLETED]: (state, action) => {
      const { txid } = action.data;
      return _extends$d({}, state, {
        massClaimingTips: false,
        pendingMassClaimTxid: txid
      });
    },
  
    [TIP_CLAIM_MASS_FAILED]: (state, action) => {
      return _extends$d({}, state, {
        massClaimingTips: false
      });
    },
  
    [PENDING_CONSOLIDATED_TXOS_UPDATED]: (state, action) => {
      const { pendingTxos, pendingMassClaimTxid, pendingConsolidateTxid } = state;
  
      const { txids, remove } = action.data;
  
      if (remove) {
        const newTxos = pendingTxos.filter(txo => !txids.includes(txo));
        const newPendingMassClaimTxid = txids.includes(pendingMassClaimTxid) ? undefined : pendingMassClaimTxid;
        const newPendingConsolidateTxid = txids.includes(pendingConsolidateTxid) ? undefined : pendingConsolidateTxid;
        return _extends$d({}, state, {
          pendingTxos: newTxos,
          pendingMassClaimTxid: newPendingMassClaimTxid,
          pendingConsolidateTxid: newPendingConsolidateTxid
        });
      } else {
        const newPendingSet = new Set([...pendingTxos, ...txids]);
        return _extends$d({}, state, { pendingTxos: Array.from(newPendingSet) });
      }
    },
  
    [UPDATE_TXO_FETCH_PARAMS]: (state, action) => {
      return _extends$d({}, state, {
        txoFetchParams: action.data
      });
    },
  
    [FETCH_SUPPORTS_STARTED]: state => _extends$d({}, state, {
      fetchingSupports: true
    }),
  
    [FETCH_SUPPORTS_COMPLETED]: (state, action) => {
      const byOutpoint = state.supports;
      const { supports } = action.data;
  
      supports.forEach(transaction => {
        const { txid, nout } = transaction;
        byOutpoint[`${txid}:${nout}`] = transaction;
      });
  
      return _extends$d({}, state, { supports: byOutpoint, fetchingSupports: false });
    },
  
    [ABANDON_SUPPORT_STARTED]: (state, action) => {
      const { outpoint } = action.data;
      const currentlyAbandoning = state.abandoningSupportsByOutpoint;
  
      currentlyAbandoning[outpoint] = true;
  
      return _extends$d({}, state, {
        abandoningSupportsByOutpoint: currentlyAbandoning
      });
    },
  
    [ABANDON_SUPPORT_COMPLETED]: (state, action) => {
      const { outpoint } = action.data;
      const byOutpoint = state.supports;
      const currentlyAbandoning = state.abandoningSupportsByOutpoint;
  
      delete currentlyAbandoning[outpoint];
      delete byOutpoint[outpoint];
  
      return _extends$d({}, state, {
        supports: byOutpoint,
        abandoningSupportsByOutpoint: currentlyAbandoning
      });
    },
  
    [ABANDON_CLAIM_SUPPORT_STARTED]: (state, action) => {
      return _extends$d({}, state, {
        abandonClaimSupportError: undefined
      });
    },
  
    [ABANDON_CLAIM_SUPPORT_PREVIEW]: (state, action) => {
      return _extends$d({}, state, {
        abandonClaimSupportError: undefined
      });
    },
  
    [ABANDON_CLAIM_SUPPORT_COMPLETED]: (state, action) => {
      const {
        claimId,
        type,
        txid,
        effective
      } = action.data;
      const pendingtxs = Object.assign({}, state.pendingSupportTransactions);
  
      pendingtxs[claimId] = { txid, type, effective };
  
      return _extends$d({}, state, {
        pendingSupportTransactions: pendingtxs,
        abandonClaimSupportError: undefined
      });
    },
  
    [ABANDON_CLAIM_SUPPORT_FAILED]: (state, action) => {
      return _extends$d({}, state, {
        abandonClaimSupportError: action.data
      });
    },
  
    [PENDING_SUPPORTS_UPDATED]: (state, action) => {
      return _extends$d({}, state, {
        pendingSupportTransactions: action.data
      });
    },
  
    [GET_NEW_ADDRESS_STARTED]: state => _extends$d({}, state, {
      gettingNewAddress: true
    }),
  
    [GET_NEW_ADDRESS_COMPLETED]: (state, action) => {
      const { address } = action.data;
  
      return _extends$d({}, state, { gettingNewAddress: false, receiveAddress: address });
    },
  
    [UPDATE_BALANCE]: (state, action) => _extends$d({}, state, {
      totalBalance: action.data.totalBalance,
      balance: action.data.balance,
      reservedBalance: action.data.reservedBalance,
      claimsBalance: action.data.claimsBalance,
      supportsBalance: action.data.supportsBalance,
      tipsBalance: action.data.tipsBalance
    }),
  
    [CHECK_ADDRESS_IS_MINE_STARTED]: state => _extends$d({}, state, {
      checkingAddressOwnership: true
    }),
  
    [CHECK_ADDRESS_IS_MINE_COMPLETED]: state => _extends$d({}, state, {
      checkingAddressOwnership: false
    }),
  
    [SET_DRAFT_TRANSACTION_AMOUNT]: (state, action) => {
      const oldDraft = state.draftTransaction;
      const newDraft = _extends$d({}, oldDraft, { amount: parseFloat(action.data.amount) });
  
      return _extends$d({}, state, { draftTransaction: newDraft });
    },
  
    [SET_DRAFT_TRANSACTION_ADDRESS]: (state, action) => {
      const oldDraft = state.draftTransaction;
      const newDraft = _extends$d({}, oldDraft, { address: action.data.address });
  
      return _extends$d({}, state, { draftTransaction: newDraft });
    },
  
    [SEND_TRANSACTION_STARTED]: state => {
      const newDraftTransaction = _extends$d({}, state.draftTransaction, { sending: true });
  
      return _extends$d({}, state, { draftTransaction: newDraftTransaction });
    },
  
    [SEND_TRANSACTION_COMPLETED]: state => Object.assign({}, state, {
      draftTransaction: buildDraftTransaction()
    }),
  
    [SEND_TRANSACTION_FAILED]: (state, action) => {
      const newDraftTransaction = Object.assign({}, state.draftTransaction, {
        sending: false,
        error: action.data.error
      });
  
      return _extends$d({}, state, { draftTransaction: newDraftTransaction });
    },
  
    [SUPPORT_TRANSACTION_STARTED]: state => _extends$d({}, state, {
      sendingSupport: true
    }),
  
    [SUPPORT_TRANSACTION_COMPLETED]: state => _extends$d({}, state, {
      sendingSupport: false
    }),
  
    [SUPPORT_TRANSACTION_FAILED]: (state, action) => _extends$d({}, state, {
      error: action.data.error,
      sendingSupport: false
    }),
  
    [CLEAR_SUPPORT_TRANSACTION]: state => _extends$d({}, state, {
      sendingSupport: false
    }),
  
    [WALLET_STATUS_COMPLETED]: (state, action) => _extends$d({}, state, {
      walletIsEncrypted: action.result
    }),
  
    [WALLET_ENCRYPT_START]: state => _extends$d({}, state, {
      walletEncryptPending: true,
      walletEncryptSucceded: null,
      walletEncryptResult: null
    }),
  
    [WALLET_ENCRYPT_COMPLETED]: (state, action) => _extends$d({}, state, {
      walletEncryptPending: false,
      walletEncryptSucceded: true,
      walletEncryptResult: action.result
    }),
  
    [WALLET_ENCRYPT_FAILED]: (state, action) => _extends$d({}, state, {
      walletEncryptPending: false,
      walletEncryptSucceded: false,
      walletEncryptResult: action.result
    }),
  
    [WALLET_DECRYPT_START]: state => _extends$d({}, state, {
      walletDecryptPending: true,
      walletDecryptSucceded: null,
      walletDecryptResult: null
    }),
  
    [WALLET_DECRYPT_COMPLETED]: (state, action) => _extends$d({}, state, {
      walletDecryptPending: false,
      walletDecryptSucceded: true,
      walletDecryptResult: action.result
    }),
  
    [WALLET_DECRYPT_FAILED]: (state, action) => _extends$d({}, state, {
      walletDecryptPending: false,
      walletDecryptSucceded: false,
      walletDecryptResult: action.result
    }),
  
    [WALLET_UNLOCK_START]: state => _extends$d({}, state, {
      walletUnlockPending: true,
      walletUnlockSucceded: null,
      walletUnlockResult: null
    }),
  
    [WALLET_UNLOCK_COMPLETED]: (state, action) => _extends$d({}, state, {
      walletUnlockPending: false,
      walletUnlockSucceded: true,
      walletUnlockResult: action.result
    }),
  
    [WALLET_UNLOCK_FAILED]: (state, action) => _extends$d({}, state, {
      walletUnlockPending: false,
      walletUnlockSucceded: false,
      walletUnlockResult: action.result
    }),
  
    [WALLET_LOCK_START]: state => _extends$d({}, state, {
      walletLockPending: false,
      walletLockSucceded: null,
      walletLockResult: null
    }),
  
    [WALLET_LOCK_COMPLETED]: (state, action) => _extends$d({}, state, {
      walletLockPending: false,
      walletLockSucceded: true,
      walletLockResult: action.result
    }),
  
    [WALLET_LOCK_FAILED]: (state, action) => _extends$d({}, state, {
      walletLockPending: false,
      walletLockSucceded: false,
      walletLockResult: action.result
    }),
  
    [SET_TRANSACTION_LIST_FILTER]: (state, action) => _extends$d({}, state, {
      transactionListFilter: action.data
    }),
  
    [UPDATE_CURRENT_HEIGHT]: (state, action) => _extends$d({}, state, {
      latestBlock: action.data
    }),
    [WALLET_RESTART]: state => _extends$d({}, state, {
      walletReconnecting: true
    }),
  
    [WALLET_RESTART_COMPLETED]: state => _extends$d({}, state, {
      walletReconnecting: false
    })
  }, defaultState$5);
  
  var _extends$e = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  const getTimestamp$1 = () => {
    return Math.floor(Date.now() / 1000);
  };
  
  const defaultState$6 = {
    builtin: {
      watchlater: {
        items: [],
        id: WATCH_LATER_ID,
        name: 'Watch Later',
        updatedAt: getTimestamp$1(),
        type: COL_TYPE_PLAYLIST
      },
      favorites: {
        items: [],
        id: FAVORITES_ID,
        name: 'Favorites',
        type: COL_TYPE_PLAYLIST,
        updatedAt: getTimestamp$1()
      }
    },
    resolved: {},
    unpublished: {}, // sync
    edited: {},
    pending: {},
    saved: [],
    isResolvingCollectionById: {},
    error: null
  };
  
  const collectionsReducer = handleActions({
    [COLLECTION_NEW]: (state, action) => {
      const { entry: params } = action.data; // { id:, items: Array<string>}
      // entry
      const newListTemplate = {
        id: params.id,
        name: params.name,
        items: [],
        updatedAt: getTimestamp$1(),
        type: params.type
      };
  
      const newList = Object.assign({}, newListTemplate, _extends$e({}, params));
      const { unpublished: lists } = state;
      const newLists = Object.assign({}, lists, { [params.id]: newList });
  
      return _extends$e({}, state, {
        unpublished: newLists
      });
    },
  
    [COLLECTION_DELETE]: (state, action) => {
      const { id, collectionKey } = action.data;
      const { edited: editList, unpublished: unpublishedList, pending: pendingList } = state;
      const newEditList = Object.assign({}, editList);
      const newUnpublishedList = Object.assign({}, unpublishedList);
  
      const newPendingList = Object.assign({}, pendingList);
  
      if (collectionKey && state[collectionKey] && state[collectionKey][id]) {
        const newList = Object.assign({}, state[collectionKey]);
        delete newList[id];
        return _extends$e({}, state, {
          [collectionKey]: newList
        });
      } else {
        if (newEditList[id]) {
          delete newEditList[id];
        } else if (newUnpublishedList[id]) {
          delete newUnpublishedList[id];
        } else if (newPendingList[id]) {
          delete newPendingList[id];
        }
      }
      return _extends$e({}, state, {
        edited: newEditList,
        unpublished: newUnpublishedList,
        pending: newPendingList
      });
    },
  
    [COLLECTION_PENDING]: (state, action) => {
      const { localId, claimId } = action.data;
      const { edited: editList, unpublished: unpublishedList, pending: pendingList } = state;
      const newEditList = Object.assign({}, editList);
      const newUnpublishedList = Object.assign({}, unpublishedList);
      const newPendingList = Object.assign({}, pendingList);
  
      if (localId) {
        // new publish
        newPendingList[claimId] = Object.assign({}, newUnpublishedList[localId] || {});
        delete newUnpublishedList[localId];
      } else {
        // edit update
        newPendingList[claimId] = Object.assign({}, newEditList[claimId]);
        delete newEditList[claimId];
      }
  
      return _extends$e({}, state, {
        edited: newEditList,
        unpublished: newUnpublishedList,
        pending: newPendingList
      });
    },
  
    [COLLECTION_EDIT]: (state, action) => {
      const { id, collectionKey, collection } = action.data;
  
      if (BUILTIN_LISTS.includes(id)) {
        const { builtin: lists } = state;
        return _extends$e({}, state, {
          [collectionKey]: _extends$e({}, lists, { [id]: collection })
        });
      }
  
      if (collectionKey === 'edited') {
        const { edited: lists } = state;
        return _extends$e({}, state, {
          edited: _extends$e({}, lists, { [id]: collection })
        });
      }
      const { unpublished: lists } = state;
      return _extends$e({}, state, {
        unpublished: _extends$e({}, lists, { [id]: collection })
      });
    },
  
    [COLLECTION_ERROR]: (state, action) => {
      return Object.assign({}, state, {
        error: action.data.message
      });
    },
  
    [COLLECTION_ITEMS_RESOLVE_STARTED]: (state, action) => {
      const { ids } = action.data;
      const { isResolvingCollectionById } = state;
      const newResolving = Object.assign({}, isResolvingCollectionById);
      ids.forEach(id => {
        newResolving[id] = true;
      });
      return Object.assign({}, state, _extends$e({}, state, {
        error: '',
        isResolvingCollectionById: newResolving
      }));
    },
    [USER_STATE_POPULATE]: (state, action) => {
      const { builtinCollections, savedCollections, unpublishedCollections } = action.data;
      return _extends$e({}, state, {
        unpublished: unpublishedCollections || state.unpublished,
        builtin: builtinCollections || state.builtin,
        saved: savedCollections || state.saved
      });
    },
    [COLLECTION_ITEMS_RESOLVE_COMPLETED]: (state, action) => {
      const { resolvedCollections, failedCollectionIds } = action.data;
      const { pending, edited, isResolvingCollectionById, resolved } = state;
      const newPending = Object.assign({}, pending);
      const newEdited = Object.assign({}, edited);
      const newResolved = Object.assign({}, resolved, resolvedCollections);
  
      const resolvedIds = Object.keys(resolvedCollections);
      const newResolving = Object.assign({}, isResolvingCollectionById);
      if (resolvedCollections && Object.keys(resolvedCollections).length) {
        resolvedIds.forEach(resolvedId => {
          if (newEdited[resolvedId]) {
            if (newEdited[resolvedId]['updatedAt'] < resolvedCollections[resolvedId]['updatedAt']) {
              delete newEdited[resolvedId];
            }
          }
          delete newResolving[resolvedId];
          if (newPending[resolvedId]) {
            delete newPending[resolvedId];
          }
        });
      }
  
      if (failedCollectionIds && Object.keys(failedCollectionIds).length) {
        failedCollectionIds.forEach(failedId => {
          delete newResolving[failedId];
        });
      }
  
      return Object.assign({}, state, _extends$e({}, state, {
        pending: newPending,
        resolved: newResolved,
        edited: newEdited,
        isResolvingCollectionById: newResolving
      }));
    },
    [COLLECTION_ITEMS_RESOLVE_FAILED]: (state, action) => {
      const { ids } = action.data;
      const { isResolvingCollectionById } = state;
      const newResolving = Object.assign({}, isResolvingCollectionById);
      ids.forEach(id => {
        delete newResolving[id];
      });
      return Object.assign({}, state, _extends$e({}, state, {
        isResolvingCollectionById: newResolving,
        error: action.data.message
      }));
    }
  }, defaultState$6);
  
  //      
  
  const selectState$5 = state => state.content || {};
  
  const makeSelectContentPositionForUri = uri => reselect.createSelector(selectState$5, makeSelectClaimForUri(uri), (state, claim) => {
    if (!claim) {
      return null;
    }
    const outpoint = `${claim.txid}:${claim.nout}`;
    const id = claim.claim_id;
    return state.positions[id] ? state.positions[id][outpoint] : null;
  });
  
  var _extends$f = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  const selectState$6 = state => state.notifications || {};
  
  const selectToast = reselect.createSelector(selectState$6, state => {
    if (state.toasts.length) {
      const { id, params } = state.toasts[0];
      return _extends$f({
        id
      }, params);
    }
  
    return null;
  });
  
  const selectError = reselect.createSelector(selectState$6, state => {
    if (state.errors.length) {
      const { error } = state.errors[0];
      return {
        error
      };
    }
  
    return null;
  });
  
  exports.ABANDON_STATES = abandon_states;
  exports.ACTIONS = action_types;
  exports.CLAIM_VALUES = claim;
  exports.COLLECTIONS_CONSTS = collections;
  exports.DAEMON_SETTINGS = daemon_settings;
  exports.DEFAULT_FOLLOWED_TAGS = DEFAULT_FOLLOWED_TAGS;
  exports.DEFAULT_KNOWN_TAGS = DEFAULT_KNOWN_TAGS;
  exports.LICENSES = licenses;
  exports.Lbry = lbryProxy;
  exports.LbryFirst = lbryFirstProxy;
  exports.MATURE_TAGS = MATURE_TAGS;
  exports.PAGES = pages;
  exports.SETTINGS = settings;
  exports.SHARED_PREFERENCES = shared_preferences;
  exports.SORT_OPTIONS = sort_options;
  exports.SPEECH_URLS = speech_urls;
  exports.THUMBNAIL_STATUSES = thumbnail_upload_statuses;
  exports.TRANSACTIONS = transaction_types;
  exports.TXO_LIST = txo_list;
  exports.TX_LIST = transaction_list;
  exports.apiCall = apiCall;
  exports.batchActions = batchActions;
  exports.buildSharedStateMiddleware = buildSharedStateMiddleware;
  exports.buildURI = buildURI;
  exports.claimsReducer = claimsReducer;
  exports.collectionsReducer = collectionsReducer;
  exports.contentReducer = contentReducer;
  exports.convertToShareLink = convertToShareLink;
  exports.createNormalizedClaimSearchKey = createNormalizedClaimSearchKey;
  exports.creditsToString = creditsToString;
  exports.doAbandonClaim = doAbandonClaim;
  exports.doAbandonTxo = doAbandonTxo;
  exports.doBalanceSubscribe = doBalanceSubscribe;
  exports.doCheckAddressIsMine = doCheckAddressIsMine;
  exports.doCheckPendingClaims = doCheckPendingClaims;
  exports.doCheckPublishNameAvailability = doCheckPublishNameAvailability;
  exports.doCheckReflectingFiles = doCheckReflectingFiles;
  exports.doClaimSearch = doClaimSearch;
  exports.doClearChannelErrors = doClearChannelErrors;
  exports.doClearPublish = doClearPublish;
  exports.doClearPurchasedUriSuccess = doClearPurchasedUriSuccess;
  exports.doClearRepostError = doClearRepostError;
  exports.doClearSupport = doClearSupport;
  exports.doCollectionDelete = doCollectionDelete;
  exports.doCollectionEdit = doCollectionEdit;
  exports.doCollectionPublish = doCollectionPublish;
  exports.doCollectionPublishUpdate = doCollectionPublishUpdate;
  exports.doCreateChannel = doCreateChannel;
  exports.doDismissError = doDismissError;
  exports.doDismissToast = doDismissToast;
  exports.doError = doError;
  exports.doFetchChannelListMine = doFetchChannelListMine;
  exports.doFetchClaimListMine = doFetchClaimListMine;
  exports.doFetchClaimsByChannel = doFetchClaimsByChannel;
  exports.doFetchCollectionListMine = doFetchCollectionListMine;
  exports.doFetchFileInfo = doFetchFileInfo;
  exports.doFetchFileInfos = doFetchFileInfos;
  exports.doFetchItemsInCollection = doFetchItemsInCollection;
  exports.doFetchItemsInCollections = doFetchItemsInCollections;
  exports.doFetchTransactions = doFetchTransactions;
  exports.doFetchTxoPage = doFetchTxoPage;
  exports.doFetchUtxoCounts = doFetchUtxoCounts;
  exports.doFileGet = doFileGet;
  exports.doFileList = doFileList;
  exports.doGetNewAddress = doGetNewAddress;
  exports.doImportChannel = doImportChannel;
  exports.doLocalCollectionCreate = doLocalCollectionCreate;
  exports.doPopulateSharedUserState = doPopulateSharedUserState;
  exports.doPreferenceGet = doPreferenceGet;
  exports.doPreferenceSet = doPreferenceSet;
  exports.doPrepareEdit = doPrepareEdit;
  exports.doPublish = doPublish;
  exports.doPurchaseList = doPurchaseList;
  exports.doPurchaseUri = doPurchaseUri;
  exports.doRepost = doRepost;
  exports.doResetThumbnailStatus = doResetThumbnailStatus;
  exports.doResolveUri = doResolveUri;
  exports.doResolveUris = doResolveUris;
  exports.doSendDraftTransaction = doSendDraftTransaction;
  exports.doSendTip = doSendTip;
  exports.doSetDraftTransactionAddress = doSetDraftTransactionAddress;
  exports.doSetDraftTransactionAmount = doSetDraftTransactionAmount;
  exports.doSetFileListSort = doSetFileListSort;
  exports.doSetTransactionListFilter = doSetTransactionListFilter;
  exports.doSupportAbandonForClaim = doSupportAbandonForClaim;
  exports.doTipClaimMass = doTipClaimMass;
  exports.doToast = doToast;
  exports.doUpdateBalance = doUpdateBalance;
  exports.doUpdateBlockHeight = doUpdateBlockHeight;
  exports.doUpdateChannel = doUpdateChannel;
  exports.doUpdatePublishForm = doUpdatePublishForm;
  exports.doUpdateTxoPageParams = doUpdateTxoPageParams;
  exports.doUploadThumbnail = doUploadThumbnail;
  exports.doUtxoConsolidate = doUtxoConsolidate;
  exports.doWalletDecrypt = doWalletDecrypt;
  exports.doWalletEncrypt = doWalletEncrypt;
  exports.doWalletReconnect = doWalletReconnect;
  exports.doWalletStatus = doWalletStatus;
  exports.doWalletUnlock = doWalletUnlock;
  exports.fileInfoReducer = fileInfoReducer;
  exports.formatCredits = formatCredits;
  exports.formatFullPrice = formatFullPrice;
  exports.isClaimNsfw = isClaimNsfw;
  exports.isNameValid = isNameValid;
  exports.isURIClaimable = isURIClaimable;
  exports.isURIValid = isURIValid;
  exports.makeSelectAbandoningClaimById = makeSelectAbandoningClaimById;
  exports.makeSelectAmountForUri = makeSelectAmountForUri;
  exports.makeSelectCanonicalUrlForUri = makeSelectCanonicalUrlForUri;
  exports.makeSelectChannelForClaimUri = makeSelectChannelForClaimUri;
  exports.makeSelectChannelPermUrlForClaimUri = makeSelectChannelPermUrlForClaimUri;
  exports.makeSelectClaimForClaimId = makeSelectClaimForClaimId;
  exports.makeSelectClaimForUri = makeSelectClaimForUri;
  exports.makeSelectClaimHasSource = makeSelectClaimHasSource;
  exports.makeSelectClaimIdForUri = makeSelectClaimIdForUri;
  exports.makeSelectClaimIdsForCollectionId = makeSelectClaimIdsForCollectionId;
  exports.makeSelectClaimIsMine = makeSelectClaimIsMine;
  exports.makeSelectClaimIsNsfw = makeSelectClaimIsNsfw;
  exports.makeSelectClaimIsPending = makeSelectClaimIsPending;
  exports.makeSelectClaimIsStreamPlaceholder = makeSelectClaimIsStreamPlaceholder;
  exports.makeSelectClaimWasPurchased = makeSelectClaimWasPurchased;
  exports.makeSelectClaimsInChannelForCurrentPageState = makeSelectClaimsInChannelForCurrentPageState;
  exports.makeSelectClaimsInChannelForPage = makeSelectClaimsInChannelForPage;
  exports.makeSelectCollectionForId = makeSelectCollectionForId;
  exports.makeSelectCollectionForIdHasClaimUrl = makeSelectCollectionForIdHasClaimUrl;
  exports.makeSelectCollectionIsMine = makeSelectCollectionIsMine;
  exports.makeSelectContentPositionForUri = makeSelectContentPositionForUri;
  exports.makeSelectContentTypeForUri = makeSelectContentTypeForUri;
  exports.makeSelectCountForCollectionId = makeSelectCountForCollectionId;
  exports.makeSelectCoverForUri = makeSelectCoverForUri;
  exports.makeSelectDateForUri = makeSelectDateForUri;
  exports.makeSelectDownloadPathForUri = makeSelectDownloadPathForUri;
  exports.makeSelectDownloadingForUri = makeSelectDownloadingForUri;
  exports.makeSelectEditedCollectionForId = makeSelectEditedCollectionForId;
  exports.makeSelectEffectiveAmountForUri = makeSelectEffectiveAmountForUri;
  exports.makeSelectFetchingChannelClaims = makeSelectFetchingChannelClaims;
  exports.makeSelectFileInfoForUri = makeSelectFileInfoForUri;
  exports.makeSelectFileNameForUri = makeSelectFileNameForUri;
  exports.makeSelectFilePartlyDownloaded = makeSelectFilePartlyDownloaded;
  exports.makeSelectFilteredTransactionsForPage = makeSelectFilteredTransactionsForPage;
  exports.makeSelectIndexForUrlInCollection = makeSelectIndexForUrlInCollection;
  exports.makeSelectIsAbandoningClaimForUri = makeSelectIsAbandoningClaimForUri;
  exports.makeSelectIsResolvingCollectionForId = makeSelectIsResolvingCollectionForId;
  exports.makeSelectIsUriResolving = makeSelectIsUriResolving;
  exports.makeSelectLatestTransactions = makeSelectLatestTransactions;
  exports.makeSelectLoadingForUri = makeSelectLoadingForUri;
  exports.makeSelectMediaTypeForUri = makeSelectMediaTypeForUri;
  exports.makeSelectMetadataForUri = makeSelectMetadataForUri;
  exports.makeSelectMetadataItemForUri = makeSelectMetadataItemForUri;
  exports.makeSelectMyChannelPermUrlForName = makeSelectMyChannelPermUrlForName;
  exports.makeSelectMyPublishedCollectionForId = makeSelectMyPublishedCollectionForId;
  exports.makeSelectMyPurchasesForPage = makeSelectMyPurchasesForPage;
  exports.makeSelectMyStreamUrlsForPage = makeSelectMyStreamUrlsForPage;
  exports.makeSelectNameForCollectionId = makeSelectNameForCollectionId;
  exports.makeSelectNextUrlForCollectionAndUrl = makeSelectNextUrlForCollectionAndUrl;
  exports.makeSelectNsfwCountForChannel = makeSelectNsfwCountForChannel;
  exports.makeSelectNsfwCountFromUris = makeSelectNsfwCountFromUris;
  exports.makeSelectOmittedCountForChannel = makeSelectOmittedCountForChannel;
  exports.makeSelectPendingAmountByUri = makeSelectPendingAmountByUri;
  exports.makeSelectPendingClaimForUri = makeSelectPendingClaimForUri;
  exports.makeSelectPendingCollectionForId = makeSelectPendingCollectionForId;
  exports.makeSelectPermanentUrlForUri = makeSelectPermanentUrlForUri;
  exports.makeSelectPublishFormValue = makeSelectPublishFormValue;
  exports.makeSelectPublishedCollectionForId = makeSelectPublishedCollectionForId;
  exports.makeSelectReflectingClaimForUri = makeSelectReflectingClaimForUri;
  exports.makeSelectSearchDownloadUrlsCount = makeSelectSearchDownloadUrlsCount;
  exports.makeSelectSearchDownloadUrlsForPage = makeSelectSearchDownloadUrlsForPage;
  exports.makeSelectShortUrlForUri = makeSelectShortUrlForUri;
  exports.makeSelectStakedLevelForChannelUri = makeSelectStakedLevelForChannelUri;
  exports.makeSelectStreamingUrlForUri = makeSelectStreamingUrlForUri;
  exports.makeSelectSupportsForUri = makeSelectSupportsForUri;
  exports.makeSelectTagInClaimOrChannelForUri = makeSelectTagInClaimOrChannelForUri;
  exports.makeSelectTagsForUri = makeSelectTagsForUri;
  exports.makeSelectThumbnailForUri = makeSelectThumbnailForUri;
  exports.makeSelectTitleForUri = makeSelectTitleForUri;
  exports.makeSelectTotalClaimsInChannelSearch = makeSelectTotalClaimsInChannelSearch;
  exports.makeSelectTotalItemsForChannel = makeSelectTotalItemsForChannel;
  exports.makeSelectTotalPagesForChannel = makeSelectTotalPagesForChannel;
  exports.makeSelectTotalPagesInChannelSearch = makeSelectTotalPagesInChannelSearch;
  exports.makeSelectTotalStakedAmountForChannelUri = makeSelectTotalStakedAmountForChannelUri;
  exports.makeSelectUnpublishedCollectionForId = makeSelectUnpublishedCollectionForId;
  exports.makeSelectUriIsStreamable = makeSelectUriIsStreamable;
  exports.makeSelectUrlsForCollectionId = makeSelectUrlsForCollectionId;
  exports.normalizeURI = normalizeURI;
  exports.notificationsReducer = notificationsReducer;
  exports.parseQueryParams = parseQueryParams;
  exports.parseURI = parseURI;
  exports.publishReducer = publishReducer;
  exports.regexAddress = regexAddress;
  exports.regexInvalidURI = regexInvalidURI;
  exports.savePosition = savePosition;
  exports.selectAbandonClaimSupportError = selectAbandonClaimSupportError;
  exports.selectAbandoningIds = selectAbandoningIds;
  exports.selectAllClaimsByChannel = selectAllClaimsByChannel;
  exports.selectAllFetchingChannelClaims = selectAllFetchingChannelClaims;
  exports.selectAllMyClaimsByOutpoint = selectAllMyClaimsByOutpoint;
  exports.selectBalance = selectBalance;
  exports.selectBlocks = selectBlocks;
  exports.selectBuiltinCollections = selectBuiltinCollections;
  exports.selectChannelClaimCounts = selectChannelClaimCounts;
  exports.selectChannelImportPending = selectChannelImportPending;
  exports.selectClaimIdsByUri = selectClaimIdsByUri;
  exports.selectClaimSearchByQuery = selectClaimSearchByQuery;
  exports.selectClaimSearchByQueryLastPageReached = selectClaimSearchByQueryLastPageReached;
  exports.selectClaimsBalance = selectClaimsBalance;
  exports.selectClaimsById = selectClaimsById;
  exports.selectClaimsByUri = selectClaimsByUri;
  exports.selectCreateChannelError = selectCreateChannelError;
  exports.selectCreateCollectionError = selectCreateCollectionError;
  exports.selectCreatingChannel = selectCreatingChannel;
  exports.selectCreatingCollection = selectCreatingCollection;
  exports.selectCurrentChannelPage = selectCurrentChannelPage;
  exports.selectDownloadUrlsCount = selectDownloadUrlsCount;
  exports.selectDownloadedUris = selectDownloadedUris;
  exports.selectDownloadingByOutpoint = selectDownloadingByOutpoint;
  exports.selectDownloadingFileInfos = selectDownloadingFileInfos;
  exports.selectDraftTransaction = selectDraftTransaction;
  exports.selectDraftTransactionAddress = selectDraftTransactionAddress;
  exports.selectDraftTransactionAmount = selectDraftTransactionAmount;
  exports.selectDraftTransactionError = selectDraftTransactionError;
  exports.selectError = selectError;
  exports.selectFetchingClaimSearch = selectFetchingClaimSearch;
  exports.selectFetchingClaimSearchByQuery = selectFetchingClaimSearchByQuery;
  exports.selectFetchingMyChannels = selectFetchingMyChannels;
  exports.selectFetchingMyClaimsPageError = selectFetchingMyClaimsPageError;
  exports.selectFetchingMyCollections = selectFetchingMyCollections;
  exports.selectFetchingMyPurchasesError = selectFetchingMyPurchasesError;
  exports.selectFetchingTxosError = selectFetchingTxosError;
  exports.selectFileInfosByOutpoint = selectFileInfosByOutpoint;
  exports.selectFileInfosDownloaded = selectFileInfosDownloaded;
  exports.selectFileListDownloadedSort = selectFileListDownloadedSort;
  exports.selectFileListPublishedSort = selectFileListPublishedSort;
  exports.selectFilteredTransactionCount = selectFilteredTransactionCount;
  exports.selectFilteredTransactions = selectFilteredTransactions;
  exports.selectGettingNewAddress = selectGettingNewAddress;
  exports.selectHasTransactions = selectHasTransactions;
  exports.selectIsConsolidatingUtxos = selectIsConsolidatingUtxos;
  exports.selectIsFetchingClaimListMine = selectIsFetchingClaimListMine;
  exports.selectIsFetchingFileList = selectIsFetchingFileList;
  exports.selectIsFetchingFileListDownloadedOrPublished = selectIsFetchingFileListDownloadedOrPublished;
  exports.selectIsFetchingMyPurchases = selectIsFetchingMyPurchases;
  exports.selectIsFetchingTransactions = selectIsFetchingTransactions;
  exports.selectIsFetchingTxos = selectIsFetchingTxos;
  exports.selectIsFetchingUtxoCounts = selectIsFetchingUtxoCounts;
  exports.selectIsMassClaimingTips = selectIsMassClaimingTips;
  exports.selectIsResolvingPublishUris = selectIsResolvingPublishUris;
  exports.selectIsSendingSupport = selectIsSendingSupport;
  exports.selectIsStillEditing = selectIsStillEditing;
  exports.selectIsWalletReconnecting = selectIsWalletReconnecting;
  exports.selectMyActiveClaims = selectMyActiveClaims;
  exports.selectMyChannelClaims = selectMyChannelClaims;
  exports.selectMyChannelUrls = selectMyChannelUrls;
  exports.selectMyClaimForUri = selectMyClaimForUri;
  exports.selectMyClaimUrisWithoutChannels = selectMyClaimUrisWithoutChannels;
  exports.selectMyClaims = selectMyClaims;
  exports.selectMyClaimsOutpoints = selectMyClaimsOutpoints;
  exports.selectMyClaimsPage = selectMyClaimsPage;
  exports.selectMyClaimsPageItemCount = selectMyClaimsPageItemCount;
  exports.selectMyClaimsPageNumber = selectMyClaimsPageNumber;
  exports.selectMyClaimsRaw = selectMyClaimsRaw;
  exports.selectMyClaimsWithoutChannels = selectMyClaimsWithoutChannels;
  exports.selectMyCollectionIds = selectMyCollectionIds;
  exports.selectMyEditedCollections = selectMyEditedCollections;
  exports.selectMyPublishedCollections = selectMyPublishedCollections;
  exports.selectMyPublishedMixedCollections = selectMyPublishedMixedCollections;
  exports.selectMyPublishedPlaylistCollections = selectMyPublishedPlaylistCollections;
  exports.selectMyPurchases = selectMyPurchases;
  exports.selectMyPurchasesCount = selectMyPurchasesCount;
  exports.selectMyStreamUrlsCount = selectMyStreamUrlsCount;
  exports.selectMyUnpublishedCollections = selectMyUnpublishedCollections;
  exports.selectPendingClaims = selectPendingClaims;
  exports.selectPendingConsolidateTxid = selectPendingConsolidateTxid;
  exports.selectPendingIds = selectPendingIds;
  exports.selectPendingMassClaimTxid = selectPendingMassClaimTxid;
  exports.selectPendingOtherTransactions = selectPendingOtherTransactions;
  exports.selectPendingSupportTransactions = selectPendingSupportTransactions;
  exports.selectPlayingUri = selectPlayingUri;
  exports.selectPublishFormValues = selectPublishFormValues;
  exports.selectPurchaseUriSuccess = selectPurchaseUriSuccess;
  exports.selectReceiveAddress = selectReceiveAddress;
  exports.selectRecentTransactions = selectRecentTransactions;
  exports.selectReflectingById = selectReflectingById;
  exports.selectRepostError = selectRepostError;
  exports.selectRepostLoading = selectRepostLoading;
  exports.selectReservedBalance = selectReservedBalance;
  exports.selectResolvedCollections = selectResolvedCollections;
  exports.selectResolvingUris = selectResolvingUris;
  exports.selectSavedCollectionIds = selectSavedCollectionIds;
  exports.selectSupportsBalance = selectSupportsBalance;
  exports.selectSupportsByOutpoint = selectSupportsByOutpoint;
  exports.selectTakeOverAmount = selectTakeOverAmount;
  exports.selectTipsBalance = selectTipsBalance;
  exports.selectToast = selectToast;
  exports.selectTotalBalance = selectTotalBalance;
  exports.selectTotalDownloadProgress = selectTotalDownloadProgress;
  exports.selectTotalSupports = selectTotalSupports;
  exports.selectTransactionItems = selectTransactionItems;
  exports.selectTransactionListFilter = selectTransactionListFilter;
  exports.selectTransactionsById = selectTransactionsById;
  exports.selectTransactionsFile = selectTransactionsFile;
  exports.selectTxoItemCount = selectTxoItemCount;
  exports.selectTxoPage = selectTxoPage;
  exports.selectTxoPageNumber = selectTxoPageNumber;
  exports.selectTxoPageParams = selectTxoPageParams;
  exports.selectUpdateChannelError = selectUpdateChannelError;
  exports.selectUpdateCollectionError = selectUpdateCollectionError;
  exports.selectUpdatingChannel = selectUpdatingChannel;
  exports.selectUpdatingCollection = selectUpdatingCollection;
  exports.selectUrisLoading = selectUrisLoading;
  exports.selectUtxoCounts = selectUtxoCounts;
  exports.selectWalletDecryptPending = selectWalletDecryptPending;
  exports.selectWalletDecryptResult = selectWalletDecryptResult;
  exports.selectWalletDecryptSucceeded = selectWalletDecryptSucceeded;
  exports.selectWalletEncryptPending = selectWalletEncryptPending;
  exports.selectWalletEncryptResult = selectWalletEncryptResult;
  exports.selectWalletEncryptSucceeded = selectWalletEncryptSucceeded;
  exports.selectWalletIsEncrypted = selectWalletIsEncrypted;
  exports.selectWalletState = selectWalletState;
  exports.selectWalletUnlockPending = selectWalletUnlockPending;
  exports.selectWalletUnlockResult = selectWalletUnlockResult;
  exports.selectWalletUnlockSucceeded = selectWalletUnlockSucceeded;
  exports.toQueryString = toQueryString;
  exports.walletReducer = walletReducer;
  