'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } require('proxy-polyfill'); var reselect = require('reselect'); var uuid = _interopDefault(require('uuid/v4')); 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'; // 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 UPDATE_BALANCE = 'UPDATE_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 FETCH_BLOCK_SUCCESS = 'FETCH_BLOCK_SUCCESS'; const SUPPORT_TRANSACTION_STARTED = 'SUPPORT_TRANSACTION_STARTED'; const SUPPORT_TRANSACTION_COMPLETED = 'SUPPORT_TRANSACTION_COMPLETED'; const SUPPORT_TRANSACTION_FAILED = 'SUPPORT_TRANSACTION_FAILED'; 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 SET_TRANSACTION_LIST_FILTER = 'SET_TRANSACTION_LIST_FILTER'; const UPDATE_CURRENT_HEIGHT = 'UPDATE_CURRENT_HEIGHT'; // 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_CHANNEL_CLAIM_COUNT_STARTED = 'FETCH_CHANNEL_CLAIM_COUNT_STARTED'; const FETCH_CHANNEL_CLAIM_COUNT_COMPLETED = 'FETCH_CHANNEL_CLAIM_COUNT_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 CREATE_CHANNEL_STARTED = 'CREATE_CHANNEL_STARTED'; const CREATE_CHANNEL_COMPLETED = 'CREATE_CHANNEL_COMPLETED'; 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'; // 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 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'; // Search const SEARCH_START = 'SEARCH_START'; const SEARCH_SUCCESS = 'SEARCH_SUCCESS'; const SEARCH_FAIL = 'SEARCH_FAIL'; const UPDATE_SEARCH_QUERY = 'UPDATE_SEARCH_QUERY'; const UPDATE_SEARCH_OPTIONS = 'UPDATE_SEARCH_OPTIONS'; const UPDATE_SEARCH_SUGGESTIONS = 'UPDATE_SEARCH_SUGGESTIONS'; const SEARCH_FOCUS = 'SEARCH_FOCUS'; const SEARCH_BLUR = 'SEARCH_BLUR'; // Settings const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED'; 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'; // ShapeShift const GET_SUPPORTED_COINS_START = 'GET_SUPPORTED_COINS_START'; const GET_SUPPORTED_COINS_SUCCESS = 'GET_SUPPORTED_COINS_SUCCESS'; const GET_SUPPORTED_COINS_FAIL = 'GET_SUPPORTED_COINS_FAIL'; const GET_COIN_STATS_START = 'GET_COIN_STATS_START'; const GET_COIN_STATS_SUCCESS = 'GET_COIN_STATS_SUCCESS'; const GET_COIN_STATS_FAIL = 'GET_COIN_STATS_FAIL'; const PREPARE_SHAPE_SHIFT_START = 'PREPARE_SHAPE_SHIFT_START'; const PREPARE_SHAPE_SHIFT_SUCCESS = 'PREPARE_SHAPE_SHIFT_SUCCESS'; const PREPARE_SHAPE_SHIFT_FAIL = 'PREPARE_SHAPE_SHIFT_FAIL'; const GET_ACTIVE_SHIFT_START = 'GET_ACTIVE_SHIFT_START'; const GET_ACTIVE_SHIFT_SUCCESS = 'GET_ACTIVE_SHIFT_SUCCESS'; const GET_ACTIVE_SHIFT_FAIL = 'GET_ACTIVE_SHIFT_FAIL'; const CLEAR_SHAPE_SHIFT = 'CLEAR_SHAPE_SHIFT'; // 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 CREATE_TOAST = 'CREATE_TOAST'; const DISMISS_TOAST = 'DISMISS_TOAST'; const CREATE_ERROR = 'CREATE_ERROR'; const DISMISS_ERROR = 'DISMISS_ERROR'; const SET_DRAFT_TRANSACTION_AMOUNT = 'SET_DRAFT_TRANSACTION_AMOUNT'; const SET_DRAFT_TRANSACTION_ADDRESS = 'SET_DRAFT_TRANSACTION_ADDRESS'; const FETCH_DATE = 'FETCH_DATE'; const DISMISS_NOTIFICATION = 'DISMISS_NOTIFICATION'; 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, 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, UPDATE_BALANCE: UPDATE_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, FETCH_BLOCK_SUCCESS: FETCH_BLOCK_SUCCESS, SUPPORT_TRANSACTION_STARTED: SUPPORT_TRANSACTION_STARTED, SUPPORT_TRANSACTION_COMPLETED: SUPPORT_TRANSACTION_COMPLETED, SUPPORT_TRANSACTION_FAILED: SUPPORT_TRANSACTION_FAILED, 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, SET_TRANSACTION_LIST_FILTER: SET_TRANSACTION_LIST_FILTER, UPDATE_CURRENT_HEIGHT: UPDATE_CURRENT_HEIGHT, 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_CHANNEL_CLAIM_COUNT_STARTED: FETCH_CHANNEL_CLAIM_COUNT_STARTED, FETCH_CHANNEL_CLAIM_COUNT_COMPLETED: FETCH_CHANNEL_CLAIM_COUNT_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, CREATE_CHANNEL_STARTED: CREATE_CHANNEL_STARTED, CREATE_CHANNEL_COMPLETED: CREATE_CHANNEL_COMPLETED, 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, 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, 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, SEARCH_START: SEARCH_START, SEARCH_SUCCESS: SEARCH_SUCCESS, SEARCH_FAIL: SEARCH_FAIL, UPDATE_SEARCH_QUERY: UPDATE_SEARCH_QUERY, UPDATE_SEARCH_OPTIONS: UPDATE_SEARCH_OPTIONS, UPDATE_SEARCH_SUGGESTIONS: UPDATE_SEARCH_SUGGESTIONS, SEARCH_FOCUS: SEARCH_FOCUS, SEARCH_BLUR: SEARCH_BLUR, DAEMON_SETTINGS_RECEIVED: DAEMON_SETTINGS_RECEIVED, 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, GET_SUPPORTED_COINS_START: GET_SUPPORTED_COINS_START, GET_SUPPORTED_COINS_SUCCESS: GET_SUPPORTED_COINS_SUCCESS, GET_SUPPORTED_COINS_FAIL: GET_SUPPORTED_COINS_FAIL, GET_COIN_STATS_START: GET_COIN_STATS_START, GET_COIN_STATS_SUCCESS: GET_COIN_STATS_SUCCESS, GET_COIN_STATS_FAIL: GET_COIN_STATS_FAIL, PREPARE_SHAPE_SHIFT_START: PREPARE_SHAPE_SHIFT_START, PREPARE_SHAPE_SHIFT_SUCCESS: PREPARE_SHAPE_SHIFT_SUCCESS, PREPARE_SHAPE_SHIFT_FAIL: PREPARE_SHAPE_SHIFT_FAIL, GET_ACTIVE_SHIFT_START: GET_ACTIVE_SHIFT_START, GET_ACTIVE_SHIFT_SUCCESS: GET_ACTIVE_SHIFT_SUCCESS, GET_ACTIVE_SHIFT_FAIL: GET_ACTIVE_SHIFT_FAIL, CLEAR_SHAPE_SHIFT: CLEAR_SHAPE_SHIFT, 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, CREATE_TOAST: CREATE_TOAST, DISMISS_TOAST: DISMISS_TOAST, CREATE_ERROR: CREATE_ERROR, DISMISS_ERROR: DISMISS_ERROR, SET_DRAFT_TRANSACTION_AMOUNT: SET_DRAFT_TRANSACTION_AMOUNT, SET_DRAFT_TRANSACTION_ADDRESS: SET_DRAFT_TRANSACTION_ADDRESS, FETCH_DATE: FETCH_DATE, DISMISS_NOTIFICATION: DISMISS_NOTIFICATION }); 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 }); /* 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 CREDIT_REQUIRED_ACKNOWLEDGED = 'credit_required_acknowledged'; const NEW_USER_ACKNOWLEDGED = 'welcome_acknowledged'; const EMAIL_COLLECTION_ACKNOWLEDGED = 'email_collection_acknowledged'; const LANGUAGE = 'language'; const SHOW_NSFW = 'showNsfw'; const SHOW_UNAVAILABLE = 'showUnavailable'; const INSTANT_PURCHASE_ENABLED = 'instantPurchaseEnabled'; const INSTANT_PURCHASE_MAX = 'instantPurchaseMax'; const THEME = 'theme'; const THEMES = 'themes'; const AUTOMATIC_DARK_MODE_ENABLED = 'automaticDarkModeEnabled'; // mobile settings const BACKGROUND_PLAY_ENABLED = 'backgroundPlayEnabled'; const FOREGROUND_NOTIFICATION_ENABLED = 'foregroundNotificationEnabled'; const KEEP_DAEMON_RUNNING = 'keepDaemonRunning'; var settings = /*#__PURE__*/Object.freeze({ CREDIT_REQUIRED_ACKNOWLEDGED: CREDIT_REQUIRED_ACKNOWLEDGED, NEW_USER_ACKNOWLEDGED: NEW_USER_ACKNOWLEDGED, EMAIL_COLLECTION_ACKNOWLEDGED: EMAIL_COLLECTION_ACKNOWLEDGED, LANGUAGE: LANGUAGE, SHOW_NSFW: SHOW_NSFW, 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, BACKGROUND_PLAY_ENABLED: BACKGROUND_PLAY_ENABLED, FOREGROUND_NOTIFICATION_ENABLED: FOREGROUND_NOTIFICATION_ENABLED, KEEP_DAEMON_RUNNING: KEEP_DAEMON_RUNNING }); // eslint-disable-next-line import/prefer-default-export const ALL = 'all'; const SPEND = 'spend'; const RECEIVE = 'receive'; const PUBLISH = 'publish'; const CHANNEL = '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, CHANNEL: CHANNEL, TIP: TIP, SUPPORT: SUPPORT, UPDATE: UPDATE, ABANDON: ABANDON }); 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 AUTH = 'auth'; const BACKUP = 'backup'; const CHANNEL$1 = '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$1 = '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$1, DISCOVER: DISCOVER, FILE: FILE, DOWNLOADED: DOWNLOADED, PUBLISHED: PUBLISHED, GET_CREDITS: GET_CREDITS, HELP: HELP, INVITE: INVITE, PUBLISH: PUBLISH$1, REPORT: REPORT, REWARDS: REWARDS, SEARCH: SEARCH, SEND_CREDITS: SEND_CREDITS, SETTINGS: SETTINGS, SHOW: SHOW, SUBSCRIPTIONS: SUBSCRIPTIONS, TRANSACTION_HISTORY: TRANSACTION_HISTORY, HISTORY: HISTORY, WALLET: WALLET }); const SEARCH_TYPES = { FILE: 'file', CHANNEL: 'channel', SEARCH: 'search' }; const SEARCH_OPTIONS = { RESULT_COUNT: 'size', CLAIM_TYPE: 'claimType', INCLUDE_FILES: 'file', INCLUDE_CHANNELS: 'channel', INCLUDE_FILES_AND_CHANNELS: 'file,channel', MEDIA_AUDIO: 'audio', MEDIA_VIDEO: 'video', MEDIA_TEXT: 'text', MEDIA_IMAGE: 'image', MEDIA_APPLICATION: 'application' }; // const CHECK_DAEMON_STARTED_TRY_NUMBER = 200; const Lbry = { isConnected: false, daemonConnectionString: 'http://localhost:5279', pendingPublishTimeout: 20 * 60 * 1000 }; 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', body: JSON.stringify({ jsonrpc: '2.0', method, params, id: counter }) }; return fetch(Lbry.daemonConnectionString, 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); } const daemonCallWithResult = (name, params = {}) => new Promise((resolve, reject) => { apiCall(name, params, result => { resolve(result); }, reject); }); // blobs Lbry.blob_delete = (params = {}) => daemonCallWithResult('blob_delete', params); Lbry.blob_list = (params = {}) => daemonCallWithResult('blob_list', params); // core Lbry.status = (params = {}) => daemonCallWithResult('status', params); Lbry.version = () => daemonCallWithResult('version', {}); Lbry.file_delete = (params = {}) => daemonCallWithResult('file_delete', params); Lbry.file_set_status = (params = {}) => daemonCallWithResult('file_set_status', params); Lbry.stop = () => daemonCallWithResult('stop', {}); // claims Lbry.claim_list_by_channel = (params = {}) => daemonCallWithResult('claim_list_by_channel', params); // wallet Lbry.account_balance = (params = {}) => daemonCallWithResult('account_balance', params); Lbry.account_decrypt = () => daemonCallWithResult('account_decrypt', {}); Lbry.account_encrypt = (params = {}) => daemonCallWithResult('account_encrypt', params); Lbry.account_list = (params = {}) => daemonCallWithResult('account_list', params); Lbry.address_is_mine = (params = {}) => daemonCallWithResult('address_is_mine', params); Lbry.wallet_lock = () => daemonCallWithResult('wallet_lock', {}); Lbry.address_unused = (params = {}) => daemonCallWithResult('address_unused', params); Lbry.wallet_send = (params = {}) => daemonCallWithResult('wallet_send', params); Lbry.account_unlock = (params = {}) => daemonCallWithResult('account_unlock', params); Lbry.address_unused = () => daemonCallWithResult('address_unused', {}); Lbry.claim_tip = (params = {}) => daemonCallWithResult('claim_tip', params); // transactions Lbry.transaction_list = (params = {}) => daemonCallWithResult('transaction_list', params); Lbry.utxo_release = (params = {}) => daemonCallWithResult('utxo_release', params); Lbry.connectPromise = null; Lbry.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(); }); } return Lbry.connectPromise; }; Lbry.getMediaType = (contentType, extname) => { if (extname) { const formats = [[/^(mp4|m4v|webm|flv|f4v|ogv)$/i, 'video'], [/^(mp3|m4a|aac|wav|flac|ogg|opus)$/i, 'audio'], [/^(html|htm|xml|pdf|odf|doc|docx|md|markdown|txt|epub|org)$/i, 'document'], [/^(stl|obj|fbx|gcode)$/i, '3D-file']]; const res = formats.reduce((ret, testpair) => { switch (testpair[0].test(ret)) { case true: return testpair[1]; default: return ret; } }, extname); return res === extname ? 'unknown' : res; } else if (contentType) { return (/^[^/]+/.exec(contentType)[0] ); } return 'unknown'; }; /** * Wrappers for API methods to simulate missing or future behavior. Unlike the old-style stubs, * these are designed to be transparent wrappers around the corresponding API methods. */ /** * Returns results from the file_list API method, plus dummy entries for pending publishes. * (If a real publish with the same name is found, the pending publish will be ignored and removed.) */ Lbry.file_list = (params = {}) => new Promise((resolve, reject) => { apiCall('file_list', params, fileInfos => { resolve(fileInfos); }, reject); }); Lbry.claim_list_mine = (params = {}) => new Promise((resolve, reject) => { apiCall('claim_list_mine', params, claims => { resolve(claims); }, reject); }); Lbry.get = (params = {}) => new Promise((resolve, reject) => { apiCall('get', params, streamInfo => { resolve(streamInfo); }, reject); }); Lbry.resolve = (params = {}) => new Promise((resolve, reject) => { apiCall('resolve', params, data => { resolve(data || {}); }, reject); }); Lbry.publish = (params = {}) => new Promise((resolve, reject) => { if (Lbry.overrides.publish) { Lbry.overrides.publish(params).then(resolve, reject); } else { apiCall('publish', params, resolve, reject); } }); // Allow overriding Lbry methods Lbry.overrides = {}; Lbry.setOverride = (methodName, newMethod) => { Lbry.overrides[methodName] = newMethod; }; // Allow overriding daemon connection string (e.g. to `/api/proxy` for lbryweb) Lbry.setDaemonConnectionString = value => { Lbry.daemonConnectionString = value; }; 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 DEFAULT_SEARCH_RESULT_FROM = 0; const DEFAULT_SEARCH_SIZE = 20; 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('&'); } const getSearchQueryString = (query, options = {}, includeUserOptions = false) => { const encodedQuery = encodeURIComponent(query); const queryParams = [`s=${encodedQuery}`, `size=${options.size || DEFAULT_SEARCH_SIZE}`, `from=${options.from || DEFAULT_SEARCH_RESULT_FROM}`]; if (includeUserOptions) { queryParams.push(`claimType=${options[SEARCH_OPTIONS.CLAIM_TYPE]}`); // If they are only searching for channels, strip out the media info if (options[SEARCH_OPTIONS.CLAIM_TYPE] !== SEARCH_OPTIONS.INCLUDE_CHANNELS) { queryParams.push(`mediaType=${[SEARCH_OPTIONS.MEDIA_FILE, SEARCH_OPTIONS.MEDIA_AUDIO, SEARCH_OPTIONS.MEDIA_VIDEO, SEARCH_OPTIONS.MEDIA_TEXT, SEARCH_OPTIONS.MEDIA_IMAGE, SEARCH_OPTIONS.MEDIA_APPLICATION].reduce((acc, currentOption) => options[currentOption] ? `${acc}${currentOption},` : acc, '')}`); } } return queryParams.join('&'); }; 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; }; const channelNameMinLength = 1; const claimIdMaxLength = 40; const regexInvalidURI = /[^A-Za-z0-9-]/g; const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/; /** * Parses a LBRY name into its component parts. Throws errors with user-friendly * messages for invalid names. * * N.B. that "name" indicates the value in the name position of the URI. For * claims for channel content, this will actually be the channel name, and * the content name is in the path (e.g. lbry://@channel/content) * * In most situations, you'll want to use the contentName and channelName keys * and ignore the name key. * * Returns a dictionary with keys: * - name (string): The value in the "name" position in the URI. Note that this * could be either content name or channel name; see above. * - path (string, if persent) * - claimSequence (int, if present) * - bidPosition (int, if present) * - claimId (string, if present) * - isChannel (boolean) * - contentName (string): For anon claims, the name; for channel claims, the path * - channelName (string, if present): Channel name without @ */ function parseURI(URI, requireProto = false) { // Break into components. Empty sub-matches are converted to null const componentsRegex = new RegExp('^((?:lbry://)?)' + // protocol '([^:$#/]*)' + // claim name (stops at the first separator or end) '([:$#]?)([^/]*)' + // modifier separator, modifier (stops at the first path separator or end) '(/?)(.*)' // path separator, path ); const [proto, claimName, modSep, modVal, pathSep, path] = componentsRegex.exec(URI).slice(1).map(match => match || null); let contentName; // Validate protocol if (requireProto && !proto) { throw new Error(__('LBRY URIs must include a protocol prefix (lbry://).')); } // Validate and process name if (!claimName) { throw new Error(__('URI does not include name.')); } const isChannel = claimName.startsWith('@'); const channelName = isChannel ? claimName.slice(1) : claimName; if (isChannel) { if (!channelName) { throw new Error(__('No channel name after @.')); } if (channelName.length < channelNameMinLength) { throw new Error(__(`Channel names must be at least %s characters.`, channelNameMinLength)); } contentName = path; } const nameBadChars = (channelName || claimName).match(regexInvalidURI); if (nameBadChars) { throw new Error(__(`Invalid character %s in name: %s.`, nameBadChars.length === 1 ? '' : 's', nameBadChars.join(', '))); } // Validate and process modifier (claim ID, bid position or claim sequence) let claimId; let claimSequence; let bidPosition; if (modSep) { if (!modVal) { throw new Error(__(`No modifier provided after separator %s.`, modSep)); } if (modSep === '#') { claimId = modVal; } else if (modSep === ':') { claimSequence = modVal; } else if (modSep === '$') { bidPosition = modVal; } } if (claimId && (claimId.length > claimIdMaxLength || !claimId.match(/^[0-9a-f]+$/))) { throw new Error(__(`Invalid claim ID %s.`, 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.')); } // Validate and process path if (path) { if (!isChannel) { throw new Error(__('Only channel URIs may have a path.')); } const pathBadChars = path.match(regexInvalidURI); if (pathBadChars) { throw new Error(__(`Invalid character in path: %s`, pathBadChars.join(', '))); } contentName = path; } else if (pathSep) { throw new Error(__('No path provided after /')); } return _extends({ claimName, path, isChannel }, contentName ? { contentName } : {}, channelName ? { channelName } : {}, claimSequence ? { claimSequence: parseInt(claimSequence, 10) } : {}, bidPosition ? { bidPosition: parseInt(bidPosition, 10) } : {}, claimId ? { claimId } : {}, path ? { path } : {}); } /** * 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(URIObj, includeProto = true, protoDefault = 'lbry://') { const { claimId, claimSequence, bidPosition, contentName, channelName } = URIObj; let { claimName, path } = URIObj; if (channelName) { const channelNameFormatted = channelName.startsWith('@') ? channelName : `@${channelName}`; if (!claimName) { claimName = channelNameFormatted; } else if (claimName !== channelNameFormatted) { throw new Error(__('Received a channel content URI, but claim name and channelName do not match. "name" represents the value in the name position of the URI (lbry://name...), which for channel content will be the channel name. In most cases, to construct a channel URI you should just pass channelName and contentName.')); } } if (contentName) { if (!claimName) { claimName = contentName; } else if (!path) { path = contentName; } if (path && path !== contentName) { throw new Error(__('Path and contentName do not match. Only one is required; most likely you wanted contentName.')); } } return (includeProto ? protoDefault : '') + claimName + (claimId ? `#${claimId}` : '') + (claimSequence ? `:${claimSequence}` : '') + (bidPosition ? `${bidPosition}` : '') + (path ? `/${path}` : ''); } /* Takes a parseable LBRY URI and converts it to standard, canonical format */ function normalizeURI(URI) { const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI); return buildURI({ claimName, path, claimSequence, bidPosition, claimId }); } function isURIValid(URI) { let parts; try { parts = parseURI(normalizeURI(URI)); } catch (error) { return false; } return parts && parts.claimName; } function isNameValid(claimName, checkCase = true) { const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i'); return regexp.test(claimName); } function isURIClaimable(URI) { let parts; try { parts = parseURI(normalizeURI(URI)); } catch (error) { return false; } return parts && parts.claimName && !parts.claimId && !parts.bidPosition && !parts.claimSequence && !parts.isChannel && !parts.path; } function convertToShareLink(URI) { const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI); return buildURI({ claimName, path, claimSequence, bidPosition, claimId }, true, 'https://open.lbry.io/'); } 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; }; const selectState = state => state.search; const selectSearchValue = reselect.createSelector(selectState, state => state.searchQuery); const selectSearchOptions = reselect.createSelector(selectState, state => state.options); const selectSuggestions = reselect.createSelector(selectState, state => state.suggestions); const selectIsSearching = reselect.createSelector(selectState, state => state.searching); const selectSearchUrisByQuery = reselect.createSelector(selectState, state => state.urisByQuery); const makeSelectSearchUris = query => // replace statement below is kind of ugly, and repeated in doSearch action reselect.createSelector(selectSearchUrisByQuery, byQuery => byQuery[query ? query.replace(/^lbry:\/\//i, '').replace(/\//, ' ') : query]); const selectSearchBarFocused = reselect.createSelector(selectState, state => state.focused); const selectSearchSuggestions = reselect.createSelector(selectSearchValue, selectSuggestions, (query, suggestions) => { if (!query) { return []; } const queryIsPrefix = query === 'lbry:' || query === 'lbry:/' || query === 'lbry://'; if (query.startsWith('lbry://') && query !== 'lbry://') { // If it starts with a prefix, don't show any autocomplete results // They are probably typing/pasting in a lbry uri return [{ value: query, type: SEARCH_TYPES.FILE }]; } else if (queryIsPrefix) { // If it is a prefix, wait until something else comes to figure out what to do return []; } let searchSuggestions = []; try { const uri = normalizeURI(query); const { claimName, isChannel } = parseURI(uri); searchSuggestions.push({ value: claimName, type: SEARCH_TYPES.SEARCH }, { value: uri, shorthand: isChannel ? claimName.slice(1) : claimName, type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE }); } catch (e) { searchSuggestions.push({ value: query, type: SEARCH_TYPES.SEARCH }); } const apiSuggestions = suggestions[query] || []; if (apiSuggestions.length) { searchSuggestions = searchSuggestions.concat(apiSuggestions.filter(suggestion => suggestion !== query).map(suggestion => { // determine if it's a channel try { const uri = normalizeURI(suggestion); const { claimName, isChannel } = parseURI(uri); return { value: uri, shorthand: isChannel ? claimName.slice(1) : claimName, type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE }; } catch (e) { // search result includes some character that isn't valid in claim names return { value: suggestion, type: SEARCH_TYPES.SEARCH }; } })); } return searchSuggestions; }); // Creates a query string based on the state in the search reducer // Can be overrided by passing in custom sizes/from values for other areas pagination const makeSelectQueryWithOptions = (customQuery, customSize, customFrom, isBackgroundSearch = false // If it's a background search, don't use the users settings ) => reselect.createSelector(selectSearchValue, selectSearchOptions, (query, options) => { const size = customSize || options[SEARCH_OPTIONS.RESULT_COUNT]; const queryString = getSearchQueryString(customQuery || query, _extends$1({}, options, { size, from: customFrom }), !isBackgroundSearch); return queryString; }); // function doToast(params) { if (!params) { throw Error("'params' object is required to create a toast notification"); } return { type: CREATE_TOAST, data: { id: uuid(), params } }; } function doDismissToast() { return { type: DISMISS_TOAST }; } function doError(error) { return { type: CREATE_ERROR, data: { error } }; } function doDismissError() { return { type: DISMISS_ERROR }; } const isClaimNsfw = claim => claim && claim.value && claim.value.stream && claim.value.stream.metadata.nsfw; const selectState$1 = state => state.claims || {}; const selectClaimsById = reselect.createSelector(selectState$1, state => state.byId || {}); const selectCurrentChannelPage = reselect.createSelector(selectState$1, state => state.currentChannelPage || 1); const selectClaimsByUri = reselect.createSelector(selectState$1, selectClaimsById, (state, byId) => { const byUri = state.claimsByUri || {}; 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.claimsByChannel || {}); const selectPendingById = reselect.createSelector(selectState$1, state => state.pendingById || {}); const selectPendingClaims = reselect.createSelector(selectState$1, state => Object.values(state.pendingById || [])); const makeSelectClaimIsPending = uri => reselect.createSelector(selectPendingById, pendingById => { const { claimId } = parseURI(uri); return Boolean(pendingById[claimId]); }); const makeSelectPendingByUri = uri => reselect.createSelector(selectPendingById, pendingById => { const { claimId } = parseURI(uri); return pendingById[claimId]; }); const makeSelectClaimForUri = uri => reselect.createSelector(selectClaimsByUri, selectPendingById, (byUri, pendingById) => { // Check if a claim is pending first // It won't be in claimsByUri because resolving it will return nothing const { claimId } = parseURI(uri); const pendingClaim = pendingById[claimId]; if (pendingClaim) { return pendingClaim; } return byUri && byUri[normalizeURI(uri)]; }); const selectMyClaimsRaw = reselect.createSelector(selectState$1, state => state.myClaims); const selectAbandoningIds = reselect.createSelector(selectState$1, state => Object.keys(state.abandoningById || {})); 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 => { const uri = normalizeURI(rawUri); return reselect.createSelector(selectClaimsByUri, selectMyActiveClaims, (claims, myClaims) => claims && claims[uri] && claims[uri].claim_id && myClaims.has(claims[uri].claim_id)); }; 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 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 && claim.value.stream && claim.value.stream.metadata; return metadata || (claim === undefined ? undefined : null); }); const makeSelectTitleForUri = uri => reselect.createSelector(makeSelectMetadataForUri(uri), metadata => metadata && metadata.title); const makeSelectContentTypeForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => { const source = claim && claim.value && claim.value.stream && claim.value.stream.source; return source ? source.contentType : undefined; }); const selectIsFetchingClaimListMine = reselect.createSelector(selectState$1, state => state.isFetchingClaimListMine); const selectMyClaims = reselect.createSelector(selectMyActiveClaims, selectClaimsById, selectAbandoningIds, selectPendingClaims, (myClaimIds, byId, abandoningIds, pendingClaims) => { const claims = []; myClaimIds.forEach(id => { const claim = byId[id]; if (claim && abandoningIds.indexOf(id) === -1) claims.push(claim); }); return [...claims, ...pendingClaims]; }); const selectMyClaimsWithoutChannels = reselect.createSelector(selectMyClaims, myClaims => myClaims.filter(claim => !claim.name.match(/^@/))); 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 selectMyChannelClaims = reselect.createSelector(selectState$1, selectClaimsById, (state, byId) => { const ids = state.myChannelClaims || []; 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 selectResolvingUris = reselect.createSelector(selectState$1, state => state.resolvingUris || []); 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 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 (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 makeSelectRecommendedContentForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), selectSearchUrisByQuery, (claim, searchUrisByQuery) => { const atVanityURI = !uri.includes('#'); let recommendedContent; if (claim) { // If we are at a vanity uri, build the full uri so we can properly filter const currentUri = atVanityURI ? buildURI({ claimId: claim.claim_id, claimName: claim.name }) : uri; const { title } = claim.value.stream.metadata; const searchQuery = getSearchQueryString(title.replace(/\//, ' ')); let searchUris = searchUrisByQuery[searchQuery]; if (searchUris) { searchUris = searchUris.filter(searchUri => searchUri !== currentUri); recommendedContent = searchUris; } } return recommendedContent; }); const makeSelectFirstRecommendedFileForUri = uri => reselect.createSelector(makeSelectRecommendedContentForUri(uri), recommendedContent => recommendedContent ? recommendedContent[0] : null); // Returns the associated channel uri for a given claim uri const makeSelectChannelForClaimUri = (uri, includePrefix = false) => reselect.createSelector(makeSelectClaimForUri(uri), claim => { if (!claim) { return null; } const { channel_name: channelName, value } = claim; const channelClaimId = value && value.publisherSignature && value.publisherSignature.certificateId; return channelName && channelClaimId ? buildURI({ channelName, claimId: channelClaimId }, includePrefix) : null; }); const selectState$2 = state => state.wallet || {}; const selectWalletState = selectState$2; const selectWalletIsEncrypted = reselect.createSelector(selectState$2, state => state.walletIsEncrypted); const selectWalletEncryptPending = reselect.createSelector(selectState$2, state => state.walletEncryptPending); const selectWalletEncryptSucceeded = reselect.createSelector(selectState$2, state => state.walletEncryptSucceded); const selectWalletEncryptResult = reselect.createSelector(selectState$2, state => state.walletEncryptResult); const selectWalletDecryptPending = reselect.createSelector(selectState$2, state => state.walletDecryptPending); const selectWalletDecryptSucceeded = reselect.createSelector(selectState$2, state => state.walletDecryptSucceded); const selectWalletDecryptResult = reselect.createSelector(selectState$2, state => state.walletDecryptResult); const selectWalletUnlockPending = reselect.createSelector(selectState$2, state => state.walletUnlockPending); const selectWalletUnlockSucceeded = reselect.createSelector(selectState$2, state => state.walletUnlockSucceded); const selectWalletUnlockResult = reselect.createSelector(selectState$2, state => state.walletUnlockResult); const selectWalletLockPending = reselect.createSelector(selectState$2, state => state.walletLockPending); const selectWalletLockSucceeded = reselect.createSelector(selectState$2, state => state.walletLockSucceded); const selectWalletLockResult = reselect.createSelector(selectState$2, state => state.walletLockResult); const selectBalance = reselect.createSelector(selectState$2, state => state.balance); const selectTransactionsById = reselect.createSelector(selectState$2, state => state.transactions); 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 : PUBLISH }))); 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$2, state => state.fetchingTransactions); const selectIsSendingSupport = reselect.createSelector(selectState$2, state => state.sendingSupport); const selectReceiveAddress = reselect.createSelector(selectState$2, state => state.receiveAddress); const selectGettingNewAddress = reselect.createSelector(selectState$2, state => state.gettingNewAddress); const selectDraftTransaction = reselect.createSelector(selectState$2, 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$2, state => state.blocks); const selectCurrentHeight = reselect.createSelector(selectState$2, state => state.latestBlock); const makeSelectBlockDate = block => reselect.createSelector(selectBlocks, selectCurrentHeight, (blocks, latestBlock) => { // If we have the block data, look at the actual date, // If not, try to simulate it based on 2.5 minute blocks // Adding this on 11/7/2018 because caling block_show for every claim is causing // performance issues. if (blocks && blocks[block]) { return new Date(blocks[block].time * 1000); } // Pending claim if (block < 1) { return null; } const difference = latestBlock - block; const msSincePublish = difference * 2.5 * 60 * 1000; // Number of blocks * 2.5 minutes in ms const publishDate = Date.now() - msSincePublish; return new Date(publishDate); }); const selectTransactionListFilter = reselect.createSelector(selectState$2, state => state.transactionListFilter || ''); function formatCredits(amount, precision) { if (Number.isNaN(parseFloat(amount))) return '0'; return parseFloat(amount).toFixed(precision || 1).replace(/\.?0+$/, ''); } 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; } function doUpdateBalance() { return (dispatch, getState) => { const { wallet: { balance: balanceInStore } } = getState(); lbryProxy.account_balance().then(balanceAsString => { const balance = parseFloat(balanceAsString); if (balanceInStore !== balance) { dispatch({ type: UPDATE_BALANCE, data: { balance } }); } }); }; } function doBalanceSubscribe() { return dispatch => { dispatch(doUpdateBalance()); setInterval(() => dispatch(doUpdateBalance()), 5000); }; } function doFetchTransactions() { return dispatch => { dispatch({ type: FETCH_TRANSACTIONS_STARTED }); lbryProxy.utxo_release().then(() => lbryProxy.transaction_list()).then(results => { dispatch({ type: FETCH_TRANSACTIONS_COMPLETED, data: { transactions: results } }); }); }; } function doFetchBlock(height) { return dispatch => { lbryProxy.block_show({ height }).then(block => { dispatch({ type: FETCH_BLOCK_SUCCESS, data: { block } }); }); }; } function doGetNewAddress() { return dispatch => { dispatch({ type: GET_NEW_ADDRESS_STARTED }); // Removed localStorage use, since address is expected to be stored in redux store 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} LBC`, 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({ 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(amount, claimId, uri, successCallback, errorCallback) { return (dispatch, getState) => { const state = getState(); const balance = selectBalance(state); if (balance - amount <= 0) { dispatch(doToast({ message: 'Insufficient credits', isError: true })); return; } const success = () => { dispatch(doToast({ message: __(`You sent ${amount} LBC as a tip, Mahalo!`), linkText: __('History'), linkTarget: __('/wallet') })); dispatch({ type: SUPPORT_TRANSACTION_COMPLETED }); if (successCallback) { successCallback(); } }; 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.claim_tip({ claim_id: claimId, amount: creditsToString(amount) }).then(success, error); }; } function doWalletEncrypt(newPassword) { return dispatch => { dispatch({ type: WALLET_ENCRYPT_START }); lbryProxy.account_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.account_unlock({ password }).then(result => { if (result === true) { dispatch({ type: WALLET_UNLOCK_COMPLETED, result }); } else { dispatch({ type: WALLET_UNLOCK_FAILED, result }); } }); }; } function doWalletDecrypt() { return dispatch => { dispatch({ type: WALLET_DECRYPT_START }); lbryProxy.account_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.status().then(status => { if (status && status.wallet) { dispatch({ type: WALLET_STATUS_COMPLETED, result: status.wallet.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 }); } }); } function doResolveUris(uris, returnCachedClaims = false) { 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; } dispatch({ type: RESOLVE_URIS_STARTED, data: { uris: normalizedUris } }); const resolveInfo = {}; lbryProxy.resolve({ urls: urisToResolve }).then(result => { Object.entries(result).forEach(([uri, uriResolveInfo]) => { const fallbackResolveInfo = { claim: null, claimsInChannel: null, certificate: null }; const { claim, certificate, claims_in_channel: claimsInChannel } = uriResolveInfo && !uriResolveInfo.error ? uriResolveInfo : fallbackResolveInfo; resolveInfo[uri] = { claim, certificate, claimsInChannel }; }); dispatch({ type: RESOLVE_URIS_COMPLETED, data: { resolveInfo } }); }); }; } function doResolveUri(uri) { return doResolveUris([uri]); } function doFetchClaimListMine() { return dispatch => { dispatch({ type: FETCH_CLAIM_LIST_MINE_STARTED }); lbryProxy.claim_list_mine().then(claims => { dispatch({ type: FETCH_CLAIM_LIST_MINE_COMPLETED, data: { claims } }); }); }; } function doAbandonClaim(txid, nout) { return (dispatch, getState) => { const state = getState(); const myClaims = selectMyClaimsRaw(state); const { claim_id: claimId } = myClaims.find(claim => claim.txid === txid && claim.nout === nout); dispatch({ type: ABANDON_CLAIM_STARTED, data: { claimId } }); const errorCallback = () => { dispatch(doToast({ message: 'Transaction failed', isError: true })); }; const successCallback = results => { if (results.success === true) { dispatch({ type: ABANDON_CLAIM_SUCCEEDED, data: { claimId } }); dispatch(doToast({ message: 'Successfully abandoned your claim' })); // After abandoning, call claim_list_mine to show the claim as abandoned // Also fetch transactions to show the new abandon transaction dispatch(doFetchClaimListMine()); dispatch(doFetchTransactions()); } else { dispatch(doToast({ message: 'Error abandoning claim', isError: true })); } }; lbryProxy.claim_abandon({ txid, nout }).then(successCallback, errorCallback); }; } function doFetchClaimsByChannel(uri, page) { return dispatch => { dispatch({ type: FETCH_CHANNEL_CLAIMS_STARTED, data: { uri, page } }); lbryProxy.claim_list_by_channel({ uri, page: page || 1 }).then(result => { const claimResult = result[uri] || {}; const { claims_in_channel: claimsInChannel, returned_page: returnedPage } = claimResult; dispatch({ type: FETCH_CHANNEL_CLAIMS_COMPLETED, data: { uri, claims: claimsInChannel || [], page: returnedPage || undefined } }); }); }; } function doFetchClaimCountByChannel(uri) { return dispatch => { dispatch({ type: FETCH_CHANNEL_CLAIM_COUNT_STARTED, data: { uri } }); lbryProxy.claim_list_by_channel({ uri }).then(result => { const claimResult = result[uri]; const totalClaims = claimResult ? claimResult.claims_in_channel : 0; dispatch({ type: FETCH_CHANNEL_CLAIM_COUNT_COMPLETED, data: { uri, totalClaims } }); }); }; } 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.urisLoading || {}); const makeSelectLoadingForUri = uri => reselect.createSelector(selectUrisLoading, byUri => byUri && byUri[uri]); const selectFileInfosDownloaded = reselect.createSelector(selectFileInfosByOutpoint, selectMyClaims, (byOutpoint, myClaims) => Object.values(byOutpoint).filter(fileInfo => { const myClaimIds = myClaims.map(claim => claim.claim_id); return fileInfo && myClaimIds.indexOf(fileInfo.claim_id) === -1 && (fileInfo.completed || fileInfo.written_bytes); })); // 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 selectSearchDownloadUris = query => reselect.createSelector(selectFileInfosDownloaded, selectClaimsById, (fileInfos, claimsById) => { if (!query || !fileInfos.length) { return null; } const queryParts = query.toLowerCase().split(' '); const searchQueryDictionary = {}; queryParts.forEach(subQuery => { searchQueryDictionary[subQuery] = subQuery; }); const arrayContainsQueryPart = array => { for (let i = 0; i < array.length; i += 1) { const subQuery = array[i]; if (searchQueryDictionary[subQuery]) { return true; } } return false; }; const downloadResultsFromQuery = []; fileInfos.forEach(fileInfo => { const { channel_name: channelName, claim_name: claimName, metadata } = fileInfo; const { author, description, title } = metadata; if (channelName) { const lowerCaseChannel = channelName.toLowerCase(); const strippedOutChannelName = lowerCaseChannel.slice(1); // trim off the @ if (searchQueryDictionary[channelName] || searchQueryDictionary[strippedOutChannelName]) { downloadResultsFromQuery.push(fileInfo); return; } } const nameParts = claimName.toLowerCase().split('-'); if (arrayContainsQueryPart(nameParts)) { downloadResultsFromQuery.push(fileInfo); return; } const titleParts = title.toLowerCase().split(' '); if (arrayContainsQueryPart(titleParts)) { downloadResultsFromQuery.push(fileInfo); return; } if (author) { const authorParts = author.toLowerCase().split(' '); if (arrayContainsQueryPart(authorParts)) { downloadResultsFromQuery.push(fileInfo); return; } } if (description) { const descriptionParts = description.toLowerCase().split(' '); if (arrayContainsQueryPart(descriptionParts)) { downloadResultsFromQuery.push(fileInfo); } } }); return downloadResultsFromQuery.length ? downloadResultsFromQuery.map(fileInfo => { const { channel_name: channelName, claim_id: claimId, claim_name: claimName } = fileInfo; const uriParams = {}; if (channelName) { const claim = claimsById[claimId]; if (claim && claim.value) { uriParams.claimId = claim.value.publisherSignature.certificateId; } else { uriParams.claimId = claimId; } uriParams.channelName = channelName; uriParams.contentName = claimName; } else { uriParams.claimId = claimId; uriParams.claimName = claimName; } const uri = buildURI(uriParams); return uri; }) : null; }); const selectFileListPublishedSort = reselect.createSelector(selectState$3, state => state.fileListPublishedSort); const selectFileListDownloadedSort = reselect.createSelector(selectState$3, state => state.fileListDownloadedSort); 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 }).then(fileInfos => { dispatch({ type: FETCH_FILE_INFO_COMPLETED, data: { outpoint, fileInfo: fileInfos && fileInfos.length ? fileInfos[0] : null } }); }); } }; } function doFileList() { return (dispatch, getState) => { const state = getState(); const isFetching = selectIsFetchingFileList(state); if (!isFetching) { dispatch({ type: FILE_LIST_STARTED }); lbryProxy.file_list().then(fileInfos => { dispatch({ type: FILE_LIST_SUCCEEDED, data: { fileInfos } }); }); } }; } function doFetchFileInfosAndPublishedClaims() { return (dispatch, getState) => { const state = getState(); const isFetchingClaimListMine = selectIsFetchingClaimListMine(state); const isFetchingFileInfo = selectIsFetchingFileList(state); if (!isFetchingClaimListMine) dispatch(doFetchClaimListMine()); if (!isFetchingFileInfo) dispatch(doFileList()); }; } function doSetFileListSort(page, value) { return { type: SET_FILE_LIST_SORT, data: { page, value } }; } // https://github.com/reactjs/redux/issues/911 function batchActions(...actions) { return { type: 'BATCH_ACTIONS', actions }; } // Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. function debouce(func, wait, immediate) { let timeout; return function () { const context = this; const args = arguments; const later = () => { timeout = null; if (!immediate) func.apply(context, args); }; const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; } function handleFetchResponse(response) { return response.status === 200 ? Promise.resolve(response.json()) : Promise.reject(new Error(response.statusText)); } // const DEBOUNCED_SEARCH_SUGGESTION_MS = 300; // We can't use env's because they aren't passed into node_modules let CONNECTION_STRING = 'https://lighthouse.lbry.io/'; const setSearchApi = endpoint => { CONNECTION_STRING = endpoint.replace(/\/*$/, '/'); // exactly one slash at the end; }; const getSearchSuggestions = value => (dispatch, getState) => { const query = value.trim(); // strip out any basic stuff for more accurate search results let searchValue = query.replace(/lbry:\/\//g, '').replace(/-/g, ' '); if (searchValue.includes('#')) { // This should probably be more robust, but I think it's fine for now // Remove everything after # to get rid of the claim id searchValue = searchValue.substring(0, searchValue.indexOf('#')); } const suggestions = selectSuggestions(getState()); if (suggestions[searchValue]) { return; } fetch(`${CONNECTION_STRING}autocomplete?s=${searchValue}`).then(handleFetchResponse).then(apiSuggestions => { dispatch({ type: UPDATE_SEARCH_SUGGESTIONS, data: { query: searchValue, suggestions: apiSuggestions } }); }).catch(() => { // If the fetch fails, do nothing // Basic search suggestions are already populated at this point }); }; const throttledSearchSuggestions = debouce((dispatch, query) => { dispatch(getSearchSuggestions(query)); }, DEBOUNCED_SEARCH_SUGGESTION_MS); const doUpdateSearchQuery = (query, shouldSkipSuggestions) => dispatch => { dispatch({ type: UPDATE_SEARCH_QUERY, data: { query } }); // Don't fetch new suggestions if the user just added a space if (!query.endsWith(' ') || !shouldSkipSuggestions) { throttledSearchSuggestions(dispatch, query); } }; const doSearch = (rawQuery, // pass in a query if you don't want to search for what's in the search bar size, // only pass in if you don't want to use the users setting (ex: related content) from, isBackgroundSearch = false) => (dispatch, getState) => { const query = rawQuery.replace(/^lbry:\/\//i, '').replace(/\//, ' '); if (!query) { dispatch({ type: SEARCH_FAIL }); return; } const state = getState(); const queryWithOptions = makeSelectQueryWithOptions(query, size, from, isBackgroundSearch)(state); // If we have already searched for something, we don't need to do anything const urisForQuery = makeSelectSearchUris(queryWithOptions)(state); if (urisForQuery && !!urisForQuery.length) { return; } dispatch({ type: SEARCH_START }); // If the user is on the file page with a pre-populated uri and they select // the search option without typing anything, searchQuery will be empty // We need to populate it so the input is filled on the search page // isBackgroundSearch means the search is happening in the background, don't update the search query if (!state.search.searchQuery && !isBackgroundSearch) { dispatch(doUpdateSearchQuery(query)); } fetch(`${CONNECTION_STRING}search?${queryWithOptions}`).then(handleFetchResponse).then(data => { const uris = []; const actions = []; data.forEach(result => { const uri = buildURI({ claimName: result.name, claimId: result.claimId }); actions.push(doResolveUri(uri)); uris.push(uri); }); actions.push({ type: SEARCH_SUCCESS, data: { query: queryWithOptions, uris } }); dispatch(batchActions(...actions)); }).catch(() => { dispatch({ type: SEARCH_FAIL }); }); }; const doFocusSearchInput = () => dispatch => dispatch({ type: SEARCH_FOCUS }); const doBlurSearchInput = () => dispatch => dispatch({ type: SEARCH_BLUR }); const doUpdateSearchOptions = newOptions => (dispatch, getState) => { const state = getState(); const searchValue = selectSearchValue(state); dispatch({ type: UPDATE_SEARCH_OPTIONS, data: newOptions }); if (searchValue) { // After updating, perform a search with the new options dispatch(doSearch(searchValue)); } }; function savePosition(claimId, outpoint, position) { return dispatch => { dispatch({ type: SET_CONTENT_POSITION, data: { claimId, outpoint, position } }); }; } const reducers = {}; const defaultState = { channelClaimCounts: {} }; reducers[RESOLVE_URIS_COMPLETED] = (state, action) => { const { resolveInfo } = action.data; const byUri = Object.assign({}, state.claimsByUri); const byId = Object.assign({}, state.byId); const channelClaimCounts = Object.assign({}, state.channelClaimCounts); Object.entries(resolveInfo).forEach(([uri, { certificate, claimsInChannel }]) => { if (certificate && !Number.isNaN(claimsInChannel)) { channelClaimCounts[uri] = claimsInChannel; } }); Object.entries(resolveInfo).forEach(([uri, { certificate, claim }]) => { if (claim) { byId[claim.claim_id] = claim; byUri[uri] = claim.claim_id; } else if (claim === undefined && certificate !== undefined) { byId[certificate.claim_id] = certificate; // Don't point URI at the channel certificate unless it actually is // a channel URI. This is brittle. if (!uri.split(certificate.name)[1].match(/\//)) { byUri[uri] = certificate.claim_id; } else { byUri[uri] = null; } } else { byUri[uri] = null; } }); return Object.assign({}, state, { byId, claimsByUri: byUri, channelClaimCounts, resolvingUris: (state.resolvingUris || []).filter(uri => !resolveInfo[uri]) }); }; reducers[FETCH_CLAIM_LIST_MINE_STARTED] = state => Object.assign({}, state, { isFetchingClaimListMine: true }); reducers[FETCH_CLAIM_LIST_MINE_COMPLETED] = (state, action) => { const { claims } = action.data; const byId = Object.assign({}, state.byId); const byUri = Object.assign({}, state.claimsByUri); const pendingById = Object.assign({}, state.pendingById); claims.forEach(claim => { const uri = buildURI({ claimName: claim.name, claimId: claim.claim_id }); if (claim.type && claim.type.match(/claim|update/)) { if (claim.confirmations < 1) { pendingById[claim.claim_id] = claim; delete byId[claim.claim_id]; delete byUri[claim.claim_id]; } else { byId[claim.claim_id] = claim; byUri[uri] = claim.claim_id; } } }); // Remove old pending publishes Object.values(pendingById).filter(pendingClaim => byId[pendingClaim.claim_id]).forEach(pendingClaim => { delete pendingById[pendingClaim.claim_id]; }); return Object.assign({}, state, { isFetchingClaimListMine: false, myClaims: claims, byId, claimsByUri: byUri, pendingById }); }; reducers[FETCH_CHANNEL_LIST_STARTED] = state => Object.assign({}, state, { fetchingMyChannels: true }); reducers[FETCH_CHANNEL_LIST_COMPLETED] = (state, action) => { const { claims } = action.data; const myChannelClaims = new Set(state.myChannelClaims); const byId = Object.assign({}, state.byId); claims.forEach(claim => { myChannelClaims.add(claim.claim_id); byId[claim.claim_id] = claim; }); return Object.assign({}, state, { byId, fetchingMyChannels: false, myChannelClaims }); }; 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, page } = action.data; const claimsByChannel = Object.assign({}, state.claimsByChannel); const byChannel = Object.assign({}, claimsByChannel[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[`lbry://${claim.name}#${claim.claim_id}`] = claim.claim_id; }); } byChannel.all = allClaimIds; byChannel[page] = currentPageClaimIds; claimsByChannel[uri] = byChannel; delete fetchingChannelClaims[uri]; return Object.assign({}, state, { claimsByChannel, byId, fetchingChannelClaims, claimsByUri, 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[ABANDON_CLAIM_SUCCEEDED] = (state, action) => { const { claimId } = action.data; const byId = Object.assign({}, state.byId); const claimsByUri = Object.assign({}, state.claimsByUri); Object.keys(claimsByUri).forEach(uri => { if (claimsByUri[uri] === claimId) { delete claimsByUri[uri]; } }); delete byId[claimId]; return Object.assign({}, state, { byId, claimsByUri }); }; reducers[CREATE_CHANNEL_COMPLETED] = (state, action) => { const { channelClaim } = action.data; const byId = Object.assign({}, state.byId); const myChannelClaims = new Set(state.myChannelClaims); byId[channelClaim.claim_id] = channelClaim; myChannelClaims.add(channelClaim.claim_id); return Object.assign({}, state, { byId, myChannelClaims }); }; reducers[RESOLVE_URIS_STARTED] = (state, action) => { const { uris } = action.data; const oldResolving = state.resolvingUris || []; const newResolving = Object.assign([], oldResolving); uris.forEach(uri => { if (!newResolving.includes(uri)) { newResolving.push(uri); } }); return Object.assign({}, state, { resolvingUris: newResolving }); }; reducers[FETCH_CHANNEL_CLAIM_COUNT_COMPLETED] = (state, action) => { const channelClaimCounts = Object.assign({}, state.channelClaimCounts); const { uri, totalClaims } = action.data; channelClaimCounts[uri] = totalClaims; return Object.assign({}, state, { channelClaimCounts }); }; function claimsReducer(state = defaultState, action) { const handler = reducers[action.type]; if (handler) return handler(state, action); return state; } 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; }; const reducers$1 = {}; const defaultState$1 = { fileListPublishedSort: DATE_NEW, fileListDownloadedSort: DATE_NEW }; reducers$1[FILE_LIST_STARTED] = state => Object.assign({}, state, { isFetchingFileList: true }); reducers$1[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$1[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$1[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$1[DOWNLOADING_STARTED] = (state, action) => { const { uri, outpoint, fileInfo } = action.data; const newByOutpoint = Object.assign({}, state.byOutpoint); const newDownloading = Object.assign({}, state.downloadingByOutpoint); const newLoading = Object.assign({}, state.urisLoading); newDownloading[outpoint] = true; newByOutpoint[outpoint] = fileInfo; delete newLoading[uri]; return Object.assign({}, state, { downloadingByOutpoint: newDownloading, urisLoading: newLoading, byOutpoint: newByOutpoint }); }; reducers$1[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$1[DOWNLOADING_CANCELED] = (state, action) => { const { outpoint } = action.data; const newDownloading = Object.assign({}, state.downloadingByOutpoint); delete newDownloading[outpoint]; return Object.assign({}, state, { downloadingByOutpoint: newDownloading }); }; reducers$1[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$1[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$1[LOADING_VIDEO_STARTED] = (state, action) => { const { uri } = action.data; const newLoading = Object.assign({}, state.urisLoading); newLoading[uri] = true; const newErrors = _extends$2({}, state.errors); if (uri in newErrors) delete newErrors[uri]; return Object.assign({}, state, { urisLoading: newLoading, errors: _extends$2({}, newErrors) }); }; reducers$1[LOADING_VIDEO_FAILED] = (state, action) => { const { uri } = action.data; const newLoading = Object.assign({}, state.urisLoading); delete newLoading[uri]; const newErrors = _extends$2({}, state.errors); newErrors[uri] = true; return Object.assign({}, state, { urisLoading: newLoading, errors: _extends$2({}, newErrors) }); }; reducers$1[FETCH_DATE] = (state, action) => { const { time } = action.data; if (time) { return Object.assign({}, state, { publishedDate: time }); } return null; }; reducers$1[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$1, action) { const handler = reducers$1[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$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 defaultState$2 = { 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$3({}, state, { toasts: newToasts }); }, [DISMISS_TOAST]: state => { const newToasts = state.toasts.slice(); newToasts.shift(); return _extends$3({}, state, { toasts: newToasts }); }, // Notifications [CREATE_NOTIFICATION]: (state, action) => { const notification = action.data; const newNotifications = state.notifications.slice(); newNotifications.push(notification); return _extends$3({}, 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$3({}, state, { notifications }); }, [DELETE_NOTIFICATION]: (state, action) => { const { id } = action.data; let newNotifications = state.notifications.slice(); newNotifications = newNotifications.filter(notification => notification.id !== id); return _extends$3({}, state, { notifications: newNotifications }); }, // Errors [CREATE_ERROR]: (state, action) => { const error = action.data; const newErrors = state.errors.slice(); newErrors.push(error); return _extends$3({}, state, { errors: newErrors }); }, [DISMISS_ERROR]: state => { const newErrors = state.errors.slice(); newErrors.shift(); return _extends$3({}, state, { errors: newErrors }); } }, defaultState$2); 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; }; const defaultState$3 = { isActive: false, // does the user have any typed text in the search input focused: false, // is the search input focused searchQuery: '', // needs to be an empty string for input focusing options: { [SEARCH_OPTIONS.RESULT_COUNT]: 30, [SEARCH_OPTIONS.CLAIM_TYPE]: SEARCH_OPTIONS.INCLUDE_FILES_AND_CHANNELS, [SEARCH_OPTIONS.MEDIA_AUDIO]: true, [SEARCH_OPTIONS.MEDIA_VIDEO]: true, [SEARCH_OPTIONS.MEDIA_TEXT]: true, [SEARCH_OPTIONS.MEDIA_IMAGE]: true, [SEARCH_OPTIONS.MEDIA_APPLICATION]: true }, suggestions: {}, urisByQuery: {} }; const searchReducer = handleActions({ [SEARCH_START]: state => _extends$4({}, state, { searching: true }), [SEARCH_SUCCESS]: (state, action) => { const { query, uris } = action.data; return _extends$4({}, state, { searching: false, urisByQuery: Object.assign({}, state.urisByQuery, { [query]: uris }) }); }, [SEARCH_FAIL]: state => _extends$4({}, state, { searching: false }), [UPDATE_SEARCH_QUERY]: (state, action) => _extends$4({}, state, { searchQuery: action.data.query, isActive: true }), [UPDATE_SEARCH_SUGGESTIONS]: (state, action) => _extends$4({}, state, { suggestions: _extends$4({}, state.suggestions, { [action.data.query]: action.data.suggestions }) }), // clear the searchQuery on back/forward unless to search page [HISTORY_NAVIGATE]: (state, action) => { const { url } = action.data; return _extends$4({}, state, { searchQuery: url.indexOf('/search') === 0 ? url.slice(14) : '', isActive: url.indexOf('/search') === 0, suggestions: {} }); }, // sets isActive to false so the uri will be populated correctly if the // user is on a file page. The search query will still be present on any // other page [DISMISS_NOTIFICATION]: state => _extends$4({}, state, { isActive: false }), [SEARCH_FOCUS]: state => _extends$4({}, state, { focused: true }), [SEARCH_BLUR]: state => _extends$4({}, state, { focused: false }), [UPDATE_SEARCH_OPTIONS]: (state, action) => { const { options: oldOptions } = state; const newOptions = action.data; const options = _extends$4({}, oldOptions, newOptions); return _extends$4({}, state, { options }); } }, defaultState$3); // const reducers$2 = {}; 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$4 = { balance: undefined, blocks: {}, latestBlock: undefined, transactions: {}, fetchingTransactions: false, 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' }; reducers$2[FETCH_TRANSACTIONS_STARTED] = state => Object.assign({}, state, { fetchingTransactions: true }); reducers$2[FETCH_TRANSACTIONS_COMPLETED] = (state, action) => { const byId = Object.assign({}, state.transactions); const { transactions } = action.data; transactions.forEach(transaction => { byId[transaction.txid] = transaction; }); return Object.assign({}, state, { transactions: byId, fetchingTransactions: false }); }; reducers$2[GET_NEW_ADDRESS_STARTED] = state => Object.assign({}, state, { gettingNewAddress: true }); reducers$2[GET_NEW_ADDRESS_COMPLETED] = (state, action) => { const { address } = action.data; // Say no to localStorage! return Object.assign({}, state, { gettingNewAddress: false, receiveAddress: address }); }; reducers$2[UPDATE_BALANCE] = (state, action) => Object.assign({}, state, { balance: action.data.balance }); reducers$2[CHECK_ADDRESS_IS_MINE_STARTED] = state => Object.assign({}, state, { checkingAddressOwnership: true }); reducers$2[CHECK_ADDRESS_IS_MINE_COMPLETED] = state => Object.assign({}, state, { checkingAddressOwnership: false }); reducers$2[SET_DRAFT_TRANSACTION_AMOUNT] = (state, action) => { const oldDraft = state.draftTransaction; const newDraft = Object.assign({}, oldDraft, { amount: parseFloat(action.data.amount) }); return Object.assign({}, state, { draftTransaction: newDraft }); }; reducers$2[SET_DRAFT_TRANSACTION_ADDRESS] = (state, action) => { const oldDraft = state.draftTransaction; const newDraft = Object.assign({}, oldDraft, { address: action.data.address }); return Object.assign({}, state, { draftTransaction: newDraft }); }; reducers$2[SEND_TRANSACTION_STARTED] = state => { const newDraftTransaction = Object.assign({}, state.draftTransaction, { sending: true }); return Object.assign({}, state, { draftTransaction: newDraftTransaction }); }; reducers$2[SEND_TRANSACTION_COMPLETED] = state => Object.assign({}, state, { draftTransaction: buildDraftTransaction() }); reducers$2[SEND_TRANSACTION_FAILED] = (state, action) => { const newDraftTransaction = Object.assign({}, state.draftTransaction, { sending: false, error: action.data.error }); return Object.assign({}, state, { draftTransaction: newDraftTransaction }); }; reducers$2[SUPPORT_TRANSACTION_STARTED] = state => Object.assign({}, state, { sendingSupport: true }); reducers$2[SUPPORT_TRANSACTION_COMPLETED] = state => Object.assign({}, state, { sendingSupport: false }); reducers$2[SUPPORT_TRANSACTION_FAILED] = (state, action) => Object.assign({}, state, { error: action.data.error, sendingSupport: false }); reducers$2[FETCH_BLOCK_SUCCESS] = (state, action) => { const { block, block: { height } } = action.data; const blocks = Object.assign({}, state.blocks); blocks[height] = block; return Object.assign({}, state, { blocks }); }; reducers$2[WALLET_STATUS_COMPLETED] = (state, action) => Object.assign({}, state, { walletIsEncrypted: action.result }); reducers$2[WALLET_ENCRYPT_START] = state => Object.assign({}, state, { walletEncryptPending: true, walletEncryptSucceded: null, walletEncryptResult: null }); reducers$2[WALLET_ENCRYPT_COMPLETED] = (state, action) => Object.assign({}, state, { walletEncryptPending: false, walletEncryptSucceded: true, walletEncryptResult: action.result }); reducers$2[WALLET_ENCRYPT_FAILED] = (state, action) => Object.assign({}, state, { walletEncryptPending: false, walletEncryptSucceded: false, walletEncryptResult: action.result }); reducers$2[WALLET_DECRYPT_START] = state => Object.assign({}, state, { walletDecryptPending: true, walletDecryptSucceded: null, walletDecryptResult: null }); reducers$2[WALLET_DECRYPT_COMPLETED] = (state, action) => Object.assign({}, state, { walletDecryptPending: false, walletDecryptSucceded: true, walletDecryptResult: action.result }); reducers$2[WALLET_DECRYPT_FAILED] = (state, action) => Object.assign({}, state, { walletDecryptPending: false, walletDecryptSucceded: false, walletDecryptResult: action.result }); reducers$2[WALLET_UNLOCK_START] = state => Object.assign({}, state, { walletUnlockPending: true, walletUnlockSucceded: null, walletUnlockResult: null }); reducers$2[WALLET_UNLOCK_COMPLETED] = (state, action) => Object.assign({}, state, { walletUnlockPending: false, walletUnlockSucceded: true, walletUnlockResult: action.result }); reducers$2[WALLET_UNLOCK_FAILED] = (state, action) => Object.assign({}, state, { walletUnlockPending: false, walletUnlockSucceded: false, walletUnlockResult: action.result }); reducers$2[WALLET_LOCK_START] = state => Object.assign({}, state, { walletLockPending: false, walletLockSucceded: null, walletLockResult: null }); reducers$2[WALLET_LOCK_COMPLETED] = (state, action) => Object.assign({}, state, { walletLockPending: false, walletLockSucceded: true, walletLockResult: action.result }); reducers$2[WALLET_LOCK_FAILED] = (state, action) => Object.assign({}, state, { walletLockPending: false, walletLockSucceded: false, walletLockResult: action.result }); reducers$2[SET_TRANSACTION_LIST_FILTER] = (state, action) => Object.assign({}, state, { transactionListFilter: action.data }); reducers$2[UPDATE_CURRENT_HEIGHT] = (state, action) => Object.assign({}, state, { latestBlock: action.data }); function walletReducer(state = defaultState$4, action) { const handler = reducers$2[action.type]; if (handler) return handler(state, action); return state; } 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; }; const reducers$3 = {}; const defaultState$5 = { positions: {} }; reducers$3[SET_CONTENT_POSITION] = (state, action) => { const { claimId, outpoint, position } = action.data; return _extends$5({}, state, { positions: _extends$5({}, state.positions, { [claimId]: _extends$5({}, state.positions[claimId], { [outpoint]: position }) }) }); }; function contentReducer(state = defaultState$5, action) { const handler = reducers$3[action.type]; if (handler) return handler(state, action); return state; } const selectState$4 = state => state.content || {}; const makeSelectContentPositionForUri = uri => reselect.createSelector(selectState$4, 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$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; }; const selectState$5 = state => state.notifications || {}; const selectToast = reselect.createSelector(selectState$5, state => { if (state.toasts.length) { const { id, params } = state.toasts[0]; return _extends$6({ id }, params); } return null; }); const selectError = reselect.createSelector(selectState$5, state => { if (state.errors.length) { const { error } = state.errors[0]; return { error }; } return null; }); exports.ACTIONS = action_types; exports.Lbry = lbryProxy; exports.PAGES = pages; exports.SEARCH_OPTIONS = SEARCH_OPTIONS; exports.SEARCH_TYPES = SEARCH_TYPES; exports.SETTINGS = settings; exports.SORT_OPTIONS = sort_options; exports.THUMBNAIL_STATUSES = thumbnail_upload_statuses; exports.TRANSACTIONS = transaction_types; exports.batchActions = batchActions; exports.buildURI = buildURI; exports.claimsReducer = claimsReducer; exports.contentReducer = contentReducer; exports.convertToShareLink = convertToShareLink; exports.creditsToString = creditsToString; exports.doAbandonClaim = doAbandonClaim; exports.doBalanceSubscribe = doBalanceSubscribe; exports.doBlurSearchInput = doBlurSearchInput; exports.doCheckAddressIsMine = doCheckAddressIsMine; exports.doDismissError = doDismissError; exports.doDismissToast = doDismissToast; exports.doError = doError; exports.doFetchBlock = doFetchBlock; exports.doFetchClaimCountByChannel = doFetchClaimCountByChannel; exports.doFetchClaimListMine = doFetchClaimListMine; exports.doFetchClaimsByChannel = doFetchClaimsByChannel; exports.doFetchFileInfo = doFetchFileInfo; exports.doFetchFileInfosAndPublishedClaims = doFetchFileInfosAndPublishedClaims; exports.doFetchTransactions = doFetchTransactions; exports.doFileList = doFileList; exports.doFocusSearchInput = doFocusSearchInput; exports.doGetNewAddress = doGetNewAddress; exports.doResolveUri = doResolveUri; exports.doResolveUris = doResolveUris; exports.doSearch = doSearch; exports.doSendDraftTransaction = doSendDraftTransaction; exports.doSendTip = doSendTip; exports.doSetDraftTransactionAddress = doSetDraftTransactionAddress; exports.doSetDraftTransactionAmount = doSetDraftTransactionAmount; exports.doSetFileListSort = doSetFileListSort; exports.doSetTransactionListFilter = doSetTransactionListFilter; exports.doToast = doToast; exports.doUpdateBalance = doUpdateBalance; exports.doUpdateBlockHeight = doUpdateBlockHeight; exports.doUpdateSearchOptions = doUpdateSearchOptions; exports.doUpdateSearchQuery = doUpdateSearchQuery; exports.doWalletDecrypt = doWalletDecrypt; exports.doWalletEncrypt = doWalletEncrypt; exports.doWalletStatus = doWalletStatus; exports.doWalletUnlock = doWalletUnlock; exports.fileInfoReducer = fileInfoReducer; exports.formatCredits = formatCredits; exports.formatFullPrice = formatFullPrice; exports.isNameValid = isNameValid; exports.isURIClaimable = isURIClaimable; exports.isURIValid = isURIValid; exports.makeSelectBlockDate = makeSelectBlockDate; exports.makeSelectChannelForClaimUri = makeSelectChannelForClaimUri; exports.makeSelectClaimForUri = makeSelectClaimForUri; exports.makeSelectClaimIsMine = makeSelectClaimIsMine; exports.makeSelectClaimIsPending = makeSelectClaimIsPending; exports.makeSelectClaimsInChannelForCurrentPageState = makeSelectClaimsInChannelForCurrentPageState; exports.makeSelectClaimsInChannelForPage = makeSelectClaimsInChannelForPage; exports.makeSelectContentPositionForUri = makeSelectContentPositionForUri; exports.makeSelectContentTypeForUri = makeSelectContentTypeForUri; exports.makeSelectDownloadingForUri = makeSelectDownloadingForUri; exports.makeSelectFetchingChannelClaims = makeSelectFetchingChannelClaims; exports.makeSelectFileInfoForUri = makeSelectFileInfoForUri; exports.makeSelectFirstRecommendedFileForUri = makeSelectFirstRecommendedFileForUri; exports.makeSelectIsUriResolving = makeSelectIsUriResolving; exports.makeSelectLoadingForUri = makeSelectLoadingForUri; exports.makeSelectMetadataForUri = makeSelectMetadataForUri; exports.makeSelectNsfwCountForChannel = makeSelectNsfwCountForChannel; exports.makeSelectNsfwCountFromUris = makeSelectNsfwCountFromUris; exports.makeSelectPendingByUri = makeSelectPendingByUri; exports.makeSelectQueryWithOptions = makeSelectQueryWithOptions; exports.makeSelectRecommendedContentForUri = makeSelectRecommendedContentForUri; exports.makeSelectSearchUris = makeSelectSearchUris; exports.makeSelectTitleForUri = makeSelectTitleForUri; exports.makeSelectTotalItemsForChannel = makeSelectTotalItemsForChannel; exports.makeSelectTotalPagesForChannel = makeSelectTotalPagesForChannel; exports.normalizeURI = normalizeURI; exports.notificationsReducer = notificationsReducer; exports.parseQueryParams = parseQueryParams; exports.parseURI = parseURI; exports.regexAddress = regexAddress; exports.regexInvalidURI = regexInvalidURI; exports.savePosition = savePosition; exports.searchReducer = searchReducer; exports.selectAbandoningIds = selectAbandoningIds; exports.selectAllClaimsByChannel = selectAllClaimsByChannel; exports.selectAllFetchingChannelClaims = selectAllFetchingChannelClaims; exports.selectAllMyClaimsByOutpoint = selectAllMyClaimsByOutpoint; exports.selectBalance = selectBalance; exports.selectBlocks = selectBlocks; exports.selectChannelClaimCounts = selectChannelClaimCounts; exports.selectClaimsById = selectClaimsById; exports.selectClaimsByUri = selectClaimsByUri; exports.selectCurrentChannelPage = selectCurrentChannelPage; exports.selectDownloadingByOutpoint = selectDownloadingByOutpoint; exports.selectDownloadingFileInfos = selectDownloadingFileInfos; exports.selectDraftTransaction = selectDraftTransaction; exports.selectDraftTransactionAddress = selectDraftTransactionAddress; exports.selectDraftTransactionAmount = selectDraftTransactionAmount; exports.selectDraftTransactionError = selectDraftTransactionError; exports.selectError = selectError; exports.selectFetchingMyChannels = selectFetchingMyChannels; exports.selectFileInfosByOutpoint = selectFileInfosByOutpoint; exports.selectFileInfosDownloaded = selectFileInfosDownloaded; exports.selectFileListDownloadedSort = selectFileListDownloadedSort; exports.selectFileListPublishedSort = selectFileListPublishedSort; exports.selectGettingNewAddress = selectGettingNewAddress; exports.selectHasTransactions = selectHasTransactions; exports.selectIsFetchingClaimListMine = selectIsFetchingClaimListMine; exports.selectIsFetchingFileList = selectIsFetchingFileList; exports.selectIsFetchingFileListDownloadedOrPublished = selectIsFetchingFileListDownloadedOrPublished; exports.selectIsFetchingTransactions = selectIsFetchingTransactions; exports.selectIsSearching = selectIsSearching; exports.selectIsSendingSupport = selectIsSendingSupport; exports.selectMyActiveClaims = selectMyActiveClaims; exports.selectMyChannelClaims = selectMyChannelClaims; exports.selectMyClaims = selectMyClaims; exports.selectMyClaimsOutpoints = selectMyClaimsOutpoints; exports.selectMyClaimsRaw = selectMyClaimsRaw; exports.selectMyClaimsWithoutChannels = selectMyClaimsWithoutChannels; exports.selectPendingById = selectPendingById; exports.selectPendingClaims = selectPendingClaims; exports.selectPlayingUri = selectPlayingUri; exports.selectReceiveAddress = selectReceiveAddress; exports.selectRecentTransactions = selectRecentTransactions; exports.selectResolvingUris = selectResolvingUris; exports.selectSearchBarFocused = selectSearchBarFocused; exports.selectSearchDownloadUris = selectSearchDownloadUris; exports.selectSearchOptions = selectSearchOptions; exports.selectSearchState = selectState; exports.selectSearchSuggestions = selectSearchSuggestions; exports.selectSearchUrisByQuery = selectSearchUrisByQuery; exports.selectSearchValue = selectSearchValue; exports.selectToast = selectToast; exports.selectTotalDownloadProgress = selectTotalDownloadProgress; exports.selectTransactionItems = selectTransactionItems; exports.selectTransactionListFilter = selectTransactionListFilter; exports.selectTransactionsById = selectTransactionsById; exports.selectUrisLoading = selectUrisLoading; 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.setSearchApi = setSearchApi; exports.toQueryString = toQueryString; exports.walletReducer = walletReducer;