4779 lines
154 KiB
JavaScript
4779 lines
154 KiB
JavaScript
'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 MINIMUM_PUBLISH_BID = 0.00000001;
|
|
|
|
const CHANNEL_ANONYMOUS = 'anonymous';
|
|
const CHANNEL_NEW = 'new';
|
|
const PAGE_SIZE = 20;
|
|
|
|
var claim = /*#__PURE__*/Object.freeze({
|
|
MINIMUM_PUBLISH_BID: MINIMUM_PUBLISH_BID,
|
|
CHANNEL_ANONYMOUS: CHANNEL_ANONYMOUS,
|
|
CHANNEL_NEW: CHANNEL_NEW,
|
|
PAGE_SIZE: PAGE_SIZE
|
|
});
|
|
|
|
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 FETCH_SUPPORTS_STARTED = 'FETCH_SUPPORTS_STARTED';
|
|
const FETCH_SUPPORTS_COMPLETED = 'FETCH_SUPPORTS_COMPLETED';
|
|
const ABANDON_SUPPORT_STARTED = 'ABANDON_SUPPORT_STARTED';
|
|
const ABANDON_SUPPORT_COMPLETED = 'ABANDON_SUPPORT_COMPLETED';
|
|
const UPDATE_BALANCE = 'UPDATE_BALANCE';
|
|
const UPDATE_TOTAL_BALANCE = 'UPDATE_TOTAL_BALANCE';
|
|
const CHECK_ADDRESS_IS_MINE_STARTED = 'CHECK_ADDRESS_IS_MINE_STARTED';
|
|
const CHECK_ADDRESS_IS_MINE_COMPLETED = 'CHECK_ADDRESS_IS_MINE_COMPLETED';
|
|
const SEND_TRANSACTION_STARTED = 'SEND_TRANSACTION_STARTED';
|
|
const SEND_TRANSACTION_COMPLETED = 'SEND_TRANSACTION_COMPLETED';
|
|
const SEND_TRANSACTION_FAILED = 'SEND_TRANSACTION_FAILED';
|
|
const SUPPORT_TRANSACTION_STARTED = 'SUPPORT_TRANSACTION_STARTED';
|
|
const SUPPORT_TRANSACTION_COMPLETED = 'SUPPORT_TRANSACTION_COMPLETED';
|
|
const SUPPORT_TRANSACTION_FAILED = 'SUPPORT_TRANSACTION_FAILED';
|
|
const 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';
|
|
const SET_DRAFT_TRANSACTION_AMOUNT = 'SET_DRAFT_TRANSACTION_AMOUNT';
|
|
const SET_DRAFT_TRANSACTION_ADDRESS = 'SET_DRAFT_TRANSACTION_ADDRESS';
|
|
|
|
// Claims
|
|
const RESOLVE_URIS_STARTED = 'RESOLVE_URIS_STARTED';
|
|
const RESOLVE_URIS_COMPLETED = 'RESOLVE_URIS_COMPLETED';
|
|
const FETCH_CHANNEL_CLAIMS_STARTED = 'FETCH_CHANNEL_CLAIMS_STARTED';
|
|
const FETCH_CHANNEL_CLAIMS_COMPLETED = 'FETCH_CHANNEL_CLAIMS_COMPLETED';
|
|
const FETCH_CLAIM_LIST_MINE_STARTED = 'FETCH_CLAIM_LIST_MINE_STARTED';
|
|
const FETCH_CLAIM_LIST_MINE_COMPLETED = 'FETCH_CLAIM_LIST_MINE_COMPLETED';
|
|
const ABANDON_CLAIM_STARTED = 'ABANDON_CLAIM_STARTED';
|
|
const ABANDON_CLAIM_SUCCEEDED = 'ABANDON_CLAIM_SUCCEEDED';
|
|
const FETCH_CHANNEL_LIST_STARTED = 'FETCH_CHANNEL_LIST_STARTED';
|
|
const FETCH_CHANNEL_LIST_COMPLETED = 'FETCH_CHANNEL_LIST_COMPLETED';
|
|
const CREATE_CHANNEL_STARTED = 'CREATE_CHANNEL_STARTED';
|
|
const CREATE_CHANNEL_COMPLETED = 'CREATE_CHANNEL_COMPLETED';
|
|
const CREATE_CHANNEL_FAILED = 'CREATE_CHANNEL_FAILED';
|
|
const PUBLISH_STARTED = 'PUBLISH_STARTED';
|
|
const PUBLISH_COMPLETED = 'PUBLISH_COMPLETED';
|
|
const PUBLISH_FAILED = 'PUBLISH_FAILED';
|
|
const SET_PLAYING_URI = 'SET_PLAYING_URI';
|
|
const SET_CONTENT_POSITION = 'SET_CONTENT_POSITION';
|
|
const SET_CONTENT_LAST_VIEWED = 'SET_CONTENT_LAST_VIEWED';
|
|
const CLEAR_CONTENT_HISTORY_URI = 'CLEAR_CONTENT_HISTORY_URI';
|
|
const CLEAR_CONTENT_HISTORY_ALL = 'CLEAR_CONTENT_HISTORY_ALL';
|
|
const CLAIM_SEARCH_STARTED = 'CLAIM_SEARCH_STARTED';
|
|
const CLAIM_SEARCH_COMPLETED = 'CLAIM_SEARCH_COMPLETED';
|
|
const CLAIM_SEARCH_FAILED = 'CLAIM_SEARCH_FAILED';
|
|
|
|
// Comments
|
|
const COMMENT_LIST_STARTED = 'COMMENT_LIST_STARTED';
|
|
const COMMENT_LIST_COMPLETED = 'COMMENT_LIST_COMPLETED';
|
|
const COMMENT_LIST_FAILED = 'COMMENT_LIST_FAILED';
|
|
const COMMENT_CREATE_STARTED = 'COMMENT_CREATE_STARTED';
|
|
const COMMENT_CREATE_COMPLETED = 'COMMENT_CREATE_COMPLETED';
|
|
const COMMENT_CREATE_FAILED = 'COMMENT_CREATE_FAILED';
|
|
|
|
// 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';
|
|
const PURCHASE_URI_STARTED = 'PURCHASE_URI_STARTED';
|
|
const PURCHASE_URI_COMPLETED = 'PURCHASE_URI_COMPLETED';
|
|
const PURCHASE_URI_FAILED = 'PURCHASE_URI_FAILED';
|
|
const DELETE_PURCHASED_URI = 'DELETE_PURCHASED_URI';
|
|
const LOADING_FILE_STARTED = 'LOADING_FILE_STARTED';
|
|
const LOADING_FILE_COMPLETED = 'LOADING_FILE_COMPLETED';
|
|
const LOADING_FILE_FAILED = 'LOADING_FILE_FAILED';
|
|
|
|
// 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';
|
|
|
|
// Subscriptions
|
|
const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
|
|
const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
|
|
const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
|
|
const SET_SUBSCRIPTION_LATEST = 'SET_SUBSCRIPTION_LATEST';
|
|
const SET_SUBSCRIPTION_NOTIFICATION = 'SET_SUBSCRIPTION_NOTIFICATION';
|
|
const SET_SUBSCRIPTION_NOTIFICATIONS = 'SET_SUBSCRIPTION_NOTIFICATIONS';
|
|
const CHECK_SUBSCRIPTION_STARTED = 'CHECK_SUBSCRIPTION_STARTED';
|
|
const CHECK_SUBSCRIPTION_COMPLETED = 'CHECK_SUBSCRIPTION_COMPLETED';
|
|
const CHECK_SUBSCRIPTIONS_SUBSCRIBE = 'CHECK_SUBSCRIPTIONS_SUBSCRIBE';
|
|
|
|
// Publishing
|
|
const CLEAR_PUBLISH = 'CLEAR_PUBLISH';
|
|
const UPDATE_PUBLISH_FORM = 'UPDATE_PUBLISH_FORM';
|
|
const PUBLISH_START = 'PUBLISH_START';
|
|
const PUBLISH_SUCCESS = 'PUBLISH_SUCCESS';
|
|
const PUBLISH_FAIL = 'PUBLISH_FAIL';
|
|
const CLEAR_PUBLISH_ERROR = 'CLEAR_PUBLISH_ERROR';
|
|
const REMOVE_PENDING_PUBLISH = 'REMOVE_PENDING_PUBLISH';
|
|
const DO_PREPARE_EDIT = 'DO_PREPARE_EDIT';
|
|
|
|
// Notifications
|
|
const CREATE_NOTIFICATION = 'CREATE_NOTIFICATION';
|
|
const EDIT_NOTIFICATION = 'EDIT_NOTIFICATION';
|
|
const DELETE_NOTIFICATION = 'DELETE_NOTIFICATION';
|
|
const DISMISS_NOTIFICATION = 'DISMISS_NOTIFICATION';
|
|
const CREATE_TOAST = 'CREATE_TOAST';
|
|
const DISMISS_TOAST = 'DISMISS_TOAST';
|
|
const CREATE_ERROR = 'CREATE_ERROR';
|
|
const DISMISS_ERROR = 'DISMISS_ERROR';
|
|
|
|
const FETCH_DATE = 'FETCH_DATE';
|
|
|
|
// Cost info
|
|
const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED';
|
|
const FETCH_COST_INFO_COMPLETED = 'FETCH_COST_INFO_COMPLETED';
|
|
const FETCH_COST_INFO_FAILED = 'FETCH_COST_INFO_FAILED';
|
|
// Tags
|
|
const TOGGLE_TAG_FOLLOW = 'TOGGLE_TAG_FOLLOW';
|
|
const TAG_ADD = 'TAG_ADD';
|
|
const TAG_DELETE = 'TAG_DELETE';
|
|
|
|
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,
|
|
FETCH_SUPPORTS_STARTED: FETCH_SUPPORTS_STARTED,
|
|
FETCH_SUPPORTS_COMPLETED: FETCH_SUPPORTS_COMPLETED,
|
|
ABANDON_SUPPORT_STARTED: ABANDON_SUPPORT_STARTED,
|
|
ABANDON_SUPPORT_COMPLETED: ABANDON_SUPPORT_COMPLETED,
|
|
UPDATE_BALANCE: UPDATE_BALANCE,
|
|
UPDATE_TOTAL_BALANCE: UPDATE_TOTAL_BALANCE,
|
|
CHECK_ADDRESS_IS_MINE_STARTED: CHECK_ADDRESS_IS_MINE_STARTED,
|
|
CHECK_ADDRESS_IS_MINE_COMPLETED: CHECK_ADDRESS_IS_MINE_COMPLETED,
|
|
SEND_TRANSACTION_STARTED: SEND_TRANSACTION_STARTED,
|
|
SEND_TRANSACTION_COMPLETED: SEND_TRANSACTION_COMPLETED,
|
|
SEND_TRANSACTION_FAILED: SEND_TRANSACTION_FAILED,
|
|
SUPPORT_TRANSACTION_STARTED: SUPPORT_TRANSACTION_STARTED,
|
|
SUPPORT_TRANSACTION_COMPLETED: SUPPORT_TRANSACTION_COMPLETED,
|
|
SUPPORT_TRANSACTION_FAILED: SUPPORT_TRANSACTION_FAILED,
|
|
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,
|
|
SET_DRAFT_TRANSACTION_AMOUNT: SET_DRAFT_TRANSACTION_AMOUNT,
|
|
SET_DRAFT_TRANSACTION_ADDRESS: SET_DRAFT_TRANSACTION_ADDRESS,
|
|
RESOLVE_URIS_STARTED: RESOLVE_URIS_STARTED,
|
|
RESOLVE_URIS_COMPLETED: RESOLVE_URIS_COMPLETED,
|
|
FETCH_CHANNEL_CLAIMS_STARTED: FETCH_CHANNEL_CLAIMS_STARTED,
|
|
FETCH_CHANNEL_CLAIMS_COMPLETED: FETCH_CHANNEL_CLAIMS_COMPLETED,
|
|
FETCH_CLAIM_LIST_MINE_STARTED: FETCH_CLAIM_LIST_MINE_STARTED,
|
|
FETCH_CLAIM_LIST_MINE_COMPLETED: FETCH_CLAIM_LIST_MINE_COMPLETED,
|
|
ABANDON_CLAIM_STARTED: ABANDON_CLAIM_STARTED,
|
|
ABANDON_CLAIM_SUCCEEDED: ABANDON_CLAIM_SUCCEEDED,
|
|
FETCH_CHANNEL_LIST_STARTED: FETCH_CHANNEL_LIST_STARTED,
|
|
FETCH_CHANNEL_LIST_COMPLETED: FETCH_CHANNEL_LIST_COMPLETED,
|
|
CREATE_CHANNEL_STARTED: CREATE_CHANNEL_STARTED,
|
|
CREATE_CHANNEL_COMPLETED: CREATE_CHANNEL_COMPLETED,
|
|
CREATE_CHANNEL_FAILED: CREATE_CHANNEL_FAILED,
|
|
PUBLISH_STARTED: PUBLISH_STARTED,
|
|
PUBLISH_COMPLETED: PUBLISH_COMPLETED,
|
|
PUBLISH_FAILED: PUBLISH_FAILED,
|
|
SET_PLAYING_URI: SET_PLAYING_URI,
|
|
SET_CONTENT_POSITION: SET_CONTENT_POSITION,
|
|
SET_CONTENT_LAST_VIEWED: SET_CONTENT_LAST_VIEWED,
|
|
CLEAR_CONTENT_HISTORY_URI: CLEAR_CONTENT_HISTORY_URI,
|
|
CLEAR_CONTENT_HISTORY_ALL: CLEAR_CONTENT_HISTORY_ALL,
|
|
CLAIM_SEARCH_STARTED: CLAIM_SEARCH_STARTED,
|
|
CLAIM_SEARCH_COMPLETED: CLAIM_SEARCH_COMPLETED,
|
|
CLAIM_SEARCH_FAILED: CLAIM_SEARCH_FAILED,
|
|
COMMENT_LIST_STARTED: COMMENT_LIST_STARTED,
|
|
COMMENT_LIST_COMPLETED: COMMENT_LIST_COMPLETED,
|
|
COMMENT_LIST_FAILED: COMMENT_LIST_FAILED,
|
|
COMMENT_CREATE_STARTED: COMMENT_CREATE_STARTED,
|
|
COMMENT_CREATE_COMPLETED: COMMENT_CREATE_COMPLETED,
|
|
COMMENT_CREATE_FAILED: COMMENT_CREATE_FAILED,
|
|
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,
|
|
PURCHASE_URI_STARTED: PURCHASE_URI_STARTED,
|
|
PURCHASE_URI_COMPLETED: PURCHASE_URI_COMPLETED,
|
|
PURCHASE_URI_FAILED: PURCHASE_URI_FAILED,
|
|
DELETE_PURCHASED_URI: DELETE_PURCHASED_URI,
|
|
LOADING_FILE_STARTED: LOADING_FILE_STARTED,
|
|
LOADING_FILE_COMPLETED: LOADING_FILE_COMPLETED,
|
|
LOADING_FILE_FAILED: LOADING_FILE_FAILED,
|
|
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,
|
|
CHANNEL_SUBSCRIBE: CHANNEL_SUBSCRIBE,
|
|
CHANNEL_UNSUBSCRIBE: CHANNEL_UNSUBSCRIBE,
|
|
HAS_FETCHED_SUBSCRIPTIONS: HAS_FETCHED_SUBSCRIPTIONS,
|
|
SET_SUBSCRIPTION_LATEST: SET_SUBSCRIPTION_LATEST,
|
|
SET_SUBSCRIPTION_NOTIFICATION: SET_SUBSCRIPTION_NOTIFICATION,
|
|
SET_SUBSCRIPTION_NOTIFICATIONS: SET_SUBSCRIPTION_NOTIFICATIONS,
|
|
CHECK_SUBSCRIPTION_STARTED: CHECK_SUBSCRIPTION_STARTED,
|
|
CHECK_SUBSCRIPTION_COMPLETED: CHECK_SUBSCRIPTION_COMPLETED,
|
|
CHECK_SUBSCRIPTIONS_SUBSCRIBE: CHECK_SUBSCRIPTIONS_SUBSCRIBE,
|
|
CLEAR_PUBLISH: CLEAR_PUBLISH,
|
|
UPDATE_PUBLISH_FORM: UPDATE_PUBLISH_FORM,
|
|
PUBLISH_START: PUBLISH_START,
|
|
PUBLISH_SUCCESS: PUBLISH_SUCCESS,
|
|
PUBLISH_FAIL: PUBLISH_FAIL,
|
|
CLEAR_PUBLISH_ERROR: CLEAR_PUBLISH_ERROR,
|
|
REMOVE_PENDING_PUBLISH: REMOVE_PENDING_PUBLISH,
|
|
DO_PREPARE_EDIT: DO_PREPARE_EDIT,
|
|
CREATE_NOTIFICATION: CREATE_NOTIFICATION,
|
|
EDIT_NOTIFICATION: EDIT_NOTIFICATION,
|
|
DELETE_NOTIFICATION: DELETE_NOTIFICATION,
|
|
DISMISS_NOTIFICATION: DISMISS_NOTIFICATION,
|
|
CREATE_TOAST: CREATE_TOAST,
|
|
DISMISS_TOAST: DISMISS_TOAST,
|
|
CREATE_ERROR: CREATE_ERROR,
|
|
DISMISS_ERROR: DISMISS_ERROR,
|
|
FETCH_DATE: FETCH_DATE,
|
|
FETCH_COST_INFO_STARTED: FETCH_COST_INFO_STARTED,
|
|
FETCH_COST_INFO_COMPLETED: FETCH_COST_INFO_COMPLETED,
|
|
FETCH_COST_INFO_FAILED: FETCH_COST_INFO_FAILED,
|
|
TOGGLE_TAG_FOLLOW: TOGGLE_TAG_FOLLOW,
|
|
TAG_ADD: TAG_ADD,
|
|
TAG_DELETE: TAG_DELETE
|
|
});
|
|
|
|
const CC_LICENSES = [{
|
|
value: 'Creative Commons Attribution 4.0 International',
|
|
url: 'https://creativecommons.org/licenses/by/4.0/legalcode'
|
|
}, {
|
|
value: 'Creative Commons Attribution-ShareAlike 4.0 International',
|
|
url: 'https://creativecommons.org/licenses/by-sa/4.0/legalcode'
|
|
}, {
|
|
value: 'Creative Commons Attribution-NoDerivatives 4.0 International',
|
|
url: 'https://creativecommons.org/licenses/by-nd/4.0/legalcode'
|
|
}, {
|
|
value: 'Creative Commons Attribution-NonCommercial 4.0 International',
|
|
url: 'https://creativecommons.org/licenses/by-nc/4.0/legalcode'
|
|
}, {
|
|
value: 'Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International',
|
|
url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode'
|
|
}, {
|
|
value: 'Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International',
|
|
url: 'https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode'
|
|
}];
|
|
|
|
const NONE = 'None';
|
|
const PUBLIC_DOMAIN = 'Public Domain';
|
|
const OTHER = 'other';
|
|
const COPYRIGHT = 'copyright';
|
|
|
|
var licenses = /*#__PURE__*/Object.freeze({
|
|
CC_LICENSES: CC_LICENSES,
|
|
NONE: NONE,
|
|
PUBLIC_DOMAIN: PUBLIC_DOMAIN,
|
|
OTHER: OTHER,
|
|
COPYRIGHT: COPYRIGHT
|
|
});
|
|
|
|
const AUTH = 'auth';
|
|
const BACKUP = 'backup';
|
|
const CHANNEL = 'channel';
|
|
const DISCOVER = 'discover';
|
|
const FILE = 'file';
|
|
const DOWNLOADED = 'downloaded';
|
|
const PUBLISHED = 'published';
|
|
const GET_CREDITS = 'getcredits';
|
|
const HELP = 'help';
|
|
const INVITE = 'invite';
|
|
const PUBLISH = 'publish';
|
|
const REPORT = 'report';
|
|
const REWARDS = 'rewards';
|
|
const SEARCH = 'search';
|
|
const SEND_CREDITS = 'send';
|
|
const SETTINGS = 'settings';
|
|
const SHOW = 'show';
|
|
const SUBSCRIPTIONS = 'subscriptions';
|
|
const TRANSACTION_HISTORY = 'history';
|
|
const HISTORY = 'user_history';
|
|
const WALLET = 'wallet';
|
|
|
|
var pages = /*#__PURE__*/Object.freeze({
|
|
AUTH: AUTH,
|
|
BACKUP: BACKUP,
|
|
CHANNEL: CHANNEL,
|
|
DISCOVER: DISCOVER,
|
|
FILE: FILE,
|
|
DOWNLOADED: DOWNLOADED,
|
|
PUBLISHED: PUBLISHED,
|
|
GET_CREDITS: GET_CREDITS,
|
|
HELP: HELP,
|
|
INVITE: INVITE,
|
|
PUBLISH: PUBLISH,
|
|
REPORT: REPORT,
|
|
REWARDS: REWARDS,
|
|
SEARCH: SEARCH,
|
|
SEND_CREDITS: SEND_CREDITS,
|
|
SETTINGS: SETTINGS,
|
|
SHOW: SHOW,
|
|
SUBSCRIPTIONS: SUBSCRIPTIONS,
|
|
TRANSACTION_HISTORY: TRANSACTION_HISTORY,
|
|
HISTORY: HISTORY,
|
|
WALLET: WALLET
|
|
});
|
|
|
|
/* hardcoded names still exist for these in reducers/settings.js - only discovered when debugging */
|
|
/* Many SETTINGS are stored in the localStorage by their name -
|
|
be careful about changing the value of a SETTINGS constant, as doing so can invalidate existing SETTINGS */
|
|
const 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
|
|
});
|
|
|
|
const DATE_NEW = 'dateNew';
|
|
const DATE_OLD = 'dateOld';
|
|
const TITLE = 'title';
|
|
const FILENAME = 'filename';
|
|
|
|
var sort_options = /*#__PURE__*/Object.freeze({
|
|
DATE_NEW: DATE_NEW,
|
|
DATE_OLD: DATE_OLD,
|
|
TITLE: TITLE,
|
|
FILENAME: FILENAME
|
|
});
|
|
|
|
const API_DOWN = 'apiDown';
|
|
const READY = 'ready';
|
|
const IN_PROGRESS = 'inProgress';
|
|
const COMPLETE = 'complete';
|
|
const MANUAL = 'manual';
|
|
|
|
var thumbnail_upload_statuses = /*#__PURE__*/Object.freeze({
|
|
API_DOWN: API_DOWN,
|
|
READY: READY,
|
|
IN_PROGRESS: IN_PROGRESS,
|
|
COMPLETE: COMPLETE,
|
|
MANUAL: MANUAL
|
|
});
|
|
|
|
// eslint-disable-next-line import/prefer-default-export
|
|
const ALL = 'all';
|
|
const SPEND = 'spend';
|
|
const RECEIVE = 'receive';
|
|
const PUBLISH$1 = 'publish';
|
|
const CHANNEL$1 = 'channel';
|
|
const TIP = 'tip';
|
|
const SUPPORT = 'support';
|
|
const UPDATE = 'update';
|
|
const ABANDON = 'abandon';
|
|
|
|
var transaction_types = /*#__PURE__*/Object.freeze({
|
|
ALL: ALL,
|
|
SPEND: SPEND,
|
|
RECEIVE: RECEIVE,
|
|
PUBLISH: PUBLISH$1,
|
|
CHANNEL: CHANNEL$1,
|
|
TIP: TIP,
|
|
SUPPORT: SUPPORT,
|
|
UPDATE: UPDATE,
|
|
ABANDON: ABANDON
|
|
});
|
|
|
|
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;
|
|
//
|
|
// Basic LBRY sdk connection config
|
|
// Offers a proxy to call LBRY sdk methods
|
|
|
|
//
|
|
const Lbry = {
|
|
isConnected: false,
|
|
connectPromise: null,
|
|
daemonConnectionString: 'http://localhost:5279',
|
|
apiRequestHeaders: { 'Content-Type': 'application/json-rpc' },
|
|
|
|
// Allow overriding daemon connection string (e.g. to `/api/proxy` for lbryweb)
|
|
setDaemonConnectionString: value => {
|
|
Lbry.daemonConnectionString = value;
|
|
},
|
|
|
|
setApiHeader: (key, value) => {
|
|
Lbry.apiRequestHeaders = Object.assign(Lbry.apiRequestHeaders, { [key]: value });
|
|
},
|
|
|
|
unsetApiHeader: key => {
|
|
Object.keys(Lbry.apiRequestHeaders).includes(key) && delete Lbry.apiRequestHeaders['key'];
|
|
},
|
|
// Allow overriding Lbry methods
|
|
overrides: {},
|
|
setOverride: (methodName, newMethod) => {
|
|
Lbry.overrides[methodName] = newMethod;
|
|
},
|
|
|
|
// Returns a human readable media type based on the content type or extension of a file that is returned by the sdk
|
|
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) {
|
|
// $FlowFixMe
|
|
return (/^[^/]+/.exec(contentType)[0]
|
|
);
|
|
}
|
|
return 'unknown';
|
|
},
|
|
|
|
//
|
|
// Lbry SDK Methods
|
|
// https://lbry.tech/api/sdk
|
|
//
|
|
status: (params = {}) => daemonCallWithResult('status', params),
|
|
stop: () => daemonCallWithResult('stop', {}),
|
|
version: () => daemonCallWithResult('version', {}),
|
|
|
|
// Claim fetching and manipulation
|
|
resolve: params => daemonCallWithResult('resolve', params),
|
|
get: params => daemonCallWithResult('get', params),
|
|
publish: params => daemonCallWithResult('publish', params),
|
|
claim_search: params => daemonCallWithResult('claim_search', params),
|
|
claim_list: params => daemonCallWithResult('claim_list', params),
|
|
channel_create: params => daemonCallWithResult('channel_create', params),
|
|
channel_list: params => daemonCallWithResult('channel_list', params),
|
|
stream_abandon: params => daemonCallWithResult('stream_abandon', params),
|
|
channel_abandon: params => daemonCallWithResult('channel_abandon', params),
|
|
support_create: params => daemonCallWithResult('support_create', params),
|
|
|
|
// File fetching and manipulation
|
|
file_list: (params = {}) => daemonCallWithResult('file_list', params),
|
|
file_delete: (params = {}) => daemonCallWithResult('file_delete', params),
|
|
file_set_status: (params = {}) => daemonCallWithResult('file_set_status', params),
|
|
blob_delete: (params = {}) => daemonCallWithResult('blob_delete', params),
|
|
blob_list: (params = {}) => daemonCallWithResult('blob_list', params),
|
|
|
|
// Wallet utilities
|
|
account_balance: (params = {}) => daemonCallWithResult('account_balance', params),
|
|
account_decrypt: () => daemonCallWithResult('account_decrypt', {}),
|
|
account_encrypt: (params = {}) => daemonCallWithResult('account_encrypt', params),
|
|
account_unlock: (params = {}) => daemonCallWithResult('account_unlock', params),
|
|
account_list: (params = {}) => daemonCallWithResult('account_list', params),
|
|
account_send: (params = {}) => daemonCallWithResult('account_send', params),
|
|
account_set: (params = {}) => daemonCallWithResult('account_set', params),
|
|
address_is_mine: (params = {}) => daemonCallWithResult('address_is_mine', params),
|
|
address_unused: (params = {}) => daemonCallWithResult('address_unused', params),
|
|
transaction_list: (params = {}) => daemonCallWithResult('transaction_list', params),
|
|
utxo_release: (params = {}) => daemonCallWithResult('utxo_release', params),
|
|
support_abandon: (params = {}) => daemonCallWithResult('support_abandon', params),
|
|
|
|
sync_hash: (params = {}) => daemonCallWithResult('sync_hash', params),
|
|
sync_apply: (params = {}) => daemonCallWithResult('sync_apply', params),
|
|
|
|
// Comments
|
|
comment_list: (params = {}) => daemonCallWithResult('comment_list', params),
|
|
comment_create: (params = {}) => daemonCallWithResult('comment_create', params),
|
|
// Connect to the sdk
|
|
connect: () => {
|
|
if (Lbry.connectPromise === null) {
|
|
Lbry.connectPromise = new Promise((resolve, reject) => {
|
|
let tryNum = 0;
|
|
// Check every half second to see if the daemon is accepting connections
|
|
function checkDaemonStarted() {
|
|
tryNum += 1;
|
|
Lbry.status().then(resolve).catch(() => {
|
|
if (tryNum <= CHECK_DAEMON_STARTED_TRY_NUMBER) {
|
|
setTimeout(checkDaemonStarted, tryNum < 50 ? 400 : 1000);
|
|
} else {
|
|
reject(new Error('Unable to connect to LBRY'));
|
|
}
|
|
});
|
|
}
|
|
|
|
checkDaemonStarted();
|
|
});
|
|
}
|
|
|
|
// Flow thinks this could be empty, but it will always reuturn a promise
|
|
// $FlowFixMe
|
|
return Lbry.connectPromise;
|
|
}
|
|
};
|
|
|
|
function checkAndParse(response) {
|
|
if (response.status >= 200 && response.status < 300) {
|
|
return response.json();
|
|
}
|
|
return response.json().then(json => {
|
|
let error;
|
|
if (json.error) {
|
|
const errorMessage = typeof json.error === 'object' ? json.error.message : json.error;
|
|
error = new Error(errorMessage);
|
|
} else {
|
|
error = new Error('Protocol error with unknown response signature');
|
|
}
|
|
return Promise.reject(error);
|
|
});
|
|
}
|
|
|
|
function apiCall(method, params, resolve, reject) {
|
|
const counter = new Date().getTime();
|
|
const options = {
|
|
method: 'POST',
|
|
headers: Lbry.apiRequestHeaders,
|
|
body: JSON.stringify({
|
|
jsonrpc: '2.0',
|
|
method,
|
|
params,
|
|
id: counter
|
|
})
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
function daemonCallWithResult(name, params = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
apiCall(name, params, result => {
|
|
resolve(result);
|
|
}, reject);
|
|
});
|
|
}
|
|
|
|
// This is only for a fallback
|
|
// If there is a Lbry method that is being called by an app, it should be added to /flow-typed/Lbry.js
|
|
const lbryProxy = new Proxy(Lbry, {
|
|
get(target, name) {
|
|
if (name in target) {
|
|
return target[name];
|
|
}
|
|
|
|
return (params = {}) => new Promise((resolve, reject) => {
|
|
apiCall(name, params, resolve, reject);
|
|
});
|
|
}
|
|
});
|
|
|
|
//
|
|
|
|
const 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) {
|
|
const claimType = options[SEARCH_OPTIONS.CLAIM_TYPE];
|
|
queryParams.push(`claimType=${claimType}`);
|
|
|
|
// If they are only searching for channels, strip out the media info
|
|
if (!claimType.includes(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;
|
|
|
|
// see https://spec.lbry.com/#urls
|
|
const regexInvalidURI = exports.regexInvalidURI = /[=&#:$@%?\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/gu;
|
|
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) {
|
|
console.error(__('LBRY URIs must include a protocol prefix (lbry://).'));
|
|
return {};
|
|
}
|
|
|
|
// Validate and process name
|
|
if (!claimName) {
|
|
console.error(__('URI does not include name.'));
|
|
return {};
|
|
}
|
|
|
|
const isChannel = claimName.startsWith('@');
|
|
const channelName = isChannel ? claimName.slice(1) : claimName;
|
|
|
|
if (isChannel) {
|
|
if (!channelName) {
|
|
console.error(__('No channel name after @.'));
|
|
return {};
|
|
}
|
|
|
|
if (channelName.length < channelNameMinLength) {
|
|
console.error(__(`Channel names must be at least %s characters.`, channelNameMinLength));
|
|
return {};
|
|
}
|
|
|
|
contentName = path;
|
|
}
|
|
|
|
const nameBadChars = (channelName || claimName).match(regexInvalidURI);
|
|
if (nameBadChars) {
|
|
console.error(__(`Invalid character %s in name: %s.`, nameBadChars.length === 1 ? '' : 's', nameBadChars.join(', ')));
|
|
return {};
|
|
}
|
|
|
|
// Validate and process modifier (claim ID, bid position or claim sequence)
|
|
let claimId;
|
|
let claimSequence;
|
|
let bidPosition;
|
|
if (modSep) {
|
|
if (!modVal) {
|
|
console.error(__(`No modifier provided after separator %s.`, modSep));
|
|
return {};
|
|
}
|
|
|
|
if (modSep === '#') {
|
|
claimId = modVal;
|
|
} else if (modSep === ':') {
|
|
claimSequence = modVal;
|
|
} else if (modSep === '$') {
|
|
bidPosition = modVal;
|
|
}
|
|
}
|
|
|
|
if (claimId && (claimId.length > claimIdMaxLength || !claimId.match(/^[0-9a-f]+$/))) {
|
|
console.error(__(`Invalid claim ID %s.`, claimId));
|
|
return {};
|
|
}
|
|
|
|
if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) {
|
|
console.error(__('Claim sequence must be a number.'));
|
|
return {};
|
|
}
|
|
|
|
if (bidPosition && !bidPosition.match(/^-?[1-9][0-9]*$/)) {
|
|
console.error(__('Bid position must be a number.'));
|
|
return {};
|
|
}
|
|
|
|
// Validate and process path
|
|
if (path) {
|
|
if (!isChannel) {
|
|
console.error(__('Only channel URIs may have a path.'));
|
|
return {};
|
|
}
|
|
|
|
const pathBadChars = path.match(regexInvalidURI);
|
|
if (pathBadChars) {
|
|
console.error(__(`Invalid character in path: %s`, pathBadChars.join(', ')));
|
|
return {};
|
|
}
|
|
|
|
contentName = path;
|
|
} else if (pathSep) {
|
|
console.error(__('No path provided after /'));
|
|
return {};
|
|
}
|
|
|
|
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) {
|
|
return !regexInvalidURI.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.com/');
|
|
}
|
|
|
|
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://' || query === 'lbry://@';
|
|
|
|
if (queryIsPrefix) {
|
|
// If it is a prefix, wait until something else comes to figure out what to do
|
|
return [];
|
|
} else if (query.startsWith('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: query[7] === '@' ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE
|
|
}];
|
|
}
|
|
|
|
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
|
|
};
|
|
}
|
|
|
|
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 naughtyTags = ['porn', 'nsfw', 'mature', 'xxx'].reduce((acc, tag) => _extends$2({}, acc, { [tag]: true }), {});
|
|
|
|
const isClaimNsfw = claim => {
|
|
if (!claim) {
|
|
throw new Error('No claim passed to isClaimNsfw()');
|
|
}
|
|
|
|
if (!claim.value) {
|
|
return false;
|
|
}
|
|
|
|
const tags = claim.value.tags || [];
|
|
for (let i = 0; i < tags.length; i += 1) {
|
|
const tag = tags[i].toLowerCase();
|
|
if (naughtyTags[tag]) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
//
|
|
|
|
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;
|
|
return metadata || (claim === undefined ? undefined : null);
|
|
});
|
|
|
|
const makeSelectMetadataItemForUri = (uri, key) => reselect.createSelector(makeSelectMetadataForUri(uri), metadata => {
|
|
return metadata ? metadata[key] : undefined;
|
|
});
|
|
|
|
const makeSelectTitleForUri = uri => reselect.createSelector(makeSelectMetadataForUri(uri), metadata => metadata && metadata.title);
|
|
|
|
const makeSelectDateForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
|
|
const timestamp = claim && claim.value && (claim.value.release_time ? claim.value.release_time * 1000 : claim.meta.creation_timestamp * 1000);
|
|
if (!timestamp) {
|
|
return undefined;
|
|
}
|
|
const dateObj = new Date(timestamp);
|
|
return dateObj;
|
|
});
|
|
|
|
const makeSelectContentTypeForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
|
|
const source = claim && claim.value && claim.value.source;
|
|
return source ? source.media_type : undefined;
|
|
});
|
|
|
|
const makeSelectThumbnailForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
|
|
const thumbnail = claim && claim.value && claim.value.thumbnail;
|
|
return thumbnail ? thumbnail.url : undefined;
|
|
});
|
|
|
|
const makeSelectCoverForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
|
|
const cover = claim && claim.value && claim.value.cover;
|
|
return cover ? cover.url : 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(/^@/)).sort((a, b) => a.timestamp - b.timestamp));
|
|
|
|
const selectMyClaimUrisWithoutChannels = reselect.createSelector(selectMyClaimsWithoutChannels, myClaims => myClaims.sort((a, b) => a.confirmations - b.confirmations).map(claim => `lbry://${claim.name}#${claim.claim_id}`));
|
|
|
|
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 (claim && isClaimNsfw(claim)) {
|
|
return acc + 1;
|
|
}
|
|
return acc;
|
|
}, 0));
|
|
|
|
const makeSelectNsfwCountForChannel = uri => reselect.createSelector(selectClaimsById, selectAllClaimsByChannel, selectCurrentChannelPage, (byId, allClaims, page) => {
|
|
const byChannel = allClaims[uri] || {};
|
|
const claimIds = byChannel[page || 1];
|
|
|
|
if (!claimIds) return 0;
|
|
|
|
return claimIds.reduce((acc, claimId) => {
|
|
const claim = byId[claimId];
|
|
if (isClaimNsfw(claim)) {
|
|
return acc + 1;
|
|
}
|
|
return acc;
|
|
}, 0);
|
|
});
|
|
|
|
const makeSelectClaimIsNsfw = uri => reselect.createSelector(makeSelectClaimForUri(uri),
|
|
// Eventually these will come from some list of tags that are considered adult
|
|
// Or possibly come from users settings of what tags they want to hide
|
|
// For now, there is just a hard coded list of tags inside `isClaimNsfw`
|
|
// selectNaughtyTags(),
|
|
claim => {
|
|
if (!claim) {
|
|
return false;
|
|
}
|
|
|
|
return isClaimNsfw(claim);
|
|
});
|
|
|
|
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;
|
|
|
|
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
|
|
// accepts a regular claim uri lbry://something
|
|
// returns the channel uri that created this claim lbry://@channel
|
|
const makeSelectChannelForClaimUri = (uri, includePrefix = false) => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
|
|
if (!claim || !claim.signing_channel) {
|
|
return null;
|
|
}
|
|
|
|
const { claim_id: claimId, name } = claim.signing_channel;
|
|
let channel = `${name}#${claimId}`;
|
|
return includePrefix ? `lbry://${channel}` : channel;
|
|
});
|
|
|
|
const makeSelectTagsForUri = uri => reselect.createSelector(makeSelectMetadataForUri(uri), metadata => {
|
|
return metadata && metadata.tags || [];
|
|
});
|
|
|
|
const selectFetchingClaimSearch = reselect.createSelector(selectState$1, state => state.fetchingClaimSearch);
|
|
|
|
const selectLastClaimSearchUris = reselect.createSelector(selectState$1, state => state.lastClaimSearchUris);
|
|
|
|
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 selectTotalBalance = reselect.createSelector(selectState$2, state => state.totalBalance);
|
|
|
|
const selectTransactionsById = reselect.createSelector(selectState$2, state => state.transactions || {});
|
|
|
|
const selectSupportsByOutpoint = reselect.createSelector(selectState$2, state => state.supports || {});
|
|
|
|
const selectTransactionItems = reselect.createSelector(selectTransactionsById, byId => {
|
|
const items = [];
|
|
|
|
Object.keys(byId).forEach(txid => {
|
|
const tx = byId[txid];
|
|
|
|
// ignore dust/fees
|
|
// it is fee only txn if all infos are also empty
|
|
if (Math.abs(tx.value) === Math.abs(tx.fee) && tx.claim_info.length === 0 && tx.support_info.length === 0 && tx.update_info.length === 0 && tx.abandon_info.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const append = [];
|
|
|
|
append.push(...tx.claim_info.map(item => Object.assign({}, tx, item, {
|
|
type: item.claim_name[0] === '@' ? CHANNEL$1 : PUBLISH$1
|
|
})));
|
|
append.push(...tx.support_info.map(item => Object.assign({}, tx, item, {
|
|
type: !item.is_tip ? SUPPORT : TIP
|
|
})));
|
|
append.push(...tx.update_info.map(item => Object.assign({}, tx, item, { type: UPDATE })));
|
|
append.push(...tx.abandon_info.map(item => Object.assign({}, tx, item, { type: ABANDON })));
|
|
|
|
if (!append.length) {
|
|
append.push(Object.assign({}, tx, {
|
|
type: tx.value < 0 ? SPEND : RECEIVE
|
|
}));
|
|
}
|
|
|
|
items.push(...append.map(item => {
|
|
// value on transaction, amount on outpoint
|
|
// amount is always positive, but should match sign of value
|
|
const balanceDelta = parseFloat(item.balance_delta);
|
|
const value = parseFloat(item.value);
|
|
const amount = balanceDelta || value;
|
|
const fee = parseFloat(tx.fee);
|
|
|
|
return {
|
|
txid,
|
|
timestamp: tx.timestamp,
|
|
date: tx.timestamp ? new Date(Number(tx.timestamp) * 1000) : null,
|
|
amount,
|
|
fee,
|
|
claim_id: item.claim_id,
|
|
claim_name: item.claim_name,
|
|
type: item.type || SPEND,
|
|
nout: item.nout,
|
|
confirmations: tx.confirmations
|
|
};
|
|
}));
|
|
});
|
|
|
|
return items.sort((tx1, tx2) => {
|
|
if (!tx1.timestamp && !tx2.timestamp) {
|
|
return 0;
|
|
} else if (!tx1.timestamp && tx2.timestamp) {
|
|
return -1;
|
|
} else if (tx1.timestamp && !tx2.timestamp) {
|
|
return 1;
|
|
}
|
|
|
|
return tx2.timestamp - tx1.timestamp;
|
|
});
|
|
});
|
|
|
|
const selectRecentTransactions = reselect.createSelector(selectTransactionItems, transactions => {
|
|
const threshold = new Date();
|
|
threshold.setDate(threshold.getDate() - 7);
|
|
return transactions.filter(transaction => {
|
|
if (!transaction.date) {
|
|
return true; // pending transaction
|
|
}
|
|
|
|
return transaction.date > threshold;
|
|
});
|
|
});
|
|
|
|
const selectHasTransactions = reselect.createSelector(selectTransactionItems, transactions => transactions && transactions.length > 0);
|
|
|
|
const selectIsFetchingTransactions = reselect.createSelector(selectState$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 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 doUpdateTotalBalance() {
|
|
return (dispatch, getState) => {
|
|
const {
|
|
wallet: { totalBalance: totalBalanceInStore }
|
|
} = getState();
|
|
lbryProxy.account_list().then(accountList => {
|
|
const { lbc_mainnet: accounts } = accountList;
|
|
const totalSatoshis = accounts.length === 1 ? accounts[0].satoshis : accounts.reduce((a, b) => a.satoshis + b.satoshis);
|
|
const totalBalance = (Number.isNaN(totalSatoshis) ? 0 : totalSatoshis) / Math.pow(10, 8);
|
|
if (totalBalanceInStore !== totalBalance) {
|
|
dispatch({
|
|
type: UPDATE_TOTAL_BALANCE,
|
|
data: {
|
|
totalBalance
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
}
|
|
|
|
function doBalanceSubscribe() {
|
|
return dispatch => {
|
|
dispatch(doUpdateBalance());
|
|
setInterval(() => dispatch(doUpdateBalance()), 5000);
|
|
};
|
|
}
|
|
|
|
function doTotalBalanceSubscribe() {
|
|
return dispatch => {
|
|
dispatch(doUpdateTotalBalance());
|
|
setInterval(() => dispatch(doUpdateTotalBalance()), 5000);
|
|
};
|
|
}
|
|
|
|
function doFetchTransactions() {
|
|
return dispatch => {
|
|
dispatch(doFetchSupports());
|
|
dispatch({
|
|
type: FETCH_TRANSACTIONS_STARTED
|
|
});
|
|
|
|
lbryProxy.utxo_release().then(() => lbryProxy.transaction_list()).then(results => {
|
|
dispatch({
|
|
type: FETCH_TRANSACTIONS_COMPLETED,
|
|
data: {
|
|
transactions: results
|
|
}
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
function doFetchSupports() {
|
|
return dispatch => {
|
|
dispatch({
|
|
type: FETCH_SUPPORTS_STARTED
|
|
});
|
|
|
|
lbryProxy.support_list().then(results => {
|
|
dispatch({
|
|
type: FETCH_SUPPORTS_COMPLETED,
|
|
data: {
|
|
supports: results
|
|
}
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
function doGetNewAddress() {
|
|
return dispatch => {
|
|
dispatch({
|
|
type: GET_NEW_ADDRESS_STARTED
|
|
});
|
|
|
|
lbryProxy.address_unused().then(address => {
|
|
dispatch({
|
|
type: GET_NEW_ADDRESS_COMPLETED,
|
|
data: { address }
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
function doCheckAddressIsMine(address) {
|
|
return dispatch => {
|
|
dispatch({
|
|
type: CHECK_ADDRESS_IS_MINE_STARTED
|
|
});
|
|
|
|
lbryProxy.address_is_mine({ address }).then(isMine => {
|
|
if (!isMine) dispatch(doGetNewAddress());
|
|
|
|
dispatch({
|
|
type: CHECK_ADDRESS_IS_MINE_COMPLETED
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
function doSendDraftTransaction(address, amount) {
|
|
return (dispatch, getState) => {
|
|
const state = getState();
|
|
const balance = selectBalance(state);
|
|
|
|
if (balance - amount <= 0) {
|
|
dispatch(doToast({
|
|
title: 'Insufficient credits',
|
|
message: 'Insufficient credits'
|
|
}));
|
|
return;
|
|
}
|
|
|
|
dispatch({
|
|
type: SEND_TRANSACTION_STARTED
|
|
});
|
|
|
|
const successCallback = response => {
|
|
if (response.txid) {
|
|
dispatch({
|
|
type: SEND_TRANSACTION_COMPLETED
|
|
});
|
|
dispatch(doToast({
|
|
message: `You sent ${amount} 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.account_send({
|
|
addresses: [address],
|
|
amount: creditsToString(amount)
|
|
}).then(successCallback, errorCallback);
|
|
};
|
|
}
|
|
|
|
function doSetDraftTransactionAmount(amount) {
|
|
return {
|
|
type: SET_DRAFT_TRANSACTION_AMOUNT,
|
|
data: { amount }
|
|
};
|
|
}
|
|
|
|
function doSetDraftTransactionAddress(address) {
|
|
return {
|
|
type: SET_DRAFT_TRANSACTION_ADDRESS,
|
|
data: { address }
|
|
};
|
|
}
|
|
|
|
function doSendTip(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.support_create({
|
|
claim_id: claimId,
|
|
amount: creditsToString(amount),
|
|
tip: true
|
|
}).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
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// https://github.com/reactjs/redux/issues/911
|
|
function batchActions(...actions) {
|
|
return {
|
|
type: 'BATCH_ACTIONS',
|
|
actions
|
|
};
|
|
}
|
|
|
|
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; };
|
|
|
|
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 = {
|
|
stream: null,
|
|
claimsInChannel: null,
|
|
channel: null
|
|
};
|
|
|
|
// Flow has terrible Object.entries support
|
|
// https://github.com/facebook/flow/issues/2221
|
|
if (uriResolveInfo) {
|
|
if (uriResolveInfo.error) {
|
|
resolveInfo[uri] = _extends$3({}, fallbackResolveInfo);
|
|
} else {
|
|
let result = {};
|
|
if (uriResolveInfo.value_type === 'channel') {
|
|
result.channel = uriResolveInfo;
|
|
// $FlowFixMe
|
|
result.claimsInChannel = uriResolveInfo.meta.claims_in_channel;
|
|
} else {
|
|
result.stream = uriResolveInfo;
|
|
if (uriResolveInfo.signing_channel) {
|
|
result.channel = uriResolveInfo.signing_channel;
|
|
result.claimsInChannel = uriResolveInfo.signing_channel.meta && uriResolveInfo.signing_channel.meta.claims_in_channel || 0;
|
|
}
|
|
}
|
|
// $FlowFixMe
|
|
resolveInfo[uri] = result;
|
|
}
|
|
}
|
|
});
|
|
|
|
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().then(claims => {
|
|
dispatch({
|
|
type: FETCH_CLAIM_LIST_MINE_COMPLETED,
|
|
data: {
|
|
claims
|
|
}
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
function doAbandonClaim(txid, nout) {
|
|
const outpoint = `${txid}:${nout}`;
|
|
|
|
return (dispatch, getState) => {
|
|
const state = getState();
|
|
const myClaims = selectMyClaimsRaw(state);
|
|
const mySupports = selectSupportsByOutpoint(state);
|
|
|
|
// A user could be trying to abandon a support or one of their claims
|
|
const claimToAbandon = myClaims.find(claim => claim.txid === txid && claim.nout === nout);
|
|
const supportToAbandon = mySupports[outpoint];
|
|
|
|
if (!claimToAbandon && !supportToAbandon) {
|
|
console.error('No associated support or claim with txid: ', txid);
|
|
return;
|
|
}
|
|
|
|
const data = claimToAbandon ? { claimId: claimToAbandon.claim_id } : { outpoint: `${supportToAbandon.txid}:${supportToAbandon.nout}` };
|
|
|
|
const isClaim = !!claimToAbandon;
|
|
const startedActionType = isClaim ? ABANDON_CLAIM_STARTED : ABANDON_SUPPORT_STARTED;
|
|
const completedActionType = isClaim ? ABANDON_CLAIM_STARTED : ABANDON_SUPPORT_COMPLETED;
|
|
|
|
dispatch({
|
|
type: startedActionType,
|
|
data
|
|
});
|
|
|
|
const errorCallback = () => {
|
|
dispatch(doToast({
|
|
message: isClaim ? 'Error abandoning your claim' : 'Error unlocking your tip',
|
|
isError: true
|
|
}));
|
|
};
|
|
|
|
const successCallback = () => {
|
|
dispatch({
|
|
type: completedActionType,
|
|
data
|
|
});
|
|
|
|
dispatch(doToast({
|
|
message: isClaim ? 'Successfully abandoned your claim' : 'Successfully unlocked your tip!'
|
|
}));
|
|
|
|
// After abandoning, call claim_list to show the claim as abandoned
|
|
// Also fetch transactions to show the new abandon transaction
|
|
dispatch(doFetchClaimListMine());
|
|
dispatch(doFetchTransactions());
|
|
};
|
|
|
|
const abandonParams = {
|
|
txid,
|
|
nout,
|
|
blocking: true
|
|
};
|
|
|
|
let method;
|
|
if (supportToAbandon) {
|
|
method = 'support_abandon';
|
|
} else if (claimToAbandon) {
|
|
const { name: claimName } = claimToAbandon;
|
|
method = claimName.startsWith('@') ? 'channel_abandon' : 'stream_abandon';
|
|
}
|
|
|
|
if (!method) {
|
|
console.error('No "method" chosen for claim or support abandon');
|
|
return;
|
|
}
|
|
|
|
lbryProxy[method](abandonParams).then(successCallback, errorCallback);
|
|
};
|
|
}
|
|
|
|
function doFetchClaimsByChannel(uri, page = 1) {
|
|
return dispatch => {
|
|
dispatch({
|
|
type: FETCH_CHANNEL_CLAIMS_STARTED,
|
|
data: { uri, page }
|
|
});
|
|
|
|
lbryProxy.claim_search({
|
|
channel: uri,
|
|
valid_channel_signature: true,
|
|
page: page || 1,
|
|
order_by: ['release_time']
|
|
}).then(result => {
|
|
const { items: claimsInChannel, page: returnedPage } = result;
|
|
|
|
dispatch({
|
|
type: FETCH_CHANNEL_CLAIMS_COMPLETED,
|
|
data: {
|
|
uri,
|
|
claims: claimsInChannel || [],
|
|
page: returnedPage || undefined
|
|
}
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
function doCreateChannel(name, amount) {
|
|
return dispatch => {
|
|
dispatch({
|
|
type: CREATE_CHANNEL_STARTED
|
|
});
|
|
|
|
return lbryProxy.channel_create({
|
|
name,
|
|
bid: creditsToString(amount)
|
|
})
|
|
// outputs[0] is the certificate
|
|
// outputs[1] is the change from the tx, not in the app currently
|
|
.then(result => {
|
|
const channelClaim = result.outputs[0];
|
|
dispatch({
|
|
type: CREATE_CHANNEL_COMPLETED,
|
|
data: { channelClaim }
|
|
});
|
|
}).catch(error => {
|
|
dispatch({
|
|
type: CREATE_CHANNEL_FAILED,
|
|
data: error
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
function doFetchChannelListMine() {
|
|
return dispatch => {
|
|
dispatch({
|
|
type: FETCH_CHANNEL_LIST_STARTED
|
|
});
|
|
|
|
const callback = channels => {
|
|
dispatch({
|
|
type: FETCH_CHANNEL_LIST_COMPLETED,
|
|
data: { claims: channels }
|
|
});
|
|
};
|
|
|
|
lbryProxy.channel_list().then(callback);
|
|
};
|
|
}
|
|
|
|
function doClaimSearch(amount = 20, options = {}) {
|
|
return dispatch => {
|
|
dispatch({
|
|
type: CLAIM_SEARCH_STARTED
|
|
});
|
|
|
|
const success = data => {
|
|
const resolveInfo = {};
|
|
const uris = [];
|
|
data.items.forEach(stream => {
|
|
resolveInfo[stream.permanent_url] = { stream };
|
|
uris.push(stream.permanent_url);
|
|
});
|
|
|
|
dispatch({
|
|
type: CLAIM_SEARCH_COMPLETED,
|
|
data: { resolveInfo, uris, append: options.page && options.page !== 1 }
|
|
});
|
|
};
|
|
|
|
const failure = err => {
|
|
dispatch({
|
|
type: CLAIM_SEARCH_FAILED,
|
|
error: err
|
|
});
|
|
};
|
|
|
|
lbryProxy.claim_search(_extends$3({
|
|
page_size: amount
|
|
}, options)).then(success, failure);
|
|
};
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (title) {
|
|
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.signing_channel) {
|
|
uriParams.claimId = claim.signing_channel.claim_id;
|
|
} 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);
|
|
|
|
const selectDownloadedUris = reselect.createSelector(selectFileInfosDownloaded,
|
|
// We should use permament_url but it doesn't exist in file_list
|
|
info => info.slice().reverse().map(claim => `lbry://${claim.claim_name}#${claim.claim_id}`));
|
|
|
|
//
|
|
|
|
const selectState$4 = state => state.file || {};
|
|
|
|
const selectPurchaseUriErrorMessage = reselect.createSelector(selectState$4, state => state.purchaseUriErrorMessage);
|
|
|
|
const selectFailedPurchaseUris = reselect.createSelector(selectState$4, state => state.failedPurchaseUris);
|
|
|
|
const selectPurchasedUris = reselect.createSelector(selectState$4, state => state.purchasedUris);
|
|
|
|
const selectPurchasedStreamingUrls = reselect.createSelector(selectState$4, state => state.purchasedStreamingUrls);
|
|
|
|
const selectLastPurchasedUri = reselect.createSelector(selectState$4, state => state.purchasedUris.length > 0 ? state.purchasedUris[state.purchasedUris.length - 1] : null);
|
|
|
|
const makeSelectStreamingUrlForUri = uri => reselect.createSelector(selectPurchasedStreamingUrls, streamingUrls => streamingUrls && streamingUrls[uri]);
|
|
|
|
//
|
|
|
|
function doFileGet(uri, saveFile = true) {
|
|
return dispatch => {
|
|
dispatch({
|
|
type: LOADING_FILE_STARTED,
|
|
data: {
|
|
uri
|
|
}
|
|
});
|
|
|
|
// set save_file argument to True to save the file (old behaviour)
|
|
lbryProxy.get({ uri, save_file: saveFile }).then(streamInfo => {
|
|
const timeout = streamInfo === null || typeof streamInfo !== 'object';
|
|
|
|
if (timeout) {
|
|
dispatch({
|
|
type: LOADING_FILE_FAILED,
|
|
data: { uri }
|
|
});
|
|
dispatch({
|
|
type: PURCHASE_URI_FAILED,
|
|
data: { uri }
|
|
});
|
|
|
|
dispatch(doToast({ message: `File timeout for uri ${uri}`, isError: true }));
|
|
} else {
|
|
// purchase was completed successfully
|
|
const { streaming_url: streamingUrl } = streamInfo;
|
|
dispatch({
|
|
type: PURCHASE_URI_COMPLETED,
|
|
data: { uri, streamingUrl: !saveFile && streamingUrl ? streamingUrl : null }
|
|
});
|
|
}
|
|
}).catch(() => {
|
|
dispatch({
|
|
type: LOADING_FILE_FAILED,
|
|
data: { uri }
|
|
});
|
|
dispatch({
|
|
type: PURCHASE_URI_FAILED,
|
|
data: { uri }
|
|
});
|
|
|
|
dispatch(doToast({
|
|
message: `Failed to download ${uri}, please try again. If this problem persists, visit https://lbry.com/faq/support for support.`,
|
|
isError: true
|
|
}));
|
|
});
|
|
};
|
|
}
|
|
|
|
function doPurchaseUri(uri, costInfo, saveFile = true) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: PURCHASE_URI_STARTED,
|
|
data: { uri }
|
|
});
|
|
|
|
const state = getState();
|
|
const balance = selectBalance(state);
|
|
const fileInfo = makeSelectFileInfoForUri(uri)(state);
|
|
const downloadingByOutpoint = selectDownloadingByOutpoint(state);
|
|
const alreadyDownloading = fileInfo && !!downloadingByOutpoint[fileInfo.outpoint];
|
|
const alreadyStreaming = makeSelectStreamingUrlForUri(uri)(state);
|
|
|
|
if (alreadyDownloading || alreadyStreaming) {
|
|
dispatch({
|
|
type: PURCHASE_URI_FAILED,
|
|
data: { uri, error: `Already fetching uri: ${uri}` }
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { cost } = costInfo;
|
|
if (parseFloat(cost) > balance) {
|
|
dispatch({
|
|
type: PURCHASE_URI_FAILED,
|
|
data: { uri, error: 'Insufficient credits' }
|
|
});
|
|
return;
|
|
}
|
|
|
|
dispatch(doFileGet(uri, saveFile));
|
|
};
|
|
}
|
|
|
|
function doDeletePurchasedUri(uri) {
|
|
return {
|
|
type: DELETE_PURCHASED_URI,
|
|
data: { uri }
|
|
};
|
|
}
|
|
|
|
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 }
|
|
};
|
|
}
|
|
|
|
//
|
|
|
|
const formatLbryUriForWeb = uri => {
|
|
const { claimName, claimId } = parseURI(uri);
|
|
|
|
let webUrl = `/${claimName}`;
|
|
if (claimId) {
|
|
webUrl += `/${claimId}`;
|
|
}
|
|
|
|
return webUrl;
|
|
};
|
|
|
|
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 doResetThumbnailStatus = () => dispatch => {
|
|
dispatch({
|
|
type: UPDATE_PUBLISH_FORM,
|
|
data: {
|
|
thumbnailPath: ''
|
|
}
|
|
});
|
|
|
|
return fetch('https://spee.ch/api/config/site/publishing').then(res => res.json()).then(status => {
|
|
if (status.disabled) {
|
|
throw Error();
|
|
}
|
|
|
|
return dispatch({
|
|
type: UPDATE_PUBLISH_FORM,
|
|
data: {
|
|
uploadThumbnailStatus: READY,
|
|
thumbnail: ''
|
|
}
|
|
});
|
|
}).catch(() => dispatch({
|
|
type: UPDATE_PUBLISH_FORM,
|
|
data: {
|
|
uploadThumbnailStatus: API_DOWN,
|
|
thumbnail: ''
|
|
}
|
|
}));
|
|
};
|
|
|
|
const doClearPublish = () => dispatch => {
|
|
dispatch({ type: CLEAR_PUBLISH });
|
|
return dispatch(doResetThumbnailStatus());
|
|
};
|
|
|
|
const doUpdatePublishForm = publishFormValue => dispatch => dispatch({
|
|
type: UPDATE_PUBLISH_FORM,
|
|
data: _extends$4({}, publishFormValue)
|
|
});
|
|
|
|
const doUploadThumbnail = (filePath, thumbnailBuffer, fsAdapter) => dispatch => {
|
|
let thumbnail, fileExt, fileName, fileType;
|
|
|
|
const makeid = () => {
|
|
let text = '';
|
|
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
for (let i = 0; i < 24; i += 1) text += possible.charAt(Math.floor(Math.random() * 62));
|
|
return text;
|
|
};
|
|
|
|
const uploadError = (error = '') => {
|
|
dispatch(batchActions({
|
|
type: UPDATE_PUBLISH_FORM,
|
|
data: {
|
|
uploadThumbnailStatus: READY,
|
|
thumbnail: '',
|
|
nsfw: false
|
|
}
|
|
}, doError(error)));
|
|
};
|
|
|
|
dispatch({
|
|
type: UPDATE_PUBLISH_FORM,
|
|
data: { uploadThumbnailStatus: IN_PROGRESS }
|
|
});
|
|
|
|
if (fsAdapter && fsAdapter.readFile && filePath) {
|
|
fsAdapter.readFile(filePath, 'base64').then(base64Image => {
|
|
fileExt = 'png';
|
|
fileName = 'thumbnail.png';
|
|
fileType = 'image/png';
|
|
|
|
const data = new FormData();
|
|
const name = makeid();
|
|
data.append('name', name);
|
|
data.append('file', { uri: 'file://' + filePath, type: fileType, name: fileName });
|
|
|
|
return fetch('https://spee.ch/api/claim/publish', {
|
|
method: 'POST',
|
|
body: data
|
|
}).then(response => response.json()).then(json => json.success ? dispatch({
|
|
type: UPDATE_PUBLISH_FORM,
|
|
data: {
|
|
uploadThumbnailStatus: COMPLETE,
|
|
thumbnail: `${json.data.url}${fileExt}`
|
|
}
|
|
}) : uploadError(json.message)).catch(err => uploadError(err.message));
|
|
});
|
|
} else {
|
|
if (filePath) {
|
|
thumbnail = fs.readFileSync(filePath);
|
|
fileExt = path.extname(filePath);
|
|
fileName = path.basename(filePath);
|
|
fileType = `image/${fileExt.slice(1)}`;
|
|
} else if (thumbnailBuffer) {
|
|
thumbnail = thumbnailBuffer;
|
|
fileExt = '.png';
|
|
fileName = 'thumbnail.png';
|
|
fileType = 'image/png';
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
const data = new FormData();
|
|
const name = makeid();
|
|
const file = new File([thumbnail], fileName, { type: fileType });
|
|
data.append('name', name);
|
|
data.append('file', file);
|
|
|
|
return fetch('https://spee.ch/api/claim/publish', {
|
|
method: 'POST',
|
|
body: data
|
|
}).then(response => response.json()).then(json => json.success ? dispatch({
|
|
type: UPDATE_PUBLISH_FORM,
|
|
data: {
|
|
uploadThumbnailStatus: COMPLETE,
|
|
thumbnail: `${json.data.url}${fileExt}`
|
|
}
|
|
}) : uploadError(json.message)).catch(err => uploadError(err.message));
|
|
}
|
|
};
|
|
|
|
const doPrepareEdit = (claim, uri, fileInfo) => dispatch => {
|
|
const { name, amount, channel_name: channelName, value } = claim;
|
|
|
|
const {
|
|
author,
|
|
description,
|
|
// use same values as default state
|
|
// fee will be undefined for free content
|
|
fee = {
|
|
amount: 0,
|
|
currency: 'LBC'
|
|
},
|
|
languages,
|
|
license,
|
|
license_url: licenseUrl,
|
|
thumbnail,
|
|
title
|
|
} = value;
|
|
|
|
const publishData = {
|
|
name,
|
|
channel: channelName,
|
|
bid: amount,
|
|
contentIsFree: !fee.amount,
|
|
author,
|
|
description,
|
|
fee: { amount: fee.amount, currency: fee.currency },
|
|
languages,
|
|
thumbnail: thumbnail ? thumbnail.url : null,
|
|
title,
|
|
uri,
|
|
uploadThumbnailStatus: thumbnail ? MANUAL : undefined,
|
|
licenseUrl,
|
|
nsfw: isClaimNsfw(claim)
|
|
};
|
|
|
|
// Make sure custom liscence's are mapped properly
|
|
// If the license isn't one of the standard licenses, map the custom license and description/url
|
|
if (!CC_LICENSES.some(({ value }) => value === license)) {
|
|
if (!license || license === NONE || license === PUBLIC_DOMAIN) {
|
|
publishData.licenseType = license;
|
|
} else if (license && !licenseUrl && license !== NONE) {
|
|
publishData.licenseType = COPYRIGHT;
|
|
} else {
|
|
publishData.licenseType = OTHER;
|
|
}
|
|
|
|
publishData.otherLicenseDescription = license;
|
|
} else {
|
|
publishData.licenseType = license;
|
|
}
|
|
|
|
if (fileInfo && fileInfo.download_path) {
|
|
try {
|
|
fs.accessSync(fileInfo.download_path, fs.constants.R_OK);
|
|
publishData.filePath = fileInfo.download_path;
|
|
} catch (e) {
|
|
console.error(e.name, e.message);
|
|
}
|
|
}
|
|
|
|
dispatch({ type: DO_PREPARE_EDIT, data: publishData });
|
|
};
|
|
|
|
const doPublish = params => (dispatch, getState) => {
|
|
dispatch({ type: PUBLISH_START });
|
|
|
|
const state = getState();
|
|
const myChannels = selectMyChannelClaims(state);
|
|
const myClaims = selectMyClaimsWithoutChannels(state);
|
|
|
|
const {
|
|
name,
|
|
bid,
|
|
filePath,
|
|
description,
|
|
language,
|
|
license,
|
|
licenseUrl,
|
|
thumbnail,
|
|
channel,
|
|
title,
|
|
contentIsFree,
|
|
fee,
|
|
uri,
|
|
nsfw,
|
|
claim
|
|
} = params;
|
|
|
|
// get the claim id from the channel name, we will use that instead
|
|
const namedChannelClaim = myChannels.find(myChannel => myChannel.name === channel);
|
|
const channelId = namedChannelClaim ? namedChannelClaim.claim_id : '';
|
|
|
|
const publishPayload = {
|
|
name,
|
|
bid: creditsToString(bid),
|
|
title,
|
|
license,
|
|
languages: [language],
|
|
description,
|
|
tags: claim && claim.value.tags || [],
|
|
locations: claim && claim.value.locations
|
|
};
|
|
|
|
// Temporary solution to keep the same publish flow with the new tags api
|
|
// Eventually we will allow users to enter their own tags on publish
|
|
// `nsfw` will probably be removed
|
|
|
|
if (licenseUrl) {
|
|
publishPayload.license_url = licenseUrl;
|
|
}
|
|
|
|
if (thumbnail) {
|
|
publishPayload.thumbnail_url = thumbnail;
|
|
}
|
|
|
|
if (claim && claim.value.release_time) {
|
|
publishPayload.release_time = Number(claim.value.release_time);
|
|
}
|
|
|
|
if (nsfw) {
|
|
if (!publishPayload.tags.includes('mature')) {
|
|
publishPayload.tags.push('mature');
|
|
}
|
|
} else {
|
|
const indexToRemove = publishPayload.tags.indexOf('mature');
|
|
if (indexToRemove > -1) {
|
|
publishPayload.tags.splice(indexToRemove, 1);
|
|
}
|
|
}
|
|
|
|
if (channelId) {
|
|
publishPayload.channel_id = channelId;
|
|
}
|
|
|
|
if (!contentIsFree && fee && fee.currency && Number(fee.amount) > 0) {
|
|
publishPayload.fee_currency = fee.currency;
|
|
publishPayload.fee_amount = creditsToString(fee.amount);
|
|
}
|
|
|
|
// Only pass file on new uploads, not metadata only edits.
|
|
// The sdk will figure it out
|
|
if (filePath) publishPayload.file_path = filePath;
|
|
|
|
const success = successResponse => {
|
|
//analytics.apiLogPublish();
|
|
|
|
const pendingClaim = successResponse.outputs[0];
|
|
const actions = [];
|
|
|
|
actions.push({
|
|
type: PUBLISH_SUCCESS
|
|
});
|
|
|
|
//actions.push(doOpenModal(MODALS.PUBLISH, { uri }));
|
|
|
|
// We have to fake a temp claim until the new pending one is returned by claim_list_mine
|
|
// We can't rely on claim_list_mine because there might be some delay before the new claims are returned
|
|
// Doing this allows us to show the pending claim immediately, it will get overwritten by the real one
|
|
const isMatch = claim => claim.claim_id === pendingClaim.claim_id;
|
|
const isEdit = myClaims.some(isMatch);
|
|
const myNewClaims = isEdit ? myClaims.map(claim => isMatch(claim) ? pendingClaim : claim) : myClaims.concat(pendingClaim);
|
|
|
|
actions.push({
|
|
type: FETCH_CLAIM_LIST_MINE_COMPLETED,
|
|
data: {
|
|
claims: myNewClaims
|
|
}
|
|
});
|
|
|
|
dispatch(batchActions(...actions));
|
|
};
|
|
|
|
const failure = error => {
|
|
dispatch({ type: PUBLISH_FAIL });
|
|
dispatch(doError(error.message));
|
|
};
|
|
|
|
return lbryProxy.publish(publishPayload).then(success, failure);
|
|
};
|
|
|
|
// Calls claim_list_mine until any pending publishes are confirmed
|
|
const doCheckPendingPublishes = () => (dispatch, getState) => {
|
|
const state = getState();
|
|
const pendingById = selectPendingById(state);
|
|
|
|
if (!Object.keys(pendingById).length) {
|
|
return;
|
|
}
|
|
|
|
let publishCheckInterval;
|
|
|
|
const checkFileList = () => {
|
|
lbryProxy.claim_list().then(claims => {
|
|
claims.forEach(claim => {
|
|
// If it's confirmed, check if it was pending previously
|
|
if (claim.confirmations > 0 && pendingById[claim.claim_id]) {
|
|
delete pendingById[claim.claim_id];
|
|
|
|
// If it's confirmed, check if we should notify the user
|
|
if (selectosNotificationsEnabled(getState())) {
|
|
const notif = new window.Notification('LBRY Publish Complete', {
|
|
body: `${claim.value.title} has been published to lbry://${claim.name}. Click here to view it`,
|
|
silent: false
|
|
});
|
|
notif.onclick = () => {
|
|
dispatch(push(formatLbryUriForWeb(claim.permanent_url)));
|
|
};
|
|
}
|
|
}
|
|
});
|
|
|
|
dispatch({
|
|
type: FETCH_CLAIM_LIST_MINE_COMPLETED,
|
|
data: {
|
|
claims
|
|
}
|
|
});
|
|
|
|
if (!Object.keys(pendingById).length) {
|
|
clearInterval(publishCheckInterval);
|
|
}
|
|
});
|
|
};
|
|
|
|
publishCheckInterval = setInterval(() => {
|
|
checkFileList();
|
|
}, 30000);
|
|
};
|
|
|
|
// 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.com/';
|
|
|
|
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 => {
|
|
if (result.name) {
|
|
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(e => {
|
|
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 doToggleTagFollow = name => ({
|
|
type: TOGGLE_TAG_FOLLOW,
|
|
data: {
|
|
name
|
|
}
|
|
});
|
|
|
|
const doAddTag = name => ({
|
|
type: TAG_ADD,
|
|
data: {
|
|
name
|
|
}
|
|
});
|
|
|
|
const doDeleteTag = name => ({
|
|
type: TAG_DELETE,
|
|
data: {
|
|
name
|
|
}
|
|
});
|
|
|
|
//
|
|
|
|
function doCommentList(uri) {
|
|
return (dispatch, getState) => {
|
|
const state = getState();
|
|
const claim = selectClaimsByUri(state)[uri];
|
|
const claimId = claim ? claim.claim_id : null;
|
|
|
|
dispatch({
|
|
type: COMMENT_LIST_STARTED
|
|
});
|
|
lbryProxy.comment_list({
|
|
claim_id: claimId
|
|
}).then(results => {
|
|
dispatch({
|
|
type: COMMENT_LIST_COMPLETED,
|
|
data: {
|
|
comments: results,
|
|
claimId: claimId,
|
|
uri: uri
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
console.log(error);
|
|
dispatch({
|
|
type: COMMENT_LIST_FAILED,
|
|
data: error
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
function doCommentCreate(comment = '', claim_id = '', channel, parent_id) {
|
|
return (dispatch, getState) => {
|
|
const state = getState();
|
|
dispatch({
|
|
type: COMMENT_CREATE_STARTED
|
|
});
|
|
const myChannels = selectMyChannelClaims(state);
|
|
const namedChannelClaim = myChannels.find(myChannel => myChannel.name === channel);
|
|
const channel_id = namedChannelClaim ? namedChannelClaim.claim_id : null;
|
|
return lbryProxy.comment_create({
|
|
comment,
|
|
claim_id,
|
|
channel_id
|
|
}).then(result => {
|
|
dispatch({
|
|
type: COMMENT_CREATE_COMPLETED,
|
|
data: {
|
|
comment: result,
|
|
claimId: claim_id
|
|
}
|
|
});
|
|
}).catch(error => {
|
|
dispatch({
|
|
type: COMMENT_CREATE_FAILED,
|
|
data: error
|
|
});
|
|
dispatch(doToast({
|
|
message: 'Oops, someone broke comments.',
|
|
isError: true
|
|
}));
|
|
});
|
|
};
|
|
}
|
|
|
|
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 = {};
|
|
const defaultState = {
|
|
byId: {},
|
|
claimsByUri: {},
|
|
claimsByChannel: {},
|
|
channelClaimCounts: {},
|
|
fetchingChannelClaims: {},
|
|
resolvingUris: [],
|
|
// This should not be a Set
|
|
// Storing sets in reducers can cause issues
|
|
myChannelClaims: new Set(),
|
|
fetchingMyChannels: false,
|
|
abandoningById: {},
|
|
pendingById: {},
|
|
fetchingClaimSearch: false,
|
|
lastClaimSearchUris: []
|
|
};
|
|
|
|
function handleClaimAction(state, action) {
|
|
const {
|
|
resolveInfo
|
|
} = action.data;
|
|
const byUri = Object.assign({}, state.claimsByUri);
|
|
const byId = Object.assign({}, state.byId);
|
|
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
|
|
|
Object.entries(resolveInfo).forEach(([uri, resolveResponse]) => {
|
|
// $FlowFixMe
|
|
if (resolveResponse.claimsInChannel) {
|
|
// $FlowFixMe
|
|
channelClaimCounts[uri] = resolveResponse.claimsInChannel;
|
|
}
|
|
});
|
|
|
|
// $FlowFixMe
|
|
Object.entries(resolveInfo).forEach(([uri, { channel, stream }]) => {
|
|
if (stream) {
|
|
byId[stream.claim_id] = stream;
|
|
byUri[uri] = stream.claim_id;
|
|
}
|
|
if (channel) {
|
|
byId[channel.claim_id] = channel;
|
|
byUri[stream ? channel.permanent_url : uri] = channel.claim_id;
|
|
}
|
|
if (!stream && !channel) {
|
|
byUri[uri] = null;
|
|
}
|
|
});
|
|
|
|
return Object.assign({}, state, {
|
|
byId,
|
|
claimsByUri: byUri,
|
|
channelClaimCounts,
|
|
resolvingUris: (state.resolvingUris || []).filter(uri => !resolveInfo[uri])
|
|
});
|
|
}
|
|
|
|
reducers[RESOLVE_URIS_COMPLETED] = (state, action) => {
|
|
return _extends$5({}, handleClaimAction(state, action));
|
|
};
|
|
|
|
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)
|
|
// $FlowFixMe
|
|
.filter(pendingClaim => byId[pendingClaim.claim_id]).forEach(pendingClaim => {
|
|
// $FlowFixMe
|
|
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.channelClaim;
|
|
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 = oldResolving.slice();
|
|
|
|
uris.forEach(uri => {
|
|
if (!newResolving.includes(uri)) {
|
|
newResolving.push(uri);
|
|
}
|
|
});
|
|
|
|
return Object.assign({}, state, {
|
|
resolvingUris: newResolving
|
|
});
|
|
};
|
|
|
|
reducers[CLAIM_SEARCH_STARTED] = state => {
|
|
return Object.assign({}, state, {
|
|
fetchingClaimSearch: true
|
|
});
|
|
};
|
|
reducers[CLAIM_SEARCH_COMPLETED] = (state, action) => {
|
|
const { lastClaimSearchUris } = state;
|
|
|
|
let newClaimSearchUris = [];
|
|
if (action.data.append) {
|
|
newClaimSearchUris = lastClaimSearchUris.concat(action.data.uris);
|
|
} else {
|
|
newClaimSearchUris = action.data.uris;
|
|
}
|
|
|
|
return _extends$5({}, handleClaimAction(state, action), {
|
|
fetchingClaimSearch: false,
|
|
lastClaimSearchUris: newClaimSearchUris
|
|
});
|
|
};
|
|
reducers[CLAIM_SEARCH_FAILED] = state => {
|
|
return Object.assign({}, state, {
|
|
fetchingClaimSearch: false
|
|
});
|
|
};
|
|
|
|
function claimsReducer(state = defaultState, action) {
|
|
const handler = reducers[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$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 defaultState$1 = {
|
|
byId: {},
|
|
commentsByUri: {},
|
|
isLoading: false
|
|
};
|
|
|
|
const commentReducer = handleActions({
|
|
[COMMENT_CREATE_STARTED]: (state, action) => _extends$6({}, state, {
|
|
isLoading: true
|
|
}),
|
|
|
|
[COMMENT_CREATE_FAILED]: (state, action) => _extends$6({}, state, {
|
|
isLoading: false
|
|
}),
|
|
|
|
[COMMENT_CREATE_COMPLETED]: (state, action) => {
|
|
const { comment, claimId } = action.data;
|
|
const byId = Object.assign({}, state.byId);
|
|
const comments = byId[claimId];
|
|
const newComments = comments.slice();
|
|
|
|
newComments.unshift(comment);
|
|
byId[claimId] = newComments;
|
|
|
|
return _extends$6({}, state, {
|
|
byId
|
|
});
|
|
},
|
|
|
|
[COMMENT_LIST_STARTED]: state => _extends$6({}, state, { isLoading: true }),
|
|
|
|
[COMMENT_LIST_COMPLETED]: (state, action) => {
|
|
const { comments, claimId, uri } = action.data;
|
|
const byId = Object.assign({}, state.byId);
|
|
const commentsByUri = Object.assign({}, state.commentsByUri);
|
|
|
|
if (comments['items']) {
|
|
byId[claimId] = comments['items'];
|
|
commentsByUri[uri] = claimId;
|
|
}
|
|
return _extends$6({}, state, {
|
|
byId,
|
|
commentsByUri,
|
|
isLoading: false
|
|
});
|
|
},
|
|
|
|
[COMMENT_LIST_FAILED]: (state, action) => _extends$6({}, state, {
|
|
isLoading: false
|
|
})
|
|
}, defaultState$1);
|
|
|
|
var _extends$7 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
const reducers$1 = {};
|
|
const defaultState$2 = {
|
|
positions: {}
|
|
};
|
|
|
|
reducers$1[SET_CONTENT_POSITION] = (state, action) => {
|
|
const { claimId, outpoint, position } = action.data;
|
|
return _extends$7({}, state, {
|
|
positions: _extends$7({}, state.positions, {
|
|
[claimId]: _extends$7({}, state.positions[claimId], {
|
|
[outpoint]: position
|
|
})
|
|
})
|
|
});
|
|
};
|
|
|
|
function contentReducer(state = defaultState$2, action) {
|
|
const handler = reducers$1[action.type];
|
|
if (handler) return handler(state, action);
|
|
return state;
|
|
}
|
|
|
|
var _extends$8 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
const reducers$2 = {};
|
|
const defaultState$3 = {
|
|
fileListPublishedSort: DATE_NEW,
|
|
fileListDownloadedSort: DATE_NEW
|
|
};
|
|
|
|
reducers$2[FILE_LIST_STARTED] = state => Object.assign({}, state, {
|
|
isFetchingFileList: true
|
|
});
|
|
|
|
reducers$2[FILE_LIST_SUCCEEDED] = (state, action) => {
|
|
const { fileInfos } = action.data;
|
|
const newByOutpoint = Object.assign({}, state.byOutpoint);
|
|
const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
|
|
|
|
fileInfos.forEach(fileInfo => {
|
|
const { outpoint } = fileInfo;
|
|
|
|
if (outpoint) newByOutpoint[fileInfo.outpoint] = fileInfo;
|
|
});
|
|
|
|
return Object.assign({}, state, {
|
|
isFetchingFileList: false,
|
|
byOutpoint: newByOutpoint,
|
|
pendingByOutpoint
|
|
});
|
|
};
|
|
|
|
reducers$2[FETCH_FILE_INFO_STARTED] = (state, action) => {
|
|
const { outpoint } = action.data;
|
|
const newFetching = Object.assign({}, state.fetching);
|
|
|
|
newFetching[outpoint] = true;
|
|
|
|
return Object.assign({}, state, {
|
|
fetching: newFetching
|
|
});
|
|
};
|
|
|
|
reducers$2[FETCH_FILE_INFO_COMPLETED] = (state, action) => {
|
|
const { fileInfo, outpoint } = action.data;
|
|
|
|
const newByOutpoint = Object.assign({}, state.byOutpoint);
|
|
const newFetching = Object.assign({}, state.fetching);
|
|
|
|
newByOutpoint[outpoint] = fileInfo;
|
|
delete newFetching[outpoint];
|
|
|
|
return Object.assign({}, state, {
|
|
byOutpoint: newByOutpoint,
|
|
fetching: newFetching
|
|
});
|
|
};
|
|
|
|
reducers$2[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$2[DOWNLOADING_PROGRESSED] = (state, action) => {
|
|
const { outpoint, fileInfo } = action.data;
|
|
|
|
const newByOutpoint = Object.assign({}, state.byOutpoint);
|
|
const newDownloading = Object.assign({}, state.downloadingByOutpoint);
|
|
|
|
newByOutpoint[outpoint] = fileInfo;
|
|
newDownloading[outpoint] = true;
|
|
|
|
return Object.assign({}, state, {
|
|
byOutpoint: newByOutpoint,
|
|
downloadingByOutpoint: newDownloading
|
|
});
|
|
};
|
|
|
|
reducers$2[DOWNLOADING_CANCELED] = (state, action) => {
|
|
const { outpoint } = action.data;
|
|
|
|
const newDownloading = Object.assign({}, state.downloadingByOutpoint);
|
|
delete newDownloading[outpoint];
|
|
|
|
return Object.assign({}, state, {
|
|
downloadingByOutpoint: newDownloading
|
|
});
|
|
};
|
|
|
|
reducers$2[DOWNLOADING_COMPLETED] = (state, action) => {
|
|
const { outpoint, fileInfo } = action.data;
|
|
|
|
const newByOutpoint = Object.assign({}, state.byOutpoint);
|
|
const newDownloading = Object.assign({}, state.downloadingByOutpoint);
|
|
|
|
newByOutpoint[outpoint] = fileInfo;
|
|
delete newDownloading[outpoint];
|
|
|
|
return Object.assign({}, state, {
|
|
byOutpoint: newByOutpoint,
|
|
downloadingByOutpoint: newDownloading
|
|
});
|
|
};
|
|
|
|
reducers$2[FILE_DELETE] = (state, action) => {
|
|
const { outpoint } = action.data;
|
|
|
|
const newByOutpoint = Object.assign({}, state.byOutpoint);
|
|
const downloadingByOutpoint = Object.assign({}, state.downloadingByOutpoint);
|
|
|
|
delete newByOutpoint[outpoint];
|
|
delete downloadingByOutpoint[outpoint];
|
|
|
|
return Object.assign({}, state, {
|
|
byOutpoint: newByOutpoint,
|
|
downloadingByOutpoint
|
|
});
|
|
};
|
|
|
|
reducers$2[LOADING_VIDEO_STARTED] = (state, action) => {
|
|
const { uri } = action.data;
|
|
|
|
const newLoading = Object.assign({}, state.urisLoading);
|
|
newLoading[uri] = true;
|
|
|
|
const newErrors = _extends$8({}, state.errors);
|
|
if (uri in newErrors) delete newErrors[uri];
|
|
|
|
return Object.assign({}, state, {
|
|
urisLoading: newLoading,
|
|
errors: _extends$8({}, newErrors)
|
|
});
|
|
};
|
|
|
|
reducers$2[LOADING_VIDEO_FAILED] = (state, action) => {
|
|
const { uri } = action.data;
|
|
|
|
const newLoading = Object.assign({}, state.urisLoading);
|
|
delete newLoading[uri];
|
|
|
|
const newErrors = _extends$8({}, state.errors);
|
|
newErrors[uri] = true;
|
|
|
|
return Object.assign({}, state, {
|
|
urisLoading: newLoading,
|
|
errors: _extends$8({}, newErrors)
|
|
});
|
|
};
|
|
|
|
reducers$2[SET_FILE_LIST_SORT] = (state, action) => {
|
|
const pageSortStates = {
|
|
[PUBLISHED]: 'fileListPublishedSort',
|
|
[DOWNLOADED]: 'fileListDownloadedSort'
|
|
};
|
|
const pageSortState = pageSortStates[action.data.page];
|
|
const { value } = action.data;
|
|
|
|
return Object.assign({}, state, {
|
|
[pageSortState]: value
|
|
});
|
|
};
|
|
|
|
function fileInfoReducer(state = defaultState$3, action) {
|
|
const handler = reducers$2[action.type];
|
|
if (handler) return handler(state, action);
|
|
return state;
|
|
}
|
|
|
|
var _extends$9 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
const reducers$3 = {};
|
|
const defaultState$4 = {
|
|
failedPurchaseUris: [],
|
|
purchasedUris: [],
|
|
purchasedStreamingUrls: {},
|
|
purchaseUriErrorMessage: ''
|
|
};
|
|
|
|
reducers$3[PURCHASE_URI_STARTED] = (state, action) => {
|
|
const { uri } = action.data;
|
|
const newFailedPurchaseUris = state.failedPurchaseUris.slice();
|
|
if (newFailedPurchaseUris.includes(uri)) {
|
|
newFailedPurchaseUris.splice(newFailedPurchaseUris.indexOf(uri), 1);
|
|
}
|
|
|
|
return _extends$9({}, state, {
|
|
failedPurchaseUris: newFailedPurchaseUris,
|
|
purchaseUriErrorMessage: ''
|
|
});
|
|
};
|
|
|
|
reducers$3[PURCHASE_URI_COMPLETED] = (state, action) => {
|
|
const { uri, streamingUrl } = action.data;
|
|
const newPurchasedUris = state.purchasedUris.slice();
|
|
const newFailedPurchaseUris = state.failedPurchaseUris.slice();
|
|
const newPurchasedStreamingUrls = Object.assign({}, state.purchasedStreamingUrls);
|
|
|
|
if (!newPurchasedUris.includes(uri)) {
|
|
newPurchasedUris.push(uri);
|
|
}
|
|
if (newFailedPurchaseUris.includes(uri)) {
|
|
newFailedPurchaseUris.splice(newFailedPurchaseUris.indexOf(uri), 1);
|
|
}
|
|
if (streamingUrl) {
|
|
newPurchasedStreamingUrls[uri] = streamingUrl;
|
|
}
|
|
|
|
return _extends$9({}, state, {
|
|
failedPurchaseUris: newFailedPurchaseUris,
|
|
purchasedUris: newPurchasedUris,
|
|
purchasedStreamingUrls: newPurchasedStreamingUrls,
|
|
purchaseUriErrorMessage: ''
|
|
});
|
|
};
|
|
|
|
reducers$3[PURCHASE_URI_FAILED] = (state, action) => {
|
|
const { uri, error } = action.data;
|
|
const newFailedPurchaseUris = state.failedPurchaseUris.slice();
|
|
|
|
if (!newFailedPurchaseUris.includes(uri)) {
|
|
newFailedPurchaseUris.push(uri);
|
|
}
|
|
|
|
return _extends$9({}, state, {
|
|
failedPurchaseUris: newFailedPurchaseUris,
|
|
purchaseUriErrorMessage: error
|
|
});
|
|
};
|
|
|
|
reducers$3[DELETE_PURCHASED_URI] = (state, action) => {
|
|
const { uri } = action.data;
|
|
const newPurchasedUris = state.purchasedUris.slice();
|
|
if (newPurchasedUris.includes(uri)) {
|
|
newPurchasedUris.splice(newPurchasedUris.indexOf(uri), 1);
|
|
}
|
|
|
|
return _extends$9({}, state, {
|
|
purchasedUris: newPurchasedUris
|
|
});
|
|
};
|
|
|
|
function fileReducer(state = defaultState$4, action) {
|
|
const handler = reducers$3[action.type];
|
|
if (handler) return handler(state, action);
|
|
return state;
|
|
}
|
|
|
|
var _extends$a = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
const defaultState$5 = {
|
|
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$a({}, state, {
|
|
toasts: newToasts
|
|
});
|
|
},
|
|
[DISMISS_TOAST]: state => {
|
|
const newToasts = state.toasts.slice();
|
|
newToasts.shift();
|
|
|
|
return _extends$a({}, state, {
|
|
toasts: newToasts
|
|
});
|
|
},
|
|
|
|
// Notifications
|
|
[CREATE_NOTIFICATION]: (state, action) => {
|
|
const notification = action.data;
|
|
const newNotifications = state.notifications.slice();
|
|
newNotifications.push(notification);
|
|
|
|
return _extends$a({}, 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$a({}, state, {
|
|
notifications
|
|
});
|
|
},
|
|
[DELETE_NOTIFICATION]: (state, action) => {
|
|
const { id } = action.data;
|
|
let newNotifications = state.notifications.slice();
|
|
newNotifications = newNotifications.filter(notification => notification.id !== id);
|
|
|
|
return _extends$a({}, state, {
|
|
notifications: newNotifications
|
|
});
|
|
},
|
|
|
|
// Errors
|
|
[CREATE_ERROR]: (state, action) => {
|
|
const error = action.data;
|
|
const newErrors = state.errors.slice();
|
|
newErrors.push(error);
|
|
|
|
return _extends$a({}, state, {
|
|
errors: newErrors
|
|
});
|
|
},
|
|
[DISMISS_ERROR]: state => {
|
|
const newErrors = state.errors.slice();
|
|
newErrors.shift();
|
|
|
|
return _extends$a({}, state, {
|
|
errors: newErrors
|
|
});
|
|
}
|
|
}, defaultState$5);
|
|
|
|
var _extends$b = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
|
|
|
const defaultState$6 = {
|
|
editingURI: undefined,
|
|
filePath: undefined,
|
|
contentIsFree: true,
|
|
fee: {
|
|
amount: 1,
|
|
currency: 'LBC'
|
|
},
|
|
title: '',
|
|
thumbnail_url: '',
|
|
thumbnailPath: '',
|
|
uploadThumbnailStatus: API_DOWN,
|
|
description: '',
|
|
language: 'en',
|
|
nsfw: false,
|
|
channel: CHANNEL_ANONYMOUS,
|
|
channelId: '',
|
|
name: '',
|
|
nameError: undefined,
|
|
bid: 0.1,
|
|
bidError: undefined,
|
|
licenseType: 'None',
|
|
otherLicenseDescription: 'All rights reserved',
|
|
licenseUrl: '',
|
|
publishing: false,
|
|
publishSuccess: false,
|
|
publishError: undefined
|
|
};
|
|
|
|
const publishReducer = handleActions({
|
|
[UPDATE_PUBLISH_FORM]: (state, action) => {
|
|
const { data } = action;
|
|
return _extends$b({}, state, data);
|
|
},
|
|
[CLEAR_PUBLISH]: () => _extends$b({}, defaultState$6),
|
|
[PUBLISH_START]: state => _extends$b({}, state, {
|
|
publishing: true,
|
|
publishSuccess: false
|
|
}),
|
|
[PUBLISH_FAIL]: state => _extends$b({}, state, {
|
|
publishing: false
|
|
}),
|
|
[PUBLISH_SUCCESS]: state => _extends$b({}, state, {
|
|
publishing: false,
|
|
publishSuccess: true
|
|
}),
|
|
[DO_PREPARE_EDIT]: (state, action) => {
|
|
const publishData = _objectWithoutProperties(action.data, []);
|
|
const { channel, name, uri } = publishData;
|
|
|
|
// The short uri is what is presented to the user
|
|
// The editingUri is the full uri with claim id
|
|
const shortUri = buildURI({
|
|
channelName: channel,
|
|
contentName: name
|
|
});
|
|
|
|
return _extends$b({}, defaultState$6, publishData, {
|
|
editingURI: uri,
|
|
uri: shortUri
|
|
});
|
|
}
|
|
}, defaultState$6);
|
|
|
|
var _extends$c = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
const defaultState$7 = {
|
|
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$c({}, state, {
|
|
searching: true
|
|
}),
|
|
[SEARCH_SUCCESS]: (state, action) => {
|
|
const { query, uris } = action.data;
|
|
|
|
return _extends$c({}, state, {
|
|
searching: false,
|
|
urisByQuery: Object.assign({}, state.urisByQuery, { [query]: uris })
|
|
});
|
|
},
|
|
|
|
[SEARCH_FAIL]: state => _extends$c({}, state, {
|
|
searching: false
|
|
}),
|
|
|
|
[UPDATE_SEARCH_QUERY]: (state, action) => _extends$c({}, state, {
|
|
searchQuery: action.data.query,
|
|
isActive: true
|
|
}),
|
|
|
|
[UPDATE_SEARCH_SUGGESTIONS]: (state, action) => _extends$c({}, state, {
|
|
suggestions: _extends$c({}, state.suggestions, {
|
|
[action.data.query]: action.data.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$c({}, state, {
|
|
isActive: false
|
|
}),
|
|
|
|
[SEARCH_FOCUS]: state => _extends$c({}, state, {
|
|
focused: true
|
|
}),
|
|
[SEARCH_BLUR]: state => _extends$c({}, state, {
|
|
focused: false
|
|
}),
|
|
[UPDATE_SEARCH_OPTIONS]: (state, action) => {
|
|
const { options: oldOptions } = state;
|
|
const newOptions = action.data;
|
|
const options = _extends$c({}, oldOptions, newOptions);
|
|
return _extends$c({}, state, {
|
|
options
|
|
});
|
|
}
|
|
}, defaultState$7);
|
|
|
|
var _extends$d = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
const tagsReducerBuilder = defaultState => handleActions({
|
|
[TOGGLE_TAG_FOLLOW]: (state, action) => {
|
|
const { followedTags } = state;
|
|
const { name } = action.data;
|
|
|
|
let newFollowedTags = followedTags.slice();
|
|
|
|
if (newFollowedTags.includes(name)) {
|
|
newFollowedTags = newFollowedTags.filter(tag => tag !== name);
|
|
} else {
|
|
newFollowedTags.push(name);
|
|
}
|
|
|
|
return _extends$d({}, state, {
|
|
followedTags: newFollowedTags
|
|
});
|
|
},
|
|
|
|
[TAG_ADD]: (state, action) => {
|
|
const { knownTags } = state;
|
|
const { name } = action.data;
|
|
|
|
let newKnownTags = _extends$d({}, knownTags);
|
|
newKnownTags[name] = { name };
|
|
|
|
return _extends$d({}, state, {
|
|
knownTags: newKnownTags
|
|
});
|
|
},
|
|
|
|
[TAG_DELETE]: (state, action) => {
|
|
const { knownTags, followedTags } = state;
|
|
const { name } = action.data;
|
|
|
|
let newKnownTags = _extends$d({}, knownTags);
|
|
delete newKnownTags[name];
|
|
const newFollowedTags = followedTags.filter(tag => tag !== name);
|
|
|
|
return _extends$d({}, state, {
|
|
knownTags: newKnownTags,
|
|
followedTags: newFollowedTags
|
|
});
|
|
}
|
|
}, defaultState);
|
|
|
|
var _extends$e = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
const 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$8 = {
|
|
balance: undefined,
|
|
totalBalance: undefined,
|
|
latestBlock: undefined,
|
|
transactions: {},
|
|
fetchingTransactions: false,
|
|
supports: {},
|
|
fetchingSupports: false,
|
|
abandoningSupportsByOutpoint: {},
|
|
gettingNewAddress: false,
|
|
draftTransaction: buildDraftTransaction(),
|
|
sendingSupport: false,
|
|
walletIsEncrypted: false,
|
|
walletEncryptPending: false,
|
|
walletEncryptSucceded: null,
|
|
walletEncryptResult: null,
|
|
walletDecryptPending: false,
|
|
walletDecryptSucceded: null,
|
|
walletDecryptResult: null,
|
|
walletUnlockPending: false,
|
|
walletUnlockSucceded: null,
|
|
walletUnlockResult: null,
|
|
walletLockPending: false,
|
|
walletLockSucceded: null,
|
|
walletLockResult: null,
|
|
transactionListFilter: 'all'
|
|
};
|
|
|
|
const walletReducer = handleActions({
|
|
[FETCH_TRANSACTIONS_STARTED]: state => _extends$e({}, state, {
|
|
fetchingTransactions: true
|
|
}),
|
|
|
|
[FETCH_TRANSACTIONS_COMPLETED]: (state, action) => {
|
|
const byId = _extends$e({}, state.transactions);
|
|
|
|
const { transactions } = action.data;
|
|
transactions.forEach(transaction => {
|
|
byId[transaction.txid] = transaction;
|
|
});
|
|
|
|
return _extends$e({}, state, {
|
|
transactions: byId,
|
|
fetchingTransactions: false
|
|
});
|
|
},
|
|
|
|
[FETCH_SUPPORTS_STARTED]: state => _extends$e({}, state, {
|
|
fetchingSupports: true
|
|
}),
|
|
|
|
[FETCH_SUPPORTS_COMPLETED]: (state, action) => {
|
|
const byOutpoint = state.supports;
|
|
const { supports } = action.data;
|
|
|
|
supports.forEach(transaction => {
|
|
const { txid, nout } = transaction;
|
|
byOutpoint[`${txid}:${nout}`] = transaction;
|
|
});
|
|
|
|
return _extends$e({}, state, { supports: byOutpoint, fetchingSupports: false });
|
|
},
|
|
|
|
[ABANDON_SUPPORT_STARTED]: (state, action) => {
|
|
const { outpoint } = action.data;
|
|
const currentlyAbandoning = state.abandoningSupportsByOutpoint;
|
|
|
|
currentlyAbandoning[outpoint] = true;
|
|
|
|
return _extends$e({}, state, {
|
|
abandoningSupportsByOutpoint: currentlyAbandoning
|
|
});
|
|
},
|
|
|
|
[ABANDON_SUPPORT_COMPLETED]: (state, action) => {
|
|
const { outpoint } = action.data;
|
|
const byOutpoint = state.supports;
|
|
const currentlyAbandoning = state.abandoningSupportsByOutpoint;
|
|
|
|
delete currentlyAbandoning[outpoint];
|
|
delete byOutpoint[outpoint];
|
|
|
|
return _extends$e({}, state, {
|
|
supports: byOutpoint,
|
|
abandoningSupportsById: currentlyAbandoning
|
|
});
|
|
},
|
|
|
|
[GET_NEW_ADDRESS_STARTED]: state => _extends$e({}, state, {
|
|
gettingNewAddress: true
|
|
}),
|
|
|
|
[GET_NEW_ADDRESS_COMPLETED]: (state, action) => {
|
|
const { address } = action.data;
|
|
|
|
return _extends$e({}, state, { gettingNewAddress: false, receiveAddress: address });
|
|
},
|
|
|
|
[UPDATE_BALANCE]: (state, action) => _extends$e({}, state, {
|
|
balance: action.data.balance
|
|
}),
|
|
|
|
[UPDATE_TOTAL_BALANCE]: (state, action) => _extends$e({}, state, {
|
|
totalBalance: action.data.totalBalance
|
|
}),
|
|
|
|
[CHECK_ADDRESS_IS_MINE_STARTED]: state => _extends$e({}, state, {
|
|
checkingAddressOwnership: true
|
|
}),
|
|
|
|
[CHECK_ADDRESS_IS_MINE_COMPLETED]: state => _extends$e({}, state, {
|
|
checkingAddressOwnership: false
|
|
}),
|
|
|
|
[SET_DRAFT_TRANSACTION_AMOUNT]: (state, action) => {
|
|
const oldDraft = state.draftTransaction;
|
|
const newDraft = _extends$e({}, oldDraft, { amount: parseFloat(action.data.amount) });
|
|
|
|
return _extends$e({}, state, { draftTransaction: newDraft });
|
|
},
|
|
|
|
[SET_DRAFT_TRANSACTION_ADDRESS]: (state, action) => {
|
|
const oldDraft = state.draftTransaction;
|
|
const newDraft = _extends$e({}, oldDraft, { address: action.data.address });
|
|
|
|
return _extends$e({}, state, { draftTransaction: newDraft });
|
|
},
|
|
|
|
[SEND_TRANSACTION_STARTED]: state => {
|
|
const newDraftTransaction = _extends$e({}, state.draftTransaction, { sending: true });
|
|
|
|
return _extends$e({}, state, { draftTransaction: newDraftTransaction });
|
|
},
|
|
|
|
[SEND_TRANSACTION_COMPLETED]: state => Object.assign({}, state, {
|
|
draftTransaction: buildDraftTransaction()
|
|
}),
|
|
|
|
[SEND_TRANSACTION_FAILED]: (state, action) => {
|
|
const newDraftTransaction = Object.assign({}, state.draftTransaction, {
|
|
sending: false,
|
|
error: action.data.error
|
|
});
|
|
|
|
return _extends$e({}, state, { draftTransaction: newDraftTransaction });
|
|
},
|
|
|
|
[SUPPORT_TRANSACTION_STARTED]: state => _extends$e({}, state, {
|
|
sendingSupport: true
|
|
}),
|
|
|
|
[SUPPORT_TRANSACTION_COMPLETED]: state => _extends$e({}, state, {
|
|
sendingSupport: false
|
|
}),
|
|
|
|
[SUPPORT_TRANSACTION_FAILED]: (state, action) => _extends$e({}, state, {
|
|
error: action.data.error,
|
|
sendingSupport: false
|
|
}),
|
|
|
|
[WALLET_STATUS_COMPLETED]: (state, action) => _extends$e({}, state, {
|
|
walletIsEncrypted: action.result
|
|
}),
|
|
|
|
[WALLET_ENCRYPT_START]: state => _extends$e({}, state, {
|
|
walletEncryptPending: true,
|
|
walletEncryptSucceded: null,
|
|
walletEncryptResult: null
|
|
}),
|
|
|
|
[WALLET_ENCRYPT_COMPLETED]: (state, action) => _extends$e({}, state, {
|
|
walletEncryptPending: false,
|
|
walletEncryptSucceded: true,
|
|
walletEncryptResult: action.result
|
|
}),
|
|
|
|
[WALLET_ENCRYPT_FAILED]: (state, action) => _extends$e({}, state, {
|
|
walletEncryptPending: false,
|
|
walletEncryptSucceded: false,
|
|
walletEncryptResult: action.result
|
|
}),
|
|
|
|
[WALLET_DECRYPT_START]: state => _extends$e({}, state, {
|
|
walletDecryptPending: true,
|
|
walletDecryptSucceded: null,
|
|
walletDecryptResult: null
|
|
}),
|
|
|
|
[WALLET_DECRYPT_COMPLETED]: (state, action) => _extends$e({}, state, {
|
|
walletDecryptPending: false,
|
|
walletDecryptSucceded: true,
|
|
walletDecryptResult: action.result
|
|
}),
|
|
|
|
[WALLET_DECRYPT_FAILED]: (state, action) => _extends$e({}, state, {
|
|
walletDecryptPending: false,
|
|
walletDecryptSucceded: false,
|
|
walletDecryptResult: action.result
|
|
}),
|
|
|
|
[WALLET_UNLOCK_START]: state => _extends$e({}, state, {
|
|
walletUnlockPending: true,
|
|
walletUnlockSucceded: null,
|
|
walletUnlockResult: null
|
|
}),
|
|
|
|
[WALLET_UNLOCK_COMPLETED]: (state, action) => _extends$e({}, state, {
|
|
walletUnlockPending: false,
|
|
walletUnlockSucceded: true,
|
|
walletUnlockResult: action.result
|
|
}),
|
|
|
|
[WALLET_UNLOCK_FAILED]: (state, action) => _extends$e({}, state, {
|
|
walletUnlockPending: false,
|
|
walletUnlockSucceded: false,
|
|
walletUnlockResult: action.result
|
|
}),
|
|
|
|
[WALLET_LOCK_START]: state => _extends$e({}, state, {
|
|
walletLockPending: false,
|
|
walletLockSucceded: null,
|
|
walletLockResult: null
|
|
}),
|
|
|
|
[WALLET_LOCK_COMPLETED]: (state, action) => _extends$e({}, state, {
|
|
walletLockPending: false,
|
|
walletLockSucceded: true,
|
|
walletLockResult: action.result
|
|
}),
|
|
|
|
[WALLET_LOCK_FAILED]: (state, action) => _extends$e({}, state, {
|
|
walletLockPending: false,
|
|
walletLockSucceded: false,
|
|
walletLockResult: action.result
|
|
}),
|
|
|
|
[SET_TRANSACTION_LIST_FILTER]: (state, action) => _extends$e({}, state, {
|
|
transactionListFilter: action.data
|
|
}),
|
|
|
|
[UPDATE_CURRENT_HEIGHT]: (state, action) => _extends$e({}, state, {
|
|
latestBlock: action.data
|
|
})
|
|
}, defaultState$8);
|
|
|
|
const selectState$5 = state => state.content || {};
|
|
|
|
const makeSelectContentPositionForUri = uri => reselect.createSelector(selectState$5, makeSelectClaimForUri(uri), (state, claim) => {
|
|
if (!claim) {
|
|
return null;
|
|
}
|
|
const outpoint = `${claim.txid}:${claim.nout}`;
|
|
const id = claim.claim_id;
|
|
return state.positions[id] ? state.positions[id][outpoint] : null;
|
|
});
|
|
|
|
var _extends$f = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
const selectState$6 = state => state.notifications || {};
|
|
|
|
const selectToast = reselect.createSelector(selectState$6, state => {
|
|
if (state.toasts.length) {
|
|
const { id, params } = state.toasts[0];
|
|
return _extends$f({
|
|
id
|
|
}, params);
|
|
}
|
|
|
|
return null;
|
|
});
|
|
|
|
const selectError = reselect.createSelector(selectState$6, state => {
|
|
if (state.errors.length) {
|
|
const { error } = state.errors[0];
|
|
return {
|
|
error
|
|
};
|
|
}
|
|
|
|
return null;
|
|
});
|
|
|
|
//
|
|
|
|
const selectState$7 = state => state.comments || {};
|
|
|
|
const selectCommentsById = reselect.createSelector(selectState$7, state => state.byId || {});
|
|
|
|
const selectCommentsByUri = reselect.createSelector(selectState$7, state => {
|
|
const byUri = state.commentsByUri || {};
|
|
const comments = {};
|
|
Object.keys(byUri).forEach(uri => {
|
|
const claimId = byUri[uri];
|
|
if (claimId === null) {
|
|
comments[uri] = null;
|
|
} else {
|
|
comments[uri] = claimId;
|
|
}
|
|
});
|
|
return comments;
|
|
});
|
|
|
|
const makeSelectCommentsForUri = uri => reselect.createSelector(selectCommentsById, selectCommentsByUri, (byId, byUri) => {
|
|
const claimId = byUri[uri];
|
|
return byId && byId[claimId];
|
|
});
|
|
|
|
function _objectWithoutProperties$1(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
|
|
|
const selectState$8 = state => state.publish || {};
|
|
|
|
const selectPublishFormValues = reselect.createSelector(selectState$8, state => {
|
|
const formValues = _objectWithoutProperties$1(state, ['pendingPublish']);
|
|
return formValues;
|
|
});
|
|
const selectIsStillEditing = reselect.createSelector(selectPublishFormValues, publishState => {
|
|
const { editingURI, uri } = publishState;
|
|
|
|
if (!editingURI || !uri) {
|
|
return false;
|
|
}
|
|
|
|
const { isChannel: currentIsChannel, claimName: currentClaimName, contentName: currentContentName } = parseURI(uri);
|
|
const { isChannel: editIsChannel, claimName: editClaimName, contentName: editContentName } = parseURI(editingURI);
|
|
|
|
// Depending on the previous/current use of a channel, we need to compare different things
|
|
// ex: going from a channel to anonymous, the new uri won't return contentName, so we need to use claimName
|
|
const currentName = currentIsChannel ? currentContentName : currentClaimName;
|
|
const editName = editIsChannel ? editContentName : editClaimName;
|
|
return currentName === editName;
|
|
});
|
|
|
|
const selectMyClaimForUri = reselect.createSelector(selectPublishFormValues, selectIsStillEditing, selectClaimsById, selectMyClaimsWithoutChannels, ({ editingURI, uri }, isStillEditing, claimsById, myClaims) => {
|
|
const { contentName, claimName } = parseURI(uri);
|
|
const { claimId: editClaimId } = parseURI(editingURI);
|
|
|
|
// If isStillEditing
|
|
// They clicked "edit" from the file page
|
|
// They haven't changed the channel/name after clicking edit
|
|
// Get the claim so they can edit without re-uploading a new file
|
|
return isStillEditing ? claimsById[editClaimId] : myClaims.find(claim => !contentName ? claim.name === claimName : claim.name === contentName || claim.name === claimName);
|
|
});
|
|
|
|
const selectIsResolvingPublishUris = reselect.createSelector(selectState$8, selectResolvingUris, ({ uri, name }, resolvingUris) => {
|
|
if (uri) {
|
|
const isResolvingUri = resolvingUris.includes(uri);
|
|
const { isChannel } = parseURI(uri);
|
|
|
|
let isResolvingShortUri;
|
|
if (isChannel) {
|
|
const shortUri = buildURI({ contentName: name });
|
|
isResolvingShortUri = resolvingUris.includes(shortUri);
|
|
}
|
|
|
|
return isResolvingUri || isResolvingShortUri;
|
|
}
|
|
|
|
return false;
|
|
});
|
|
|
|
const selectTakeOverAmount = reselect.createSelector(selectState$8, selectMyClaimForUri, selectClaimsByUri, ({ name }, myClaimForUri, claimsByUri) => {
|
|
// We only care about the winning claim for the short uri
|
|
const shortUri = buildURI({ contentName: name });
|
|
const claimForShortUri = claimsByUri[shortUri];
|
|
|
|
if (!myClaimForUri && claimForShortUri) {
|
|
return claimForShortUri.effective_amount;
|
|
} else if (myClaimForUri && claimForShortUri) {
|
|
// https://github.com/lbryio/lbry/issues/1476
|
|
// We should check the current effective_amount on my claim to see how much additional lbc
|
|
// is needed to win the claim. Currently this is not possible during a takeover.
|
|
// With this, we could say something like, "You have x lbc in support, if you bid y additional LBC you will control the claim"
|
|
// For now just ignore supports. We will just show the winning claim's bid amount
|
|
return claimForShortUri.effective_amount || claimForShortUri.amount;
|
|
}
|
|
|
|
return null;
|
|
});
|
|
|
|
//
|
|
|
|
const selectState$9 = state => state.tags || {};
|
|
|
|
const selectKnownTagsByName = reselect.createSelector(selectState$9, state => state.knownTags);
|
|
|
|
const selectFollowedTagsList = reselect.createSelector(selectState$9, state => state.followedTags);
|
|
|
|
const selectFollowedTags = reselect.createSelector(selectFollowedTagsList, followedTags => followedTags.map(tag => ({ name: tag })).sort((a, b) => a.name.localeCompare(b.name)));
|
|
|
|
const selectUnfollowedTags = reselect.createSelector(selectKnownTagsByName, selectFollowedTagsList, (tagsByName, followedTags) => {
|
|
const followedTagsSet = new Set(followedTags);
|
|
let tagsToReturn = [];
|
|
|
|
Object.keys(tagsByName).forEach(key => {
|
|
if (!followedTagsSet.has(key)) {
|
|
const { name } = tagsByName[key];
|
|
tagsToReturn.push({ name });
|
|
}
|
|
});
|
|
|
|
return tagsToReturn;
|
|
});
|
|
|
|
exports.ACTIONS = action_types;
|
|
exports.CLAIM_VALUES = claim;
|
|
exports.LICENSES = licenses;
|
|
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.commentReducer = commentReducer;
|
|
exports.contentReducer = contentReducer;
|
|
exports.convertToShareLink = convertToShareLink;
|
|
exports.creditsToString = creditsToString;
|
|
exports.doAbandonClaim = doAbandonClaim;
|
|
exports.doAddTag = doAddTag;
|
|
exports.doBalanceSubscribe = doBalanceSubscribe;
|
|
exports.doBlurSearchInput = doBlurSearchInput;
|
|
exports.doCheckAddressIsMine = doCheckAddressIsMine;
|
|
exports.doCheckPendingPublishes = doCheckPendingPublishes;
|
|
exports.doClaimSearch = doClaimSearch;
|
|
exports.doClearPublish = doClearPublish;
|
|
exports.doCommentCreate = doCommentCreate;
|
|
exports.doCommentList = doCommentList;
|
|
exports.doCreateChannel = doCreateChannel;
|
|
exports.doDeletePurchasedUri = doDeletePurchasedUri;
|
|
exports.doDeleteTag = doDeleteTag;
|
|
exports.doDismissError = doDismissError;
|
|
exports.doDismissToast = doDismissToast;
|
|
exports.doError = doError;
|
|
exports.doFetchChannelListMine = doFetchChannelListMine;
|
|
exports.doFetchClaimListMine = doFetchClaimListMine;
|
|
exports.doFetchClaimsByChannel = doFetchClaimsByChannel;
|
|
exports.doFetchFileInfo = doFetchFileInfo;
|
|
exports.doFetchFileInfosAndPublishedClaims = doFetchFileInfosAndPublishedClaims;
|
|
exports.doFetchTransactions = doFetchTransactions;
|
|
exports.doFileGet = doFileGet;
|
|
exports.doFileList = doFileList;
|
|
exports.doFocusSearchInput = doFocusSearchInput;
|
|
exports.doGetNewAddress = doGetNewAddress;
|
|
exports.doPrepareEdit = doPrepareEdit;
|
|
exports.doPublish = doPublish;
|
|
exports.doPurchaseUri = doPurchaseUri;
|
|
exports.doResetThumbnailStatus = doResetThumbnailStatus;
|
|
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.doToggleTagFollow = doToggleTagFollow;
|
|
exports.doTotalBalanceSubscribe = doTotalBalanceSubscribe;
|
|
exports.doUpdateBalance = doUpdateBalance;
|
|
exports.doUpdateBlockHeight = doUpdateBlockHeight;
|
|
exports.doUpdatePublishForm = doUpdatePublishForm;
|
|
exports.doUpdateSearchOptions = doUpdateSearchOptions;
|
|
exports.doUpdateSearchQuery = doUpdateSearchQuery;
|
|
exports.doUpdateTotalBalance = doUpdateTotalBalance;
|
|
exports.doUploadThumbnail = doUploadThumbnail;
|
|
exports.doWalletDecrypt = doWalletDecrypt;
|
|
exports.doWalletEncrypt = doWalletEncrypt;
|
|
exports.doWalletStatus = doWalletStatus;
|
|
exports.doWalletUnlock = doWalletUnlock;
|
|
exports.fileInfoReducer = fileInfoReducer;
|
|
exports.fileReducer = fileReducer;
|
|
exports.formatCredits = formatCredits;
|
|
exports.formatFullPrice = formatFullPrice;
|
|
exports.isClaimNsfw = isClaimNsfw;
|
|
exports.isNameValid = isNameValid;
|
|
exports.isURIClaimable = isURIClaimable;
|
|
exports.isURIValid = isURIValid;
|
|
exports.makeSelectChannelForClaimUri = makeSelectChannelForClaimUri;
|
|
exports.makeSelectClaimForUri = makeSelectClaimForUri;
|
|
exports.makeSelectClaimIsMine = makeSelectClaimIsMine;
|
|
exports.makeSelectClaimIsNsfw = makeSelectClaimIsNsfw;
|
|
exports.makeSelectClaimIsPending = makeSelectClaimIsPending;
|
|
exports.makeSelectClaimsInChannelForCurrentPageState = makeSelectClaimsInChannelForCurrentPageState;
|
|
exports.makeSelectClaimsInChannelForPage = makeSelectClaimsInChannelForPage;
|
|
exports.makeSelectCommentsForUri = makeSelectCommentsForUri;
|
|
exports.makeSelectContentPositionForUri = makeSelectContentPositionForUri;
|
|
exports.makeSelectContentTypeForUri = makeSelectContentTypeForUri;
|
|
exports.makeSelectCoverForUri = makeSelectCoverForUri;
|
|
exports.makeSelectDateForUri = makeSelectDateForUri;
|
|
exports.makeSelectDownloadingForUri = makeSelectDownloadingForUri;
|
|
exports.makeSelectFetchingChannelClaims = makeSelectFetchingChannelClaims;
|
|
exports.makeSelectFileInfoForUri = makeSelectFileInfoForUri;
|
|
exports.makeSelectFirstRecommendedFileForUri = makeSelectFirstRecommendedFileForUri;
|
|
exports.makeSelectIsUriResolving = makeSelectIsUriResolving;
|
|
exports.makeSelectLoadingForUri = makeSelectLoadingForUri;
|
|
exports.makeSelectMetadataForUri = makeSelectMetadataForUri;
|
|
exports.makeSelectMetadataItemForUri = makeSelectMetadataItemForUri;
|
|
exports.makeSelectNsfwCountForChannel = makeSelectNsfwCountForChannel;
|
|
exports.makeSelectNsfwCountFromUris = makeSelectNsfwCountFromUris;
|
|
exports.makeSelectPendingByUri = makeSelectPendingByUri;
|
|
exports.makeSelectQueryWithOptions = makeSelectQueryWithOptions;
|
|
exports.makeSelectRecommendedContentForUri = makeSelectRecommendedContentForUri;
|
|
exports.makeSelectSearchUris = makeSelectSearchUris;
|
|
exports.makeSelectStreamingUrlForUri = makeSelectStreamingUrlForUri;
|
|
exports.makeSelectTagsForUri = makeSelectTagsForUri;
|
|
exports.makeSelectThumbnailForUri = makeSelectThumbnailForUri;
|
|
exports.makeSelectTitleForUri = makeSelectTitleForUri;
|
|
exports.makeSelectTotalItemsForChannel = makeSelectTotalItemsForChannel;
|
|
exports.makeSelectTotalPagesForChannel = makeSelectTotalPagesForChannel;
|
|
exports.normalizeURI = normalizeURI;
|
|
exports.notificationsReducer = notificationsReducer;
|
|
exports.parseQueryParams = parseQueryParams;
|
|
exports.parseURI = parseURI;
|
|
exports.publishReducer = publishReducer;
|
|
exports.regexAddress = regexAddress;
|
|
exports.regexInvalidURI = regexInvalidURI;
|
|
exports.savePosition = savePosition;
|
|
exports.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.selectDownloadedUris = selectDownloadedUris;
|
|
exports.selectDownloadingByOutpoint = selectDownloadingByOutpoint;
|
|
exports.selectDownloadingFileInfos = selectDownloadingFileInfos;
|
|
exports.selectDraftTransaction = selectDraftTransaction;
|
|
exports.selectDraftTransactionAddress = selectDraftTransactionAddress;
|
|
exports.selectDraftTransactionAmount = selectDraftTransactionAmount;
|
|
exports.selectDraftTransactionError = selectDraftTransactionError;
|
|
exports.selectError = selectError;
|
|
exports.selectFailedPurchaseUris = selectFailedPurchaseUris;
|
|
exports.selectFetchingClaimSearch = selectFetchingClaimSearch;
|
|
exports.selectFetchingMyChannels = selectFetchingMyChannels;
|
|
exports.selectFileInfosByOutpoint = selectFileInfosByOutpoint;
|
|
exports.selectFileInfosDownloaded = selectFileInfosDownloaded;
|
|
exports.selectFileListDownloadedSort = selectFileListDownloadedSort;
|
|
exports.selectFileListPublishedSort = selectFileListPublishedSort;
|
|
exports.selectFollowedTags = selectFollowedTags;
|
|
exports.selectGettingNewAddress = selectGettingNewAddress;
|
|
exports.selectHasTransactions = selectHasTransactions;
|
|
exports.selectIsFetchingClaimListMine = selectIsFetchingClaimListMine;
|
|
exports.selectIsFetchingFileList = selectIsFetchingFileList;
|
|
exports.selectIsFetchingFileListDownloadedOrPublished = selectIsFetchingFileListDownloadedOrPublished;
|
|
exports.selectIsFetchingTransactions = selectIsFetchingTransactions;
|
|
exports.selectIsResolvingPublishUris = selectIsResolvingPublishUris;
|
|
exports.selectIsSearching = selectIsSearching;
|
|
exports.selectIsSendingSupport = selectIsSendingSupport;
|
|
exports.selectIsStillEditing = selectIsStillEditing;
|
|
exports.selectLastClaimSearchUris = selectLastClaimSearchUris;
|
|
exports.selectLastPurchasedUri = selectLastPurchasedUri;
|
|
exports.selectMyActiveClaims = selectMyActiveClaims;
|
|
exports.selectMyChannelClaims = selectMyChannelClaims;
|
|
exports.selectMyClaimForUri = selectMyClaimForUri;
|
|
exports.selectMyClaimUrisWithoutChannels = selectMyClaimUrisWithoutChannels;
|
|
exports.selectMyClaims = selectMyClaims;
|
|
exports.selectMyClaimsOutpoints = selectMyClaimsOutpoints;
|
|
exports.selectMyClaimsRaw = selectMyClaimsRaw;
|
|
exports.selectMyClaimsWithoutChannels = selectMyClaimsWithoutChannels;
|
|
exports.selectPendingById = selectPendingById;
|
|
exports.selectPendingClaims = selectPendingClaims;
|
|
exports.selectPlayingUri = selectPlayingUri;
|
|
exports.selectPublishFormValues = selectPublishFormValues;
|
|
exports.selectPurchaseUriErrorMessage = selectPurchaseUriErrorMessage;
|
|
exports.selectPurchasedStreamingUrls = selectPurchasedStreamingUrls;
|
|
exports.selectPurchasedUris = selectPurchasedUris;
|
|
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.selectSupportsByOutpoint = selectSupportsByOutpoint;
|
|
exports.selectTakeOverAmount = selectTakeOverAmount;
|
|
exports.selectToast = selectToast;
|
|
exports.selectTotalBalance = selectTotalBalance;
|
|
exports.selectTotalDownloadProgress = selectTotalDownloadProgress;
|
|
exports.selectTransactionItems = selectTransactionItems;
|
|
exports.selectTransactionListFilter = selectTransactionListFilter;
|
|
exports.selectTransactionsById = selectTransactionsById;
|
|
exports.selectUnfollowedTags = selectUnfollowedTags;
|
|
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.tagsReducerBuilder = tagsReducerBuilder;
|
|
exports.toQueryString = toQueryString;
|
|
exports.walletReducer = walletReducer;
|