diff --git a/dist/bundle.es.js b/dist/bundle.es.js index 8abd424..f29e5ca 100644 --- a/dist/bundle.es.js +++ b/dist/bundle.es.js @@ -8,6 +8,19 @@ 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'; @@ -241,219 +254,284 @@ 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 + 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 API_DOWN = 'apiDown'; -const READY = 'ready'; -const IN_PROGRESS = 'inProgress'; -const COMPLETE = 'complete'; -const MANUAL = 'manual'; +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' +}]; -var thumbnail_upload_statuses = /*#__PURE__*/Object.freeze({ - API_DOWN: API_DOWN, - READY: READY, - IN_PROGRESS: IN_PROGRESS, - COMPLETE: COMPLETE, - MANUAL: MANUAL +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 */ @@ -477,43 +555,20 @@ const FOREGROUND_NOTIFICATION_ENABLED = 'foregroundNotificationEnabled'; const KEEP_DAEMON_RUNNING = 'keepDaemonRunning'; var settings = /*#__PURE__*/Object.freeze({ - CREDIT_REQUIRED_ACKNOWLEDGED: CREDIT_REQUIRED_ACKNOWLEDGED, - NEW_USER_ACKNOWLEDGED: NEW_USER_ACKNOWLEDGED, - EMAIL_COLLECTION_ACKNOWLEDGED: EMAIL_COLLECTION_ACKNOWLEDGED, - LANGUAGE: LANGUAGE, - SHOW_NSFW: SHOW_NSFW, - SHOW_UNAVAILABLE: SHOW_UNAVAILABLE, - INSTANT_PURCHASE_ENABLED: INSTANT_PURCHASE_ENABLED, - INSTANT_PURCHASE_MAX: INSTANT_PURCHASE_MAX, - THEME: THEME, - THEMES: THEMES, - AUTOMATIC_DARK_MODE_ENABLED: AUTOMATIC_DARK_MODE_ENABLED, - BACKGROUND_PLAY_ENABLED: BACKGROUND_PLAY_ENABLED, - FOREGROUND_NOTIFICATION_ENABLED: FOREGROUND_NOTIFICATION_ENABLED, - KEEP_DAEMON_RUNNING: KEEP_DAEMON_RUNNING -}); - -// eslint-disable-next-line import/prefer-default-export -const ALL = 'all'; -const SPEND = 'spend'; -const RECEIVE = 'receive'; -const PUBLISH = 'publish'; -const CHANNEL = 'channel'; -const TIP = 'tip'; -const SUPPORT = 'support'; -const UPDATE = 'update'; -const ABANDON = 'abandon'; - -var transaction_types = /*#__PURE__*/Object.freeze({ - ALL: ALL, - SPEND: SPEND, - RECEIVE: RECEIVE, - PUBLISH: PUBLISH, - CHANNEL: CHANNEL, - TIP: TIP, - SUPPORT: SUPPORT, - UPDATE: UPDATE, - ABANDON: ABANDON + 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'; @@ -522,56 +577,47 @@ const TITLE = 'title'; const FILENAME = 'filename'; var sort_options = /*#__PURE__*/Object.freeze({ - DATE_NEW: DATE_NEW, - DATE_OLD: DATE_OLD, - TITLE: TITLE, - FILENAME: FILENAME + DATE_NEW: DATE_NEW, + DATE_OLD: DATE_OLD, + TITLE: TITLE, + FILENAME: FILENAME }); -const AUTH = 'auth'; -const BACKUP = 'backup'; -const CHANNEL$1 = 'channel'; -const DISCOVER = 'discover'; -const FILE = 'file'; -const DOWNLOADED = 'downloaded'; -const PUBLISHED = 'published'; -const GET_CREDITS = 'getcredits'; -const HELP = 'help'; -const INVITE = 'invite'; -const PUBLISH$1 = 'publish'; -const REPORT = 'report'; -const REWARDS = 'rewards'; -const SEARCH = 'search'; -const SEND_CREDITS = 'send'; -const SETTINGS = 'settings'; -const SHOW = 'show'; -const SUBSCRIPTIONS = 'subscriptions'; -const TRANSACTION_HISTORY = 'history'; -const HISTORY = 'user_history'; -const WALLET = 'wallet'; +const API_DOWN = 'apiDown'; +const READY = 'ready'; +const IN_PROGRESS = 'inProgress'; +const COMPLETE = 'complete'; +const MANUAL = 'manual'; -var pages = /*#__PURE__*/Object.freeze({ - AUTH: AUTH, - BACKUP: BACKUP, - CHANNEL: CHANNEL$1, - DISCOVER: DISCOVER, - FILE: FILE, - DOWNLOADED: DOWNLOADED, - PUBLISHED: PUBLISHED, - GET_CREDITS: GET_CREDITS, - HELP: HELP, - INVITE: INVITE, - PUBLISH: PUBLISH$1, - REPORT: REPORT, - REWARDS: REWARDS, - SEARCH: SEARCH, - SEND_CREDITS: SEND_CREDITS, - SETTINGS: SETTINGS, - SHOW: SHOW, - SUBSCRIPTIONS: SUBSCRIPTIONS, - TRANSACTION_HISTORY: TRANSACTION_HISTORY, - HISTORY: HISTORY, - WALLET: WALLET +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 = { @@ -1493,7 +1539,7 @@ const selectTransactionItems = reselect.createSelector(selectTransactionsById, b const append = []; append.push(...tx.claim_info.map(item => Object.assign({}, tx, item, { - type: item.claim_name[0] === '@' ? CHANNEL : PUBLISH + 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 @@ -2578,6 +2624,372 @@ function doSetFileListSort(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 @@ -2844,7 +3256,7 @@ function doCommentCreate(comment = '', claim_id = '', channel, parent_id) { }; } -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; }; +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 = { @@ -2904,7 +3316,7 @@ function handleClaimAction(state, action) { } reducers[RESOLVE_URIS_COMPLETED] = (state, action) => { - return _extends$4({}, handleClaimAction(state, action)); + return _extends$5({}, handleClaimAction(state, action)); }; reducers[FETCH_CLAIM_LIST_MINE_STARTED] = state => Object.assign({}, state, { @@ -3085,7 +3497,7 @@ reducers[CLAIM_SEARCH_STARTED] = state => { }); }; reducers[CLAIM_SEARCH_COMPLETED] = (state, action) => { - return _extends$4({}, handleClaimAction(state, action), { + return _extends$5({}, handleClaimAction(state, action), { fetchingClaimSearch: false, lastClaimSearchUris: action.data.uris }); @@ -3102,89 +3514,106 @@ function claimsReducer(state = defaultState, action) { return state; } -var _extends$5 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +// 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$1 = { - failedPurchaseUris: [], - purchasedUris: [], - purchasedStreamingUrls: {}, - purchaseUriErrorMessage: '' +const defaultState$2 = { + positions: {} }; -reducers$1[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$5({}, state, { - failedPurchaseUris: newFailedPurchaseUris, - purchaseUriErrorMessage: '' +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 + }) + }) }); }; -reducers$1[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$5({}, state, { - failedPurchaseUris: newFailedPurchaseUris, - purchasedUris: newPurchasedUris, - purchasedStreamingUrls: newPurchasedStreamingUrls, - purchaseUriErrorMessage: '' - }); -}; - -reducers$1[PURCHASE_URI_FAILED] = (state, action) => { - const { uri, error } = action.data; - const newFailedPurchaseUris = state.failedPurchaseUris.slice(); - - if (!newFailedPurchaseUris.includes(uri)) { - newFailedPurchaseUris.push(uri); - } - - return _extends$5({}, state, { - failedPurchaseUris: newFailedPurchaseUris, - purchaseUriErrorMessage: error - }); -}; - -reducers$1[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$5({}, state, { - purchasedUris: newPurchasedUris - }); -}; - -function fileReducer(state = defaultState$1, action) { +function contentReducer(state = defaultState$2, action) { const handler = reducers$1[action.type]; if (handler) return handler(state, action); 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; }; +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$2 = { +const defaultState$3 = { fileListPublishedSort: DATE_NEW, fileListDownloadedSort: DATE_NEW }; @@ -3317,12 +3746,12 @@ reducers$2[LOADING_VIDEO_STARTED] = (state, action) => { const newLoading = Object.assign({}, state.urisLoading); newLoading[uri] = true; - const newErrors = _extends$6({}, state.errors); + const newErrors = _extends$8({}, state.errors); if (uri in newErrors) delete newErrors[uri]; return Object.assign({}, state, { urisLoading: newLoading, - errors: _extends$6({}, newErrors) + errors: _extends$8({}, newErrors) }); }; @@ -3332,12 +3761,12 @@ reducers$2[LOADING_VIDEO_FAILED] = (state, action) => { const newLoading = Object.assign({}, state.urisLoading); delete newLoading[uri]; - const newErrors = _extends$6({}, state.errors); + const newErrors = _extends$8({}, state.errors); newErrors[uri] = true; return Object.assign({}, state, { urisLoading: newLoading, - errors: _extends$6({}, newErrors) + errors: _extends$8({}, newErrors) }); }; @@ -3354,33 +3783,94 @@ reducers$2[SET_FILE_LIST_SORT] = (state, action) => { }); }; -function fileInfoReducer(state = defaultState$2, action) { +function fileInfoReducer(state = defaultState$3, action) { const handler = reducers$2[action.type]; if (handler) return handler(state, action); return state; } -// util for creating reducers -// based off of redux-actions -// https://redux-actions.js.org/docs/api/handleAction.html#handleactions +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; }; -// 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; +const reducers$3 = {}; +const defaultState$4 = { + failedPurchaseUris: [], + purchasedUris: [], + purchasedStreamingUrls: {}, + purchaseUriErrorMessage: '' }; -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; }; +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); + } -const defaultState$3 = { + 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: [] @@ -3393,7 +3883,7 @@ const notificationsReducer = handleActions({ const newToasts = state.toasts.slice(); newToasts.push(toast); - return _extends$7({}, state, { + return _extends$a({}, state, { toasts: newToasts }); }, @@ -3401,7 +3891,7 @@ const notificationsReducer = handleActions({ const newToasts = state.toasts.slice(); newToasts.shift(); - return _extends$7({}, state, { + return _extends$a({}, state, { toasts: newToasts }); }, @@ -3412,7 +3902,7 @@ const notificationsReducer = handleActions({ const newNotifications = state.notifications.slice(); newNotifications.push(notification); - return _extends$7({}, state, { + return _extends$a({}, state, { notifications: newNotifications }); }, @@ -3423,7 +3913,7 @@ const notificationsReducer = handleActions({ notifications = notifications.map(pastNotification => pastNotification.id === notification.id ? notification : pastNotification); - return _extends$7({}, state, { + return _extends$a({}, state, { notifications }); }, @@ -3432,7 +3922,7 @@ const notificationsReducer = handleActions({ let newNotifications = state.notifications.slice(); newNotifications = newNotifications.filter(notification => notification.id !== id); - return _extends$7({}, state, { + return _extends$a({}, state, { notifications: newNotifications }); }, @@ -3443,7 +3933,7 @@ const notificationsReducer = handleActions({ const newErrors = state.errors.slice(); newErrors.push(error); - return _extends$7({}, state, { + return _extends$a({}, state, { errors: newErrors }); }, @@ -3451,15 +3941,83 @@ const notificationsReducer = handleActions({ const newErrors = state.errors.slice(); newErrors.shift(); - return _extends$7({}, state, { + return _extends$a({}, state, { errors: newErrors }); } -}, defaultState$3); +}, defaultState$5); -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; }; +var _extends$b = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; -const defaultState$4 = { +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 @@ -3477,29 +4035,29 @@ const defaultState$4 = { }; const searchReducer = handleActions({ - [SEARCH_START]: state => _extends$8({}, state, { + [SEARCH_START]: state => _extends$c({}, state, { searching: true }), [SEARCH_SUCCESS]: (state, action) => { const { query, uris } = action.data; - return _extends$8({}, state, { + return _extends$c({}, state, { searching: false, urisByQuery: Object.assign({}, state.urisByQuery, { [query]: uris }) }); }, - [SEARCH_FAIL]: state => _extends$8({}, state, { + [SEARCH_FAIL]: state => _extends$c({}, state, { searching: false }), - [UPDATE_SEARCH_QUERY]: (state, action) => _extends$8({}, state, { + [UPDATE_SEARCH_QUERY]: (state, action) => _extends$c({}, state, { searchQuery: action.data.query, isActive: true }), - [UPDATE_SEARCH_SUGGESTIONS]: (state, action) => _extends$8({}, state, { - suggestions: _extends$8({}, state.suggestions, { + [UPDATE_SEARCH_SUGGESTIONS]: (state, action) => _extends$c({}, state, { + suggestions: _extends$c({}, state.suggestions, { [action.data.query]: action.data.suggestions }) }), @@ -3507,27 +4065,74 @@ const searchReducer = handleActions({ // 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$8({}, state, { + [DISMISS_NOTIFICATION]: state => _extends$c({}, state, { isActive: false }), - [SEARCH_FOCUS]: state => _extends$8({}, state, { + [SEARCH_FOCUS]: state => _extends$c({}, state, { focused: true }), - [SEARCH_BLUR]: state => _extends$8({}, state, { + [SEARCH_BLUR]: state => _extends$c({}, state, { focused: false }), [UPDATE_SEARCH_OPTIONS]: (state, action) => { const { options: oldOptions } = state; const newOptions = action.data; - const options = _extends$8({}, oldOptions, newOptions); - return _extends$8({}, state, { + const options = _extends$c({}, oldOptions, newOptions); + return _extends$c({}, state, { options }); } -}, defaultState$4); +}, defaultState$7); -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; }; +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, @@ -3538,7 +4143,7 @@ const buildDraftTransaction = () => ({ // See details in https://github.com/lbryio/lbry/issues/1307 -const defaultState$5 = { +const defaultState$8 = { balance: undefined, totalBalance: undefined, latestBlock: undefined, @@ -3567,25 +4172,25 @@ const defaultState$5 = { }; const walletReducer = handleActions({ - [FETCH_TRANSACTIONS_STARTED]: state => _extends$9({}, state, { + [FETCH_TRANSACTIONS_STARTED]: state => _extends$e({}, state, { fetchingTransactions: true }), [FETCH_TRANSACTIONS_COMPLETED]: (state, action) => { - const byId = _extends$9({}, state.transactions); + const byId = _extends$e({}, state.transactions); const { transactions } = action.data; transactions.forEach(transaction => { byId[transaction.txid] = transaction; }); - return _extends$9({}, state, { + return _extends$e({}, state, { transactions: byId, fetchingTransactions: false }); }, - [FETCH_SUPPORTS_STARTED]: state => _extends$9({}, state, { + [FETCH_SUPPORTS_STARTED]: state => _extends$e({}, state, { fetchingSupports: true }), @@ -3598,7 +4203,7 @@ const walletReducer = handleActions({ byOutpoint[`${txid}:${nout}`] = transaction; }); - return _extends$9({}, state, { supports: byOutpoint, fetchingSupports: false }); + return _extends$e({}, state, { supports: byOutpoint, fetchingSupports: false }); }, [ABANDON_SUPPORT_STARTED]: (state, action) => { @@ -3607,7 +4212,7 @@ const walletReducer = handleActions({ currentlyAbandoning[outpoint] = true; - return _extends$9({}, state, { + return _extends$e({}, state, { abandoningSupportsByOutpoint: currentlyAbandoning }); }, @@ -3620,56 +4225,56 @@ const walletReducer = handleActions({ delete currentlyAbandoning[outpoint]; delete byOutpoint[outpoint]; - return _extends$9({}, state, { + return _extends$e({}, state, { supports: byOutpoint, abandoningSupportsById: currentlyAbandoning }); }, - [GET_NEW_ADDRESS_STARTED]: state => _extends$9({}, state, { + [GET_NEW_ADDRESS_STARTED]: state => _extends$e({}, state, { gettingNewAddress: true }), [GET_NEW_ADDRESS_COMPLETED]: (state, action) => { const { address } = action.data; - return _extends$9({}, state, { gettingNewAddress: false, receiveAddress: address }); + return _extends$e({}, state, { gettingNewAddress: false, receiveAddress: address }); }, - [UPDATE_BALANCE]: (state, action) => _extends$9({}, state, { + [UPDATE_BALANCE]: (state, action) => _extends$e({}, state, { balance: action.data.balance }), - [UPDATE_TOTAL_BALANCE]: (state, action) => _extends$9({}, state, { + [UPDATE_TOTAL_BALANCE]: (state, action) => _extends$e({}, state, { totalBalance: action.data.totalBalance }), - [CHECK_ADDRESS_IS_MINE_STARTED]: state => _extends$9({}, state, { + [CHECK_ADDRESS_IS_MINE_STARTED]: state => _extends$e({}, state, { checkingAddressOwnership: true }), - [CHECK_ADDRESS_IS_MINE_COMPLETED]: state => _extends$9({}, state, { + [CHECK_ADDRESS_IS_MINE_COMPLETED]: state => _extends$e({}, state, { checkingAddressOwnership: false }), [SET_DRAFT_TRANSACTION_AMOUNT]: (state, action) => { const oldDraft = state.draftTransaction; - const newDraft = _extends$9({}, oldDraft, { amount: parseFloat(action.data.amount) }); + const newDraft = _extends$e({}, oldDraft, { amount: parseFloat(action.data.amount) }); - return _extends$9({}, state, { draftTransaction: newDraft }); + return _extends$e({}, state, { draftTransaction: newDraft }); }, [SET_DRAFT_TRANSACTION_ADDRESS]: (state, action) => { const oldDraft = state.draftTransaction; - const newDraft = _extends$9({}, oldDraft, { address: action.data.address }); + const newDraft = _extends$e({}, oldDraft, { address: action.data.address }); - return _extends$9({}, state, { draftTransaction: newDraft }); + return _extends$e({}, state, { draftTransaction: newDraft }); }, [SEND_TRANSACTION_STARTED]: state => { - const newDraftTransaction = _extends$9({}, state.draftTransaction, { sending: true }); + const newDraftTransaction = _extends$e({}, state.draftTransaction, { sending: true }); - return _extends$9({}, state, { draftTransaction: newDraftTransaction }); + return _extends$e({}, state, { draftTransaction: newDraftTransaction }); }, [SEND_TRANSACTION_COMPLETED]: state => Object.assign({}, state, { @@ -3682,231 +4287,106 @@ const walletReducer = handleActions({ error: action.data.error }); - return _extends$9({}, state, { draftTransaction: newDraftTransaction }); + return _extends$e({}, state, { draftTransaction: newDraftTransaction }); }, - [SUPPORT_TRANSACTION_STARTED]: state => _extends$9({}, state, { + [SUPPORT_TRANSACTION_STARTED]: state => _extends$e({}, state, { sendingSupport: true }), - [SUPPORT_TRANSACTION_COMPLETED]: state => _extends$9({}, state, { + [SUPPORT_TRANSACTION_COMPLETED]: state => _extends$e({}, state, { sendingSupport: false }), - [SUPPORT_TRANSACTION_FAILED]: (state, action) => _extends$9({}, state, { + [SUPPORT_TRANSACTION_FAILED]: (state, action) => _extends$e({}, state, { error: action.data.error, sendingSupport: false }), - [WALLET_STATUS_COMPLETED]: (state, action) => _extends$9({}, state, { + [WALLET_STATUS_COMPLETED]: (state, action) => _extends$e({}, state, { walletIsEncrypted: action.result }), - [WALLET_ENCRYPT_START]: state => _extends$9({}, state, { + [WALLET_ENCRYPT_START]: state => _extends$e({}, state, { walletEncryptPending: true, walletEncryptSucceded: null, walletEncryptResult: null }), - [WALLET_ENCRYPT_COMPLETED]: (state, action) => _extends$9({}, state, { + [WALLET_ENCRYPT_COMPLETED]: (state, action) => _extends$e({}, state, { walletEncryptPending: false, walletEncryptSucceded: true, walletEncryptResult: action.result }), - [WALLET_ENCRYPT_FAILED]: (state, action) => _extends$9({}, state, { + [WALLET_ENCRYPT_FAILED]: (state, action) => _extends$e({}, state, { walletEncryptPending: false, walletEncryptSucceded: false, walletEncryptResult: action.result }), - [WALLET_DECRYPT_START]: state => _extends$9({}, state, { + [WALLET_DECRYPT_START]: state => _extends$e({}, state, { walletDecryptPending: true, walletDecryptSucceded: null, walletDecryptResult: null }), - [WALLET_DECRYPT_COMPLETED]: (state, action) => _extends$9({}, state, { + [WALLET_DECRYPT_COMPLETED]: (state, action) => _extends$e({}, state, { walletDecryptPending: false, walletDecryptSucceded: true, walletDecryptResult: action.result }), - [WALLET_DECRYPT_FAILED]: (state, action) => _extends$9({}, state, { + [WALLET_DECRYPT_FAILED]: (state, action) => _extends$e({}, state, { walletDecryptPending: false, walletDecryptSucceded: false, walletDecryptResult: action.result }), - [WALLET_UNLOCK_START]: state => _extends$9({}, state, { + [WALLET_UNLOCK_START]: state => _extends$e({}, state, { walletUnlockPending: true, walletUnlockSucceded: null, walletUnlockResult: null }), - [WALLET_UNLOCK_COMPLETED]: (state, action) => _extends$9({}, state, { + [WALLET_UNLOCK_COMPLETED]: (state, action) => _extends$e({}, state, { walletUnlockPending: false, walletUnlockSucceded: true, walletUnlockResult: action.result }), - [WALLET_UNLOCK_FAILED]: (state, action) => _extends$9({}, state, { + [WALLET_UNLOCK_FAILED]: (state, action) => _extends$e({}, state, { walletUnlockPending: false, walletUnlockSucceded: false, walletUnlockResult: action.result }), - [WALLET_LOCK_START]: state => _extends$9({}, state, { + [WALLET_LOCK_START]: state => _extends$e({}, state, { walletLockPending: false, walletLockSucceded: null, walletLockResult: null }), - [WALLET_LOCK_COMPLETED]: (state, action) => _extends$9({}, state, { + [WALLET_LOCK_COMPLETED]: (state, action) => _extends$e({}, state, { walletLockPending: false, walletLockSucceded: true, walletLockResult: action.result }), - [WALLET_LOCK_FAILED]: (state, action) => _extends$9({}, state, { + [WALLET_LOCK_FAILED]: (state, action) => _extends$e({}, state, { walletLockPending: false, walletLockSucceded: false, walletLockResult: action.result }), - [SET_TRANSACTION_LIST_FILTER]: (state, action) => _extends$9({}, state, { + [SET_TRANSACTION_LIST_FILTER]: (state, action) => _extends$e({}, state, { transactionListFilter: action.data }), - [UPDATE_CURRENT_HEIGHT]: (state, action) => _extends$9({}, state, { + [UPDATE_CURRENT_HEIGHT]: (state, action) => _extends$e({}, state, { latestBlock: action.data }) -}, defaultState$5); - -var _extends$a = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; - -const reducers$3 = {}; -const defaultState$6 = { - positions: {} -}; - -reducers$3[SET_CONTENT_POSITION] = (state, action) => { - const { claimId, outpoint, position } = action.data; - return _extends$a({}, state, { - positions: _extends$a({}, state.positions, { - [claimId]: _extends$a({}, state.positions[claimId], { - [outpoint]: position - }) - }) - }); -}; - -function contentReducer(state = defaultState$6, action) { - const handler = reducers$3[action.type]; - if (handler) return handler(state, action); - return state; -} - -var _extends$b = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; - -const defaultState$7 = { - byId: {}, - commentsByUri: {}, - isLoading: false -}; - -const commentReducer = handleActions({ - [COMMENT_CREATE_STARTED]: (state, action) => _extends$b({}, state, { - isLoading: true - }), - - [COMMENT_CREATE_FAILED]: (state, action) => _extends$b({}, 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$b({}, state, { - byId - }); - }, - - [COMMENT_LIST_STARTED]: state => _extends$b({}, 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$b({}, state, { - byId, - commentsByUri, - isLoading: false - }); - }, - - [COMMENT_LIST_FAILED]: (state, action) => _extends$b({}, state, { - isLoading: false - }) -}, defaultState$7); - -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 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$c({}, state, { - followedTags: newFollowedTags - }); - }, - - [TAG_ADD]: (state, action) => { - const { knownTags } = state; - const { name } = action.data; - - let newKnownTags = _extends$c({}, knownTags); - newKnownTags[name] = { name }; - - return _extends$c({}, state, { - knownTags: newKnownTags - }); - }, - - [TAG_DELETE]: (state, action) => { - const { knownTags, followedTags } = state; - const { name } = action.data; - - let newKnownTags = _extends$c({}, knownTags); - delete newKnownTags[name]; - const newFollowedTags = followedTags.filter(tag => tag !== name); - - return _extends$c({}, state, { - knownTags: newKnownTags, - followedTags: newFollowedTags - }); - } -}, defaultState); +}, defaultState$8); const selectState$5 = state => state.content || {}; @@ -3919,14 +4399,14 @@ const makeSelectContentPositionForUri = uri => reselect.createSelector(selectSta return state.positions[id] ? state.positions[id][outpoint] : null; }); -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; }; +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$d({ + return _extends$f({ id }, params); } @@ -3970,13 +4450,85 @@ const makeSelectCommentsForUri = uri => reselect.createSelector(selectCommentsBy 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$8 = state => state.tags || {}; +const selectState$9 = state => state.tags || {}; -const selectKnownTagsByName = reselect.createSelector(selectState$8, state => state.knownTags); +const selectKnownTagsByName = reselect.createSelector(selectState$9, state => state.knownTags); -const selectFollowedTagsList = reselect.createSelector(selectState$8, state => state.followedTags); +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))); @@ -3995,6 +4547,8 @@ const selectUnfollowedTags = reselect.createSelector(selectKnownTagsByName, sele }); exports.ACTIONS = action_types; +exports.CLAIM_VALUES = claim; +exports.LICENSES = licenses; exports.Lbry = lbryProxy; exports.PAGES = pages; exports.SEARCH_OPTIONS = SEARCH_OPTIONS; @@ -4015,7 +4569,9 @@ 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; @@ -4034,7 +4590,10 @@ 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; @@ -4049,9 +4608,11 @@ 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; @@ -4100,6 +4661,7 @@ exports.normalizeURI = normalizeURI; exports.notificationsReducer = notificationsReducer; exports.parseQueryParams = parseQueryParams; exports.parseURI = parseURI; +exports.publishReducer = publishReducer; exports.regexAddress = regexAddress; exports.regexInvalidURI = regexInvalidURI; exports.savePosition = savePosition; @@ -4136,12 +4698,15 @@ 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; @@ -4150,6 +4715,7 @@ exports.selectMyClaimsWithoutChannels = selectMyClaimsWithoutChannels; exports.selectPendingById = selectPendingById; exports.selectPendingClaims = selectPendingClaims; exports.selectPlayingUri = selectPlayingUri; +exports.selectPublishFormValues = selectPublishFormValues; exports.selectPurchaseUriErrorMessage = selectPurchaseUriErrorMessage; exports.selectPurchasedStreamingUrls = selectPurchasedStreamingUrls; exports.selectPurchasedUris = selectPurchasedUris; @@ -4164,6 +4730,7 @@ exports.selectSearchSuggestions = selectSearchSuggestions; exports.selectSearchUrisByQuery = selectSearchUrisByQuery; exports.selectSearchValue = selectSearchValue; exports.selectSupportsByOutpoint = selectSupportsByOutpoint; +exports.selectTakeOverAmount = selectTakeOverAmount; exports.selectToast = selectToast; exports.selectTotalBalance = selectTotalBalance; exports.selectTotalDownloadProgress = selectTotalDownloadProgress; diff --git a/dist/flow-typed/Publish.js b/dist/flow-typed/Publish.js new file mode 100644 index 0000000..0d1cc0a --- /dev/null +++ b/dist/flow-typed/Publish.js @@ -0,0 +1,51 @@ +// @flow + +declare type UpdatePublishFormData = { + filePath?: string, + contentIsFree?: boolean, + fee?: { + amount: string, + currency: string, + }, + title?: string, + thumbnail_url?: string, + uploadThumbnailStatus?: string, + thumbnailPath?: string, + description?: string, + language?: string, + channel?: string, + channelId?: string, + name?: string, + nameError?: string, + bid?: number, + bidError?: string, + otherLicenseDescription?: string, + licenseUrl?: string, + licenseType?: string, + uri?: string, + nsfw: boolean, +}; + +declare type PublishParams = { + name: ?string, + bid: ?number, + filePath?: string, + description: ?string, + language: string, + publishingLicense?: string, + publishingLicenseUrl?: string, + thumbnail: ?string, + channel: string, + channelId?: string, + title: string, + contentIsFree: boolean, + uri?: string, + license: ?string, + licenseUrl: ?string, + fee?: { + amount: string, + currency: string, + }, + claim: StreamClaim, + nsfw: boolean, +}; diff --git a/flow-typed/Publish.js b/flow-typed/Publish.js new file mode 100644 index 0000000..0d1cc0a --- /dev/null +++ b/flow-typed/Publish.js @@ -0,0 +1,51 @@ +// @flow + +declare type UpdatePublishFormData = { + filePath?: string, + contentIsFree?: boolean, + fee?: { + amount: string, + currency: string, + }, + title?: string, + thumbnail_url?: string, + uploadThumbnailStatus?: string, + thumbnailPath?: string, + description?: string, + language?: string, + channel?: string, + channelId?: string, + name?: string, + nameError?: string, + bid?: number, + bidError?: string, + otherLicenseDescription?: string, + licenseUrl?: string, + licenseType?: string, + uri?: string, + nsfw: boolean, +}; + +declare type PublishParams = { + name: ?string, + bid: ?number, + filePath?: string, + description: ?string, + language: string, + publishingLicense?: string, + publishingLicenseUrl?: string, + thumbnail: ?string, + channel: string, + channelId?: string, + title: string, + contentIsFree: boolean, + uri?: string, + license: ?string, + licenseUrl: ?string, + fee?: { + amount: string, + currency: string, + }, + claim: StreamClaim, + nsfw: boolean, +}; diff --git a/src/constants/claim.js b/src/constants/claim.js new file mode 100644 index 0000000..4cf33ce --- /dev/null +++ b/src/constants/claim.js @@ -0,0 +1,5 @@ +export const MINIMUM_PUBLISH_BID = 0.00000001; + +export const CHANNEL_ANONYMOUS = 'anonymous'; +export const CHANNEL_NEW = 'new'; +export const PAGE_SIZE = 20; diff --git a/src/constants/licenses.js b/src/constants/licenses.js new file mode 100644 index 0000000..fbcc7e6 --- /dev/null +++ b/src/constants/licenses.js @@ -0,0 +1,31 @@ +export 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', + }, +]; + +export const NONE = 'None'; +export const PUBLIC_DOMAIN = 'Public Domain'; +export const OTHER = 'other'; +export const COPYRIGHT = 'copyright'; diff --git a/src/index.js b/src/index.js index 0e88ed3..5b861a1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,11 @@ +import * as CLAIM_VALUES from 'constants/claim'; import * as ACTIONS from 'constants/action_types'; -import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses'; -import * as SETTINGS from 'constants/settings'; -import * as TRANSACTIONS from 'constants/transaction_types'; -import * as SORT_OPTIONS from 'constants/sort_options'; +import * as LICENSES from 'constants/licenses'; import * as PAGES from 'constants/pages'; +import * as SETTINGS from 'constants/settings'; +import * as SORT_OPTIONS from 'constants/sort_options'; +import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses'; +import * as TRANSACTIONS from 'constants/transaction_types'; import { SEARCH_TYPES, SEARCH_OPTIONS } from 'constants/search'; import Lbry from 'lbry'; import { selectState as selectSearchState } from 'redux/selectors/search'; @@ -11,6 +13,8 @@ import { selectState as selectSearchState } from 'redux/selectors/search'; // constants export { ACTIONS, + CLAIM_VALUES, + LICENSES, THUMBNAIL_STATUSES, SEARCH_TYPES, SEARCH_OPTIONS, @@ -57,6 +61,16 @@ export { doSetFileListSort, } from 'redux/actions/file_info'; +export { + doResetThumbnailStatus, + doClearPublish, + doUpdatePublishForm, + doUploadThumbnail, + doPrepareEdit, + doPublish, + doCheckPendingPublishes +} from 'redux/actions/publish'; + export { doSearch, doUpdateSearchQuery, @@ -100,14 +114,15 @@ export { isClaimNsfw } from 'util/claim'; // reducers export { claimsReducer } from 'redux/reducers/claims'; -export { fileReducer } from 'redux/reducers/file'; -export { fileInfoReducer } from 'redux/reducers/file_info'; -export { notificationsReducer } from 'redux/reducers/notifications'; -export { searchReducer } from 'redux/reducers/search'; -export { walletReducer } from 'redux/reducers/wallet'; -export { contentReducer } from 'redux/reducers/content'; export { commentReducer } from 'redux/reducers/comments'; +export { contentReducer } from 'redux/reducers/content'; +export { fileInfoReducer } from 'redux/reducers/file_info'; +export { fileReducer } from 'redux/reducers/file'; +export { notificationsReducer } from 'redux/reducers/notifications'; +export { publishReducer } from 'redux/reducers/publish'; +export { searchReducer } from 'redux/reducers/search'; export { tagsReducerBuilder } from 'redux/reducers/tags'; +export { walletReducer } from 'redux/reducers/wallet'; // selectors export { makeSelectContentPositionForUri } from 'redux/selectors/content'; @@ -193,6 +208,14 @@ export { selectDownloadedUris, } from 'redux/selectors/file_info'; +export { + selectPublishFormValues, + selectIsStillEditing, + selectMyClaimForUri, + selectIsResolvingPublishUris, + selectTakeOverAmount, +} from 'redux/selectors/publish'; + export { selectSearchState }; export { makeSelectSearchUris, diff --git a/src/redux/actions/publish.js b/src/redux/actions/publish.js new file mode 100644 index 0000000..ad98252 --- /dev/null +++ b/src/redux/actions/publish.js @@ -0,0 +1,404 @@ +// @flow +import { CC_LICENSES, COPYRIGHT, OTHER, NONE, PUBLIC_DOMAIN } from 'constants/licenses'; +import * as ACTIONS from 'constants/action_types'; +//import * as MODALS from 'constants/modal_types'; +import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses'; +import Lbry from 'lbry'; +import { batchActions } from 'util/batchActions'; +import { creditsToString } from 'util/formatCredits'; +import { doError } from 'redux/actions/notifications'; +import { isClaimNsfw } from 'util/claim'; +import { + selectMyChannelClaims, + selectPendingById, + selectMyClaimsWithoutChannels, +} from 'redux/selectors/claims'; +import { formatLbryUriForWeb } from 'util/uri'; + +export const doResetThumbnailStatus = () => (dispatch: Dispatch) => { + dispatch({ + type: ACTIONS.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: ACTIONS.UPDATE_PUBLISH_FORM, + data: { + uploadThumbnailStatus: THUMBNAIL_STATUSES.READY, + thumbnail: '', + }, + }); + }) + .catch(() => + dispatch({ + type: ACTIONS.UPDATE_PUBLISH_FORM, + data: { + uploadThumbnailStatus: THUMBNAIL_STATUSES.API_DOWN, + thumbnail: '', + }, + }) + ); +}; + +export const doClearPublish = () => (dispatch: Dispatch) => { + dispatch({ type: ACTIONS.CLEAR_PUBLISH }); + return dispatch(doResetThumbnailStatus()); +}; + +export const doUpdatePublishForm = (publishFormValue: UpdatePublishFormData) => (dispatch: Dispatch) => + dispatch({ + type: ACTIONS.UPDATE_PUBLISH_FORM, + data: { ...publishFormValue }, + }); + +export const doUploadThumbnail = (filePath: string, thumbnailBuffer: Uint8Array, fsAdapter: any) => (dispatch: 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: ACTIONS.UPDATE_PUBLISH_FORM, + data: { + uploadThumbnailStatus: THUMBNAIL_STATUSES.READY, + thumbnail: '', + nsfw: false, + }, + }, + doError(error) + ) + ); + } + + dispatch({ + type: ACTIONS.UPDATE_PUBLISH_FORM, + data: { uploadThumbnailStatus: THUMBNAIL_STATUSES.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: ACTIONS.UPDATE_PUBLISH_FORM, + data: { + uploadThumbnailStatus: THUMBNAIL_STATUSES.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: ACTIONS.UPDATE_PUBLISH_FORM, + data: { + uploadThumbnailStatus: THUMBNAIL_STATUSES.COMPLETE, + thumbnail: `${json.data.url}${fileExt}`, + }, + }) + : uploadError(json.message) + ) + .catch(err => uploadError(err.message)); + } +}; + +export const doPrepareEdit = (claim: StreamClaim, uri: string, fileInfo: FileListItem) => (dispatch: 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: UpdatePublishFormData = { + 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 ? THUMBNAIL_STATUSES.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: ACTIONS.DO_PREPARE_EDIT, data: publishData }); +}; + +export const doPublish = (params: PublishParams) => (dispatch: Dispatch, getState: () => {}) => { + dispatch({ type: ACTIONS.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: ?string, + channel_id?: string, + bid: number, + file_path?: string, + tags: Array, + locations?: Array, + license_url?: string, + thumbnail_url?: string, + release_time?: number, + fee_currency?: string, + fee_amount?: string, + } = { + 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: ACTIONS.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: ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED, + data: { + claims: myNewClaims, + }, + }); + + dispatch(batchActions(...actions)); + }; + + const failure = error => { + dispatch({ type: ACTIONS.PUBLISH_FAIL }); + dispatch(doError(error.message)); + }; + + return Lbry.publish(publishPayload).then(success, failure); +}; + +// Calls claim_list_mine until any pending publishes are confirmed +export const doCheckPendingPublishes = () => (dispatch: Dispatch, getState: GetState) => { + const state = getState(); + const pendingById = selectPendingById(state); + + if (!Object.keys(pendingById).length) { + return; + } + + let publishCheckInterval; + + const checkFileList = () => { + Lbry.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: ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED, + data: { + claims, + }, + }); + + if (!Object.keys(pendingById).length) { + clearInterval(publishCheckInterval); + } + }); + }; + + publishCheckInterval = setInterval(() => { + checkFileList(); + }, 30000); +}; diff --git a/src/redux/reducers/publish.js b/src/redux/reducers/publish.js new file mode 100644 index 0000000..310a2ff --- /dev/null +++ b/src/redux/reducers/publish.js @@ -0,0 +1,107 @@ +// @flow +import { handleActions } from 'util/redux-utils'; +import { buildURI } from 'lbryURI'; +import * as ACTIONS from 'constants/action_types'; +import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses'; +import { CHANNEL_ANONYMOUS } from 'constants/claim'; + +type PublishState = { + editingURI: ?string, + filePath: ?string, + contentIsFree: boolean, + fee: { + amount: number, + currency: string, + }, + title: string, + thumbnail_url: string, + thumbnailPath: string, + uploadThumbnailStatus: string, + description: string, + language: string, + channel: string, + channelId: ?string, + name: string, + nameError: ?string, + bid: number, + bidError: ?string, + otherLicenseDescription: string, + licenseUrl: string, +}; + +const defaultState: PublishState = { + editingURI: undefined, + filePath: undefined, + contentIsFree: true, + fee: { + amount: 1, + currency: 'LBC', + }, + title: '', + thumbnail_url: '', + thumbnailPath: '', + uploadThumbnailStatus: THUMBNAIL_STATUSES.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, +}; + +export const publishReducer = handleActions( + { + [ACTIONS.UPDATE_PUBLISH_FORM]: (state, action): PublishState => { + const { data } = action; + return { + ...state, + ...data, + }; + }, + [ACTIONS.CLEAR_PUBLISH]: (): PublishState => ({ + ...defaultState, + }), + [ACTIONS.PUBLISH_START]: (state: PublishState): PublishState => ({ + ...state, + publishing: true, + publishSuccess: false, + }), + [ACTIONS.PUBLISH_FAIL]: (state: PublishState): PublishState => ({ + ...state, + publishing: false, + }), + [ACTIONS.PUBLISH_SUCCESS]: (state: PublishState): PublishState => ({ + ...state, + publishing: false, + publishSuccess: true, + }), + [ACTIONS.DO_PREPARE_EDIT]: (state: PublishState, action) => { + const { ...publishData } = 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 { + ...defaultState, + ...publishData, + editingURI: uri, + uri: shortUri, + }; + }, + }, + defaultState +); diff --git a/src/redux/selectors/publish.js b/src/redux/selectors/publish.js new file mode 100644 index 0000000..893a66f --- /dev/null +++ b/src/redux/selectors/publish.js @@ -0,0 +1,105 @@ +import { createSelector } from 'reselect'; +import { buildURI, parseURI } from 'lbryURI'; +import { + selectClaimsById, + selectMyClaimsWithoutChannels, + selectResolvingUris, + selectClaimsByUri, +} from 'redux/selectors/claims'; + +const selectState = state => state.publish || {}; + +export const selectPublishFormValues = createSelector( + selectState, + state => { + const { pendingPublish, ...formValues } = state; + return formValues; + } +); + +// Is the current uri the same as the uri they clicked "edit" on +export const selectIsStillEditing = 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; + } +); + +export const selectMyClaimForUri = 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 + ); + } +); + +export const selectIsResolvingPublishUris = createSelector( + selectState, + 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; + } +); + +export const selectTakeOverAmount = createSelector( + selectState, + 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; + } +); diff --git a/src/util/uri.js b/src/util/uri.js new file mode 100644 index 0000000..238126a --- /dev/null +++ b/src/util/uri.js @@ -0,0 +1,13 @@ +// @flow +import { parseURI } from 'lbryURI'; + +export const formatLbryUriForWeb = (uri: string) => { + const { claimName, claimId } = parseURI(uri); + + let webUrl = `/${claimName}`; + if (claimId) { + webUrl += `/${claimId}`; + } + + return webUrl; +};