Initial commit
This commit is contained in:
commit
cf9b0cfdaf
18 changed files with 2291 additions and 0 deletions
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
/node_modules
|
||||
/LBRY-darwin-x64
|
||||
/dist
|
||||
|
||||
/src/main/dist
|
||||
/src/main/locales
|
||||
/src/main/node_modules
|
||||
/src/renderer/dist
|
||||
/build/venv
|
||||
/build/daemon.ver
|
||||
/lbry-app-venv
|
||||
/lbry-app
|
||||
/lbry-venv
|
||||
/static/daemon/lbrynet*
|
||||
/static/locales
|
||||
/daemon/build
|
||||
/daemon/venv
|
||||
/daemon/requirements.txt
|
||||
/.idea
|
||||
|
||||
*.pyc
|
||||
*.iml
|
||||
.#*
|
||||
|
||||
build/daemon.zip
|
||||
.vimrc
|
||||
|
||||
package-lock.json
|
||||
|
||||
.DS_Store
|
1085
build/index.js
Normal file
1085
build/index.js
Normal file
File diff suppressed because it is too large
Load diff
111
package.json
Normal file
111
package.json
Normal file
|
@ -0,0 +1,111 @@
|
|||
{
|
||||
"name": "lbry-redux",
|
||||
"version": "0.0.1",
|
||||
"description": "Common shared components for desktop and mobile.",
|
||||
"homepage": "https://lbry.io/",
|
||||
"bugs": {
|
||||
"url": "https://github.com/lbryio/lbry-redux/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lbryio/lbry-redux"
|
||||
},
|
||||
"author": {
|
||||
"name": "LBRY Inc.",
|
||||
"email": "hello@lbry.io"
|
||||
},
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"lint": "eslint 'src/**/*.{js,jsx}' --fix",
|
||||
"format": "prettier 'src/**/*.{js,jsx,scss,json}' --write"
|
||||
},
|
||||
"keywords": [
|
||||
"lbry"
|
||||
],
|
||||
"dependencies": {
|
||||
"amplitude-js": "^4.0.0",
|
||||
"bluebird": "^3.5.1",
|
||||
"classnames": "^2.2.5",
|
||||
"electron-dl": "^1.6.0",
|
||||
"formik": "^0.10.4",
|
||||
"from2": "^2.3.0",
|
||||
"install": "^0.10.2",
|
||||
"jayson": "^2.0.2",
|
||||
"jshashes": "^1.0.7",
|
||||
"keytar": "^4.0.3",
|
||||
"localforage": "^1.5.0",
|
||||
"npm": "^5.5.1",
|
||||
"qrcode.react": "^0.7.2",
|
||||
"rc-progress": "^2.0.6",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-markdown": "^2.5.0",
|
||||
"react-modal": "^3.1.7",
|
||||
"react-paginate": "^5.0.0",
|
||||
"react-redux": "^5.0.3",
|
||||
"react-simplemde-editor": "^3.6.11",
|
||||
"redux": "^3.6.0",
|
||||
"redux-action-buffer": "^1.1.0",
|
||||
"redux-logger": "^3.0.1",
|
||||
"redux-persist": "^4.8.0",
|
||||
"redux-persist-transform-compress": "^4.2.0",
|
||||
"redux-persist-transform-filter": "0.0.10",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"render-media": "^2.10.0",
|
||||
"reselect": "^3.0.0",
|
||||
"semver": "^5.3.0",
|
||||
"shapeshift.io": "^1.3.1",
|
||||
"source-map-support": "^0.5.0",
|
||||
"tree-kill": "^1.1.0",
|
||||
"y18n": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^8.0.3",
|
||||
"babel-plugin-module-resolver": "^3.0.0",
|
||||
"babel-plugin-react-require": "^3.0.0",
|
||||
"babel-polyfill": "^6.20.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-2": "^6.18.0",
|
||||
"devtron": "^1.4.0",
|
||||
"electron": "^1.7.9",
|
||||
"electron-builder": "^19.49.0",
|
||||
"electron-devtools-installer": "^2.2.1",
|
||||
"electron-webpack": "^1.11.0",
|
||||
"eslint": "^4.13.1",
|
||||
"eslint-config-airbnb": "^16.1.0",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-import-resolver-webpack": "^0.8.3",
|
||||
"eslint-plugin-flowtype": "^2.40.1",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.3",
|
||||
"eslint-plugin-prettier": "^2.4.0",
|
||||
"eslint-plugin-react": "^7.5.1",
|
||||
"flow-babel-webpack-plugin": "^1.1.0",
|
||||
"flow-bin": "^0.61.0",
|
||||
"flow-typed": "^2.2.3",
|
||||
"husky": "^0.14.3",
|
||||
"i18n-extract": "^0.5.1",
|
||||
"json-loader": "^0.5.4",
|
||||
"lint-staged": "^6.0.0",
|
||||
"node-loader": "^0.6.0",
|
||||
"node-sass": "^4.7.2",
|
||||
"prettier": "^1.4.2",
|
||||
"sass-loader": "^6.0.6",
|
||||
"webpack": "^3.10.0",
|
||||
"webpack-build-notifier": "^0.1.18"
|
||||
},
|
||||
"resolutions": {
|
||||
"webpack/webpack-sources": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6",
|
||||
"yarn": "^1.3"
|
||||
},
|
||||
"license": "MIT",
|
||||
"lbrySettings": {
|
||||
"lbrynetDaemonVersion": "0.18.0",
|
||||
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-daemon-vDAEMONVER-OSNAME.zip"
|
||||
}
|
||||
}
|
164
src/constants/action_types.js
Normal file
164
src/constants/action_types.js
Normal file
|
@ -0,0 +1,164 @@
|
|||
export const OPEN_MODAL = 'OPEN_MODAL';
|
||||
export const CLOSE_MODAL = 'CLOSE_MODAL';
|
||||
export const SHOW_SNACKBAR = 'SHOW_SNACKBAR';
|
||||
export const REMOVE_SNACKBAR_SNACK = 'REMOVE_SNACKBAR_SNACK';
|
||||
export const WINDOW_FOCUSED = 'WINDOW_FOCUSED';
|
||||
export const DAEMON_READY = 'DAEMON_READY';
|
||||
export const DAEMON_VERSION_MATCH = 'DAEMON_VERSION_MATCH';
|
||||
export const DAEMON_VERSION_MISMATCH = 'DAEMON_VERSION_MISMATCH';
|
||||
export const VOLUME_CHANGED = 'VOLUME_CHANGED';
|
||||
|
||||
// Navigation
|
||||
export const CHANGE_AFTER_AUTH_PATH = 'CHANGE_AFTER_AUTH_PATH';
|
||||
export const WINDOW_SCROLLED = 'WINDOW_SCROLLED';
|
||||
export const HISTORY_NAVIGATE = 'HISTORY_NAVIGATE';
|
||||
|
||||
// Upgrades
|
||||
export const UPGRADE_CANCELLED = 'UPGRADE_CANCELLED';
|
||||
export const DOWNLOAD_UPGRADE = 'DOWNLOAD_UPGRADE';
|
||||
export const UPGRADE_DOWNLOAD_STARTED = 'UPGRADE_DOWNLOAD_STARTED';
|
||||
export const UPGRADE_DOWNLOAD_COMPLETED = 'UPGRADE_DOWNLOAD_COMPLETED';
|
||||
export const UPGRADE_DOWNLOAD_PROGRESSED = 'UPGRADE_DOWNLOAD_PROGRESSED';
|
||||
export const CHECK_UPGRADE_AVAILABLE = 'CHECK_UPGRADE_AVAILABLE';
|
||||
export const CHECK_UPGRADE_START = 'CHECK_UPGRADE_START';
|
||||
export const CHECK_UPGRADE_SUCCESS = 'CHECK_UPGRADE_SUCCESS';
|
||||
export const CHECK_UPGRADE_FAIL = 'CHECK_UPGRADE_FAIL';
|
||||
export const CHECK_UPGRADE_SUBSCRIBE = 'CHECK_UPGRADE_SUBSCRIBE';
|
||||
export const UPDATE_VERSION = 'UPDATE_VERSION';
|
||||
export const UPDATE_REMOTE_VERSION = 'UPDATE_REMOTE_VERSION';
|
||||
export const SKIP_UPGRADE = 'SKIP_UPGRADE';
|
||||
export const START_UPGRADE = 'START_UPGRADE';
|
||||
|
||||
// Wallet
|
||||
export const GET_NEW_ADDRESS_STARTED = 'GET_NEW_ADDRESS_STARTED';
|
||||
export const GET_NEW_ADDRESS_COMPLETED = 'GET_NEW_ADDRESS_COMPLETED';
|
||||
export const FETCH_TRANSACTIONS_STARTED = 'FETCH_TRANSACTIONS_STARTED';
|
||||
export const FETCH_TRANSACTIONS_COMPLETED = 'FETCH_TRANSACTIONS_COMPLETED';
|
||||
export const UPDATE_BALANCE = 'UPDATE_BALANCE';
|
||||
export const CHECK_ADDRESS_IS_MINE_STARTED = 'CHECK_ADDRESS_IS_MINE_STARTED';
|
||||
export const CHECK_ADDRESS_IS_MINE_COMPLETED = 'CHECK_ADDRESS_IS_MINE_COMPLETED';
|
||||
export const SET_DRAFT_TRANSACTION_AMOUNT = 'SET_DRAFT_TRANSACTION_AMOUNT';
|
||||
export const SET_DRAFT_TRANSACTION_ADDRESS = 'SET_DRAFT_TRANSACTION_ADDRESS';
|
||||
export const SEND_TRANSACTION_STARTED = 'SEND_TRANSACTION_STARTED';
|
||||
export const SEND_TRANSACTION_COMPLETED = 'SEND_TRANSACTION_COMPLETED';
|
||||
export const SEND_TRANSACTION_FAILED = 'SEND_TRANSACTION_FAILED';
|
||||
export const FETCH_BLOCK_SUCCESS = 'FETCH_BLOCK_SUCCESS';
|
||||
export const SUPPORT_TRANSACTION_STARTED = 'SUPPORT_TRANSACTION_STARTED';
|
||||
export const SUPPORT_TRANSACTION_COMPLETED = 'SUPPORT_TRANSACTION_COMPLETED';
|
||||
export const SUPPORT_TRANSACTION_FAILED = 'SUPPORT_TRANSACTION_FAILED';
|
||||
|
||||
// Claims
|
||||
export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';
|
||||
export const FETCH_FEATURED_CONTENT_COMPLETED = 'FETCH_FEATURED_CONTENT_COMPLETED';
|
||||
export const RESOLVE_URIS_STARTED = 'RESOLVE_URIS_STARTED';
|
||||
export const RESOLVE_URIS_COMPLETED = 'RESOLVE_URIS_COMPLETED';
|
||||
export const FETCH_CHANNEL_CLAIMS_STARTED = 'FETCH_CHANNEL_CLAIMS_STARTED';
|
||||
export const FETCH_CHANNEL_CLAIMS_COMPLETED = 'FETCH_CHANNEL_CLAIMS_COMPLETED';
|
||||
export const FETCH_CHANNEL_CLAIM_COUNT_STARTED = 'FETCH_CHANNEL_CLAIM_COUNT_STARTED';
|
||||
export const FETCH_CHANNEL_CLAIM_COUNT_COMPLETED = 'FETCH_CHANNEL_CLAIM_COUNT_COMPLETED';
|
||||
export const FETCH_CLAIM_LIST_MINE_STARTED = 'FETCH_CLAIM_LIST_MINE_STARTED';
|
||||
export const FETCH_CLAIM_LIST_MINE_COMPLETED = 'FETCH_CLAIM_LIST_MINE_COMPLETED';
|
||||
export const ABANDON_CLAIM_STARTED = 'ABANDON_CLAIM_STARTED';
|
||||
export const ABANDON_CLAIM_SUCCEEDED = 'ABANDON_CLAIM_SUCCEEDED';
|
||||
export const FETCH_CHANNEL_LIST_MINE_STARTED = 'FETCH_CHANNEL_LIST_MINE_STARTED';
|
||||
export const FETCH_CHANNEL_LIST_MINE_COMPLETED = 'FETCH_CHANNEL_LIST_MINE_COMPLETED';
|
||||
export const CREATE_CHANNEL_STARTED = 'CREATE_CHANNEL_STARTED';
|
||||
export const CREATE_CHANNEL_COMPLETED = 'CREATE_CHANNEL_COMPLETED';
|
||||
export const PUBLISH_STARTED = 'PUBLISH_STARTED';
|
||||
export const PUBLISH_COMPLETED = 'PUBLISH_COMPLETED';
|
||||
export const PUBLISH_FAILED = 'PUBLISH_FAILED';
|
||||
export const SET_PLAYING_URI = 'PLAY_URI';
|
||||
|
||||
// Files
|
||||
export const FILE_LIST_STARTED = 'FILE_LIST_STARTED';
|
||||
export const FILE_LIST_SUCCEEDED = 'FILE_LIST_SUCCEEDED';
|
||||
export const FETCH_FILE_INFO_STARTED = 'FETCH_FILE_INFO_STARTED';
|
||||
export const FETCH_FILE_INFO_COMPLETED = 'FETCH_FILE_INFO_COMPLETED';
|
||||
export const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED';
|
||||
export const FETCH_COST_INFO_COMPLETED = 'FETCH_COST_INFO_COMPLETED';
|
||||
export const LOADING_VIDEO_STARTED = 'LOADING_VIDEO_STARTED';
|
||||
export const LOADING_VIDEO_COMPLETED = 'LOADING_VIDEO_COMPLETED';
|
||||
export const LOADING_VIDEO_FAILED = 'LOADING_VIDEO_FAILED';
|
||||
export const DOWNLOADING_STARTED = 'DOWNLOADING_STARTED';
|
||||
export const DOWNLOADING_PROGRESSED = 'DOWNLOADING_PROGRESSED';
|
||||
export const DOWNLOADING_COMPLETED = 'DOWNLOADING_COMPLETED';
|
||||
export const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED';
|
||||
export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED';
|
||||
export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED';
|
||||
export const FILE_DELETE = 'FILE_DELETE';
|
||||
|
||||
// Search
|
||||
export const SEARCH_STARTED = 'SEARCH_STARTED';
|
||||
export const SEARCH_COMPLETED = 'SEARCH_COMPLETED';
|
||||
export const SEARCH_CANCELLED = 'SEARCH_CANCELLED';
|
||||
|
||||
// Settings
|
||||
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
||||
export const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED';
|
||||
|
||||
// User
|
||||
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
|
||||
export const AUTHENTICATION_SUCCESS = 'AUTHENTICATION_SUCCESS';
|
||||
export const AUTHENTICATION_FAILURE = 'AUTHENTICATION_FAILURE';
|
||||
export const USER_EMAIL_DECLINE = 'USER_EMAIL_DECLINE';
|
||||
export const USER_EMAIL_NEW_STARTED = 'USER_EMAIL_NEW_STARTED';
|
||||
export const USER_EMAIL_NEW_SUCCESS = 'USER_EMAIL_NEW_SUCCESS';
|
||||
export const USER_EMAIL_NEW_EXISTS = 'USER_EMAIL_NEW_EXISTS';
|
||||
export const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE';
|
||||
export const USER_EMAIL_VERIFY_STARTED = 'USER_EMAIL_VERIFY_STARTED';
|
||||
export const USER_EMAIL_VERIFY_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS';
|
||||
export const USER_EMAIL_VERIFY_FAILURE = 'USER_EMAIL_VERIFY_FAILURE';
|
||||
export const USER_IDENTITY_VERIFY_STARTED = 'USER_IDENTITY_VERIFY_STARTED';
|
||||
export const USER_IDENTITY_VERIFY_SUCCESS = 'USER_IDENTITY_VERIFY_SUCCESS';
|
||||
export const USER_IDENTITY_VERIFY_FAILURE = 'USER_IDENTITY_VERIFY_FAILURE';
|
||||
export const USER_FETCH_STARTED = 'USER_FETCH_STARTED';
|
||||
export const USER_FETCH_SUCCESS = 'USER_FETCH_SUCCESS';
|
||||
export const USER_FETCH_FAILURE = 'USER_FETCH_FAILURE';
|
||||
export const USER_INVITE_STATUS_FETCH_STARTED = 'USER_INVITE_STATUS_FETCH_STARTED';
|
||||
export const USER_INVITE_STATUS_FETCH_SUCCESS = 'USER_INVITE_STATUS_FETCH_SUCCESS';
|
||||
export const USER_INVITE_STATUS_FETCH_FAILURE = 'USER_INVITE_STATUS_FETCH_FAILURE';
|
||||
export const USER_INVITE_NEW_STARTED = 'USER_INVITE_NEW_STARTED';
|
||||
export const USER_INVITE_NEW_SUCCESS = 'USER_INVITE_NEW_SUCCESS';
|
||||
export const USER_INVITE_NEW_FAILURE = 'USER_INVITE_NEW_FAILURE';
|
||||
export const FETCH_ACCESS_TOKEN_SUCCESS = 'FETCH_ACCESS_TOKEN_SUCCESS';
|
||||
|
||||
// Rewards
|
||||
export const FETCH_REWARDS_STARTED = 'FETCH_REWARDS_STARTED';
|
||||
export const FETCH_REWARDS_COMPLETED = 'FETCH_REWARDS_COMPLETED';
|
||||
export const CLAIM_REWARD_STARTED = 'CLAIM_REWARD_STARTED';
|
||||
export const CLAIM_REWARD_SUCCESS = 'CLAIM_REWARD_SUCCESS';
|
||||
export const CLAIM_REWARD_FAILURE = 'CLAIM_REWARD_FAILURE';
|
||||
export const CLAIM_REWARD_CLEAR_ERROR = 'CLAIM_REWARD_CLEAR_ERROR';
|
||||
export const FETCH_REWARD_CONTENT_COMPLETED = 'FETCH_REWARD_CONTENT_COMPLETED';
|
||||
|
||||
// Language
|
||||
export const DOWNLOAD_LANGUAGE_SUCCEEDED = 'DOWNLOAD_LANGUAGE_SUCCEEDED';
|
||||
export const DOWNLOAD_LANGUAGE_FAILED = 'DOWNLOAD_LANGUAGE_FAILED';
|
||||
|
||||
// ShapeShift
|
||||
export const GET_SUPPORTED_COINS_START = 'GET_SUPPORTED_COINS_START';
|
||||
export const GET_SUPPORTED_COINS_SUCCESS = 'GET_SUPPORTED_COINS_SUCCESS';
|
||||
export const GET_SUPPORTED_COINS_FAIL = 'GET_SUPPORTED_COINS_FAIL';
|
||||
export const GET_COIN_STATS_START = 'GET_COIN_STATS_START';
|
||||
export const GET_COIN_STATS_SUCCESS = 'GET_COIN_STATS_SUCCESS';
|
||||
export const GET_COIN_STATS_FAIL = 'GET_COIN_STATS_FAIL';
|
||||
export const PREPARE_SHAPE_SHIFT_START = 'PREPARE_SHAPE_SHIFT_START';
|
||||
export const PREPARE_SHAPE_SHIFT_SUCCESS = 'PREPARE_SHAPE_SHIFT_SUCCESS';
|
||||
export const PREPARE_SHAPE_SHIFT_FAIL = 'PREPARE_SHAPE_SHIFT_FAIL';
|
||||
export const GET_ACTIVE_SHIFT_START = 'GET_ACTIVE_SHIFT_START';
|
||||
export const GET_ACTIVE_SHIFT_SUCCESS = 'GET_ACTIVE_SHIFT_SUCCESS';
|
||||
export const GET_ACTIVE_SHIFT_FAIL = 'GET_ACTIVE_SHIFT_FAIL';
|
||||
export const CLEAR_SHAPE_SHIFT = 'CLEAR_SHAPE_SHIFT';
|
||||
|
||||
// Subscriptions
|
||||
export const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
|
||||
export const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
|
||||
export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
|
||||
|
||||
// Video controls
|
||||
export const SET_VIDEO_PAUSE = 'SET_VIDEO_PAUSE';
|
||||
|
||||
// Media controls
|
||||
export const MEDIA_PLAY = 'MEDIA_PLAY';
|
||||
export const MEDIA_PAUSE = 'MEDIA_PAUSE';
|
||||
export const MEDIA_POSITION = 'MEDIA_POSITION';
|
5
src/constants/icons.js
Normal file
5
src/constants/icons.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
export const FEATURED = 'rocket';
|
||||
export const LOCAL = 'folder';
|
||||
export const FILE = 'file';
|
||||
export const HISTORY = 'history';
|
||||
export const HELP_CIRCLE = 'question-circle';
|
187
src/constants/languages.js
Normal file
187
src/constants/languages.js
Normal file
|
@ -0,0 +1,187 @@
|
|||
const LANGUAGES = {
|
||||
aa: ['Afar', 'Afar'],
|
||||
ab: ['Abkhazian', 'Аҧсуа'],
|
||||
af: ['Afrikaans', 'Afrikaans'],
|
||||
ak: ['Akan', 'Akana'],
|
||||
am: ['Amharic', 'አማርኛ'],
|
||||
an: ['Aragonese', 'Aragonés'],
|
||||
ar: ['Arabic', 'العربية'],
|
||||
as: ['Assamese', 'অসমীয়া'],
|
||||
av: ['Avar', 'Авар'],
|
||||
ay: ['Aymara', 'Aymar'],
|
||||
az: ['Azerbaijani', 'Azərbaycanca / آذربايجان'],
|
||||
ba: ['Bashkir', 'Башҡорт'],
|
||||
be: ['Belarusian', 'Беларуская'],
|
||||
bg: ['Bulgarian', 'Български'],
|
||||
bh: ['Bihari', 'भोजपुरी'],
|
||||
bi: ['Bislama', 'Bislama'],
|
||||
bm: ['Bambara', 'Bamanankan'],
|
||||
bn: ['Bengali', 'বাংলা'],
|
||||
bo: ['Tibetan', 'བོད་ཡིག / Bod skad'],
|
||||
br: ['Breton', 'Brezhoneg'],
|
||||
bs: ['Bosnian', 'Bosanski'],
|
||||
ca: ['Catalan', 'Català'],
|
||||
ce: ['Chechen', 'Нохчийн'],
|
||||
ch: ['Chamorro', 'Chamoru'],
|
||||
co: ['Corsican', 'Corsu'],
|
||||
cr: ['Cree', 'Nehiyaw'],
|
||||
cs: ['Czech', 'Česky'],
|
||||
cu: ['Old Church Slavonic / Old Bulgarian', 'словѣньскъ / slověnĭskŭ'],
|
||||
cv: ['Chuvash', 'Чăваш'],
|
||||
cy: ['Welsh', 'Cymraeg'],
|
||||
da: ['Danish', 'Dansk'],
|
||||
de: ['German', 'Deutsch'],
|
||||
dv: ['Divehi', 'ދިވެހިބަސް'],
|
||||
dz: ['Dzongkha', 'ཇོང་ཁ'],
|
||||
ee: ['Ewe', 'Ɛʋɛ'],
|
||||
el: ['Greek', 'Ελληνικά'],
|
||||
en: ['English', 'English'],
|
||||
eo: ['Esperanto', 'Esperanto'],
|
||||
es: ['Spanish', 'Español'],
|
||||
et: ['Estonian', 'Eesti'],
|
||||
eu: ['Basque', 'Euskara'],
|
||||
fa: ['Persian', 'فارسی'],
|
||||
ff: ['Peul', 'Fulfulde'],
|
||||
fi: ['Finnish', 'Suomi'],
|
||||
fj: ['Fijian', 'Na Vosa Vakaviti'],
|
||||
fo: ['Faroese', 'Føroyskt'],
|
||||
fr: ['French', 'Français'],
|
||||
fy: ['West Frisian', 'Frysk'],
|
||||
ga: ['Irish', 'Gaeilge'],
|
||||
gd: ['Scottish Gaelic', 'Gàidhlig'],
|
||||
gl: ['Galician', 'Galego'],
|
||||
gn: ['Guarani', "Avañe'ẽ"],
|
||||
gu: ['Gujarati', 'ગુજરાતી'],
|
||||
gv: ['Manx', 'Gaelg'],
|
||||
ha: ['Hausa', 'هَوُسَ'],
|
||||
he: ['Hebrew', 'עברית'],
|
||||
hi: ['Hindi', 'हिन्दी'],
|
||||
ho: ['Hiri Motu', 'Hiri Motu'],
|
||||
hr: ['Croatian', 'Hrvatski'],
|
||||
ht: ['Haitian', 'Krèyol ayisyen'],
|
||||
hu: ['Hungarian', 'Magyar'],
|
||||
hy: ['Armenian', 'Հայերեն'],
|
||||
hz: ['Herero', 'Otsiherero'],
|
||||
ia: ['Interlingua', 'Interlingua'],
|
||||
id: ['Indonesian', 'Bahasa Indonesia'],
|
||||
ie: ['Interlingue', 'Interlingue'],
|
||||
ig: ['Igbo', 'Igbo'],
|
||||
ii: ['Sichuan Yi', 'ꆇꉙ / 四川彝语'],
|
||||
ik: ['Inupiak', 'Iñupiak'],
|
||||
io: ['Ido', 'Ido'],
|
||||
is: ['Icelandic', 'Íslenska'],
|
||||
it: ['Italian', 'Italiano'],
|
||||
iu: ['Inuktitut', 'ᐃᓄᒃᑎᑐᑦ'],
|
||||
ja: ['Japanese', '日本語'],
|
||||
jv: ['Javanese', 'Basa Jawa'],
|
||||
ka: ['Georgian', 'ქართული'],
|
||||
kg: ['Kongo', 'KiKongo'],
|
||||
ki: ['Kikuyu', 'Gĩkũyũ'],
|
||||
kj: ['Kuanyama', 'Kuanyama'],
|
||||
kk: ['Kazakh', 'Қазақша'],
|
||||
kl: ['Greenlandic', 'Kalaallisut'],
|
||||
km: ['Cambodian', 'ភាសាខ្មែរ'],
|
||||
kn: ['Kannada', 'ಕನ್ನಡ'],
|
||||
ko: ['Korean', '한국어'],
|
||||
kr: ['Kanuri', 'Kanuri'],
|
||||
ks: ['Kashmiri', 'कश्मीरी / كشميري'],
|
||||
ku: ['Kurdish', 'Kurdî / كوردی'],
|
||||
kv: ['Komi', 'Коми'],
|
||||
kw: ['Cornish', 'Kernewek'],
|
||||
ky: ['Kirghiz', 'Kırgızca / Кыргызча'],
|
||||
la: ['Latin', 'Latina'],
|
||||
lb: ['Luxembourgish', 'Lëtzebuergesch'],
|
||||
lg: ['Ganda', 'Luganda'],
|
||||
li: ['Limburgian', 'Limburgs'],
|
||||
ln: ['Lingala', 'Lingála'],
|
||||
lo: ['Laotian', 'ລາວ / Pha xa lao'],
|
||||
lt: ['Lithuanian', 'Lietuvių'],
|
||||
lv: ['Latvian', 'Latviešu'],
|
||||
mg: ['Malagasy', 'Malagasy'],
|
||||
mh: ['Marshallese', 'Kajin Majel / Ebon'],
|
||||
mi: ['Maori', 'Māori'],
|
||||
mk: ['Macedonian', 'Македонски'],
|
||||
ml: ['Malayalam', 'മലയാളം'],
|
||||
mn: ['Mongolian', 'Монгол'],
|
||||
mo: ['Moldovan', 'Moldovenească'],
|
||||
mr: ['Marathi', 'मराठी'],
|
||||
ms: ['Malay', 'Bahasa Melayu'],
|
||||
mt: ['Maltese', 'bil-Malti'],
|
||||
my: ['Burmese', 'Myanmasa'],
|
||||
na: ['Nauruan', 'Dorerin Naoero'],
|
||||
nd: ['North Ndebele', 'Sindebele'],
|
||||
ne: ['Nepali', 'नेपाली'],
|
||||
ng: ['Ndonga', 'Oshiwambo'],
|
||||
nl: ['Dutch', 'Nederlands'],
|
||||
nn: ['Norwegian Nynorsk', 'Norsk (nynorsk)'],
|
||||
no: ['Norwegian', 'Norsk (bokmål / riksmål)'],
|
||||
nr: ['South Ndebele', 'isiNdebele'],
|
||||
nv: ['Navajo', 'Diné bizaad'],
|
||||
ny: ['Chichewa', 'Chi-Chewa'],
|
||||
oc: ['Occitan', 'Occitan'],
|
||||
oj: ['Ojibwa', 'ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin'],
|
||||
om: ['Oromo', 'Oromoo'],
|
||||
or: ['Oriya', 'ଓଡ଼ିଆ'],
|
||||
os: ['Ossetian / Ossetic', 'Иронау'],
|
||||
pa: ['Panjabi / Punjabi', 'ਪੰਜਾਬੀ / पंजाबी / پنجابي'],
|
||||
pi: ['Pali', 'Pāli / पाऴि'],
|
||||
pl: ['Polish', 'Polski'],
|
||||
ps: ['Pashto', 'پښتو'],
|
||||
pt: ['Portuguese', 'Português'],
|
||||
qu: ['Quechua', 'Runa Simi'],
|
||||
rm: ['Raeto Romance', 'Rumantsch'],
|
||||
rn: ['Kirundi', 'Kirundi'],
|
||||
ro: ['Romanian', 'Română'],
|
||||
ru: ['Russian', 'Русский'],
|
||||
rw: ['Rwandi', 'Kinyarwandi'],
|
||||
sa: ['Sanskrit', 'संस्कृतम्'],
|
||||
sc: ['Sardinian', 'Sardu'],
|
||||
sd: ['Sindhi', 'सिनधि'],
|
||||
se: ['Northern Sami', 'Sámegiella'],
|
||||
sg: ['Sango', 'Sängö'],
|
||||
sh: ['Serbo-Croatian', 'Srpskohrvatski / Српскохрватски'],
|
||||
si: ['Sinhalese', 'සිංහල'],
|
||||
sk: ['Slovak', 'Slovenčina'],
|
||||
sl: ['Slovenian', 'Slovenščina'],
|
||||
sm: ['Samoan', 'Gagana Samoa'],
|
||||
sn: ['Shona', 'chiShona'],
|
||||
so: ['Somalia', 'Soomaaliga'],
|
||||
sq: ['Albanian', 'Shqip'],
|
||||
sr: ['Serbian', 'Српски'],
|
||||
ss: ['Swati', 'SiSwati'],
|
||||
st: ['Southern Sotho', 'Sesotho'],
|
||||
su: ['Sundanese', 'Basa Sunda'],
|
||||
sv: ['Swedish', 'Svenska'],
|
||||
sw: ['Swahili', 'Kiswahili'],
|
||||
ta: ['Tamil', 'தமிழ்'],
|
||||
te: ['Telugu', 'తెలుగు'],
|
||||
tg: ['Tajik', 'Тоҷикӣ'],
|
||||
th: ['Thai', 'ไทย / Phasa Thai'],
|
||||
ti: ['Tigrinya', 'ትግርኛ'],
|
||||
tk: ['Turkmen', 'Туркмен / تركمن'],
|
||||
tl: ['Tagalog / Filipino', 'Tagalog'],
|
||||
tn: ['Tswana', 'Setswana'],
|
||||
to: ['Tonga', 'Lea Faka-Tonga'],
|
||||
tr: ['Turkish', 'Türkçe'],
|
||||
ts: ['Tsonga', 'Xitsonga'],
|
||||
tt: ['Tatar', 'Tatarça'],
|
||||
tw: ['Twi', 'Twi'],
|
||||
ty: ['Tahitian', 'Reo Mā`ohi'],
|
||||
ug: ['Uyghur', 'Uyƣurqə / ئۇيغۇرچە'],
|
||||
uk: ['Ukrainian', 'Українська'],
|
||||
ur: ['Urdu', 'اردو'],
|
||||
uz: ['Uzbek', 'Ўзбек'],
|
||||
ve: ['Venda', 'Tshivenḓa'],
|
||||
vi: ['Vietnamese', 'Tiếng Việt'],
|
||||
vo: ['Volapük', 'Volapük'],
|
||||
wa: ['Walloon', 'Walon'],
|
||||
wo: ['Wolof', 'Wollof'],
|
||||
xh: ['Xhosa', 'isiXhosa'],
|
||||
yi: ['Yiddish', 'ייִדיש'],
|
||||
yo: ['Yoruba', 'Yorùbá'],
|
||||
za: ['Zhuang', 'Cuengh / Tôô / 壮语'],
|
||||
zh: ['Chinese', '中文'],
|
||||
zu: ['Zulu', 'isiZulu'],
|
||||
};
|
||||
|
||||
export default LANGUAGES;
|
15
src/constants/modal_types.js
Normal file
15
src/constants/modal_types.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
export const CONFIRM_FILE_REMOVE = 'confirmFileRemove';
|
||||
export const INCOMPATIBLE_DAEMON = 'incompatibleDaemon';
|
||||
export const FILE_TIMEOUT = 'file_timeout';
|
||||
export const DOWNLOADING = 'downloading';
|
||||
export const ERROR = 'error';
|
||||
export const INSUFFICIENT_CREDITS = 'insufficient_credits';
|
||||
export const UPGRADE = 'upgrade';
|
||||
export const WELCOME = 'welcome';
|
||||
export const EMAIL_COLLECTION = 'email_collection';
|
||||
export const FIRST_REWARD = 'first_reward';
|
||||
export const AUTHENTICATION_FAILURE = 'auth_failure';
|
||||
export const TRANSACTION_FAILED = 'transaction_failed';
|
||||
export const REWARD_APPROVAL_REQUIRED = 'reward_approval_required';
|
||||
export const AFFIRM_PURCHASE = 'affirm_purchase';
|
||||
export const CONFIRM_CLAIM_REVOKE = 'confirmClaimRevoke';
|
13
src/constants/settings.js
Normal file
13
src/constants/settings.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* hardcoded names still exist for these in reducers/settings.js - only discovered when debugging */
|
||||
/* Many SETTINGS are stored in the localStorage by their name -
|
||||
be careful about changing the value of a SETTINGS constant, as doing so can invalidate existing SETTINGS */
|
||||
export const CREDIT_REQUIRED_ACKNOWLEDGED = 'credit_required_acknowledged';
|
||||
export const NEW_USER_ACKNOWLEDGED = 'welcome_acknowledged';
|
||||
export const EMAIL_COLLECTION_ACKNOWLEDGED = 'email_collection_acknowledged';
|
||||
export const LANGUAGE = 'language';
|
||||
export const SHOW_NSFW = 'showNsfw';
|
||||
export const SHOW_UNAVAILABLE = 'showUnavailable';
|
||||
export const INSTANT_PURCHASE_ENABLED = 'instantPurchaseEnabled';
|
||||
export const INSTANT_PURCHASE_MAX = 'instantPurchaseMax';
|
||||
export const THEME = 'theme';
|
||||
export const THEMES = 'themes';
|
3
src/constants/shape_shift.js
Normal file
3
src/constants/shape_shift.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export const NO_DEPOSITS = 'no_deposits';
|
||||
export const RECEIVED = 'received';
|
||||
export const COMPLETE = 'complete';
|
2
src/constants/transaction_types.js
Normal file
2
src/constants/transaction_types.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const TIP = 'tip';
|
2
src/index.js
Normal file
2
src/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { lbry } from './lbry';
|
||||
export { lbryio } from './lbryio';
|
86
src/jsonrpc.js
Normal file
86
src/jsonrpc.js
Normal file
|
@ -0,0 +1,86 @@
|
|||
const jsonrpc = {};
|
||||
|
||||
jsonrpc.call = (
|
||||
connectionString,
|
||||
method,
|
||||
params,
|
||||
callback,
|
||||
errorCallback,
|
||||
connectFailedCallback
|
||||
) => {
|
||||
function checkAndParse(response) {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response.json();
|
||||
}
|
||||
return response.json().then(json => {
|
||||
let error;
|
||||
if (json.error) {
|
||||
error = new Error(json.error);
|
||||
} else {
|
||||
error = new Error('Protocol error with unknown response signature');
|
||||
}
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
const counter = parseInt(sessionStorage.getItem('JSONRPCCounter') || 0, 10);
|
||||
const url = connectionString;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method,
|
||||
params,
|
||||
id: counter,
|
||||
}),
|
||||
};
|
||||
|
||||
sessionStorage.setItem('JSONRPCCounter', counter + 1);
|
||||
|
||||
return fetch(url, options)
|
||||
.then(checkAndParse)
|
||||
.then(response => {
|
||||
const error = response.error || (response.result && response.result.error);
|
||||
|
||||
if (!error && typeof callback === 'function') {
|
||||
return callback(response.result);
|
||||
}
|
||||
|
||||
if (error && typeof errorCallback === 'function') {
|
||||
return errorCallback(error);
|
||||
}
|
||||
|
||||
const errorEvent = new CustomEvent('unhandledError', {
|
||||
detail: {
|
||||
connectionString,
|
||||
method,
|
||||
params,
|
||||
code: error.code,
|
||||
message: error.message || error,
|
||||
data: error.data,
|
||||
},
|
||||
});
|
||||
document.dispatchEvent(errorEvent);
|
||||
|
||||
return Promise.resolve();
|
||||
})
|
||||
.catch(error => {
|
||||
if (connectFailedCallback) {
|
||||
return connectFailedCallback(error);
|
||||
}
|
||||
|
||||
const errorEvent = new CustomEvent('unhandledError', {
|
||||
detail: {
|
||||
connectionString,
|
||||
method,
|
||||
params,
|
||||
code: error.response && error.response.status,
|
||||
message: __('Connection to API server failed'),
|
||||
},
|
||||
});
|
||||
document.dispatchEvent(errorEvent);
|
||||
return Promise.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
export default jsonrpc;
|
289
src/lbry.js
Normal file
289
src/lbry.js
Normal file
|
@ -0,0 +1,289 @@
|
|||
import jsonrpc from 'jsonrpc';
|
||||
|
||||
const CHECK_DAEMON_STARTED_TRY_NUMBER = 200;
|
||||
|
||||
const Lbry = {
|
||||
isConnected: false,
|
||||
daemonConnectionString: 'http://localhost:5279',
|
||||
pendingPublishTimeout: 20 * 60 * 1000,
|
||||
};
|
||||
|
||||
function apiCall(method, params, resolve, reject) {
|
||||
return jsonrpc.call(Lbry.daemonConnectionString, method, params, resolve, reject, reject);
|
||||
}
|
||||
|
||||
const lbryProxy = new Proxy(Lbry, {
|
||||
get(target, name) {
|
||||
if (name in target) {
|
||||
return target[name];
|
||||
}
|
||||
|
||||
return (params = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
apiCall(name, params, resolve, reject);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function getLocal(key, fallback = undefined) {
|
||||
const itemRaw = localStorage.getItem(key);
|
||||
return itemRaw === null ? fallback : JSON.parse(itemRaw);
|
||||
}
|
||||
|
||||
function setLocal(key, value) {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a publish attempt in local storage. Returns a dictionary with all the data needed to
|
||||
* needed to make a dummy claim or file info object.
|
||||
*/
|
||||
let pendingId = 0;
|
||||
function savePendingPublish({ name, channelName }) {
|
||||
pendingId += 1;
|
||||
const pendingPublishes = getLocal('pendingPublishes') || [];
|
||||
const newPendingPublish = {
|
||||
name,
|
||||
channelName,
|
||||
claim_id: `pending-${pendingId}`,
|
||||
txid: `pending-${pendingId}`,
|
||||
nout: 0,
|
||||
outpoint: `pending-${pendingId}:0`,
|
||||
time: Date.now(),
|
||||
};
|
||||
setLocal('pendingPublishes', [...pendingPublishes, newPendingPublish]);
|
||||
return newPendingPublish;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a pending publish with the given name or outpoint, remove it.
|
||||
* A channel name may also be provided along with name.
|
||||
*/
|
||||
function removePendingPublishIfNeeded({ name, channelName, outpoint }) {
|
||||
function pubMatches(pub) {
|
||||
return (
|
||||
pub.outpoint === outpoint ||
|
||||
(pub.name === name && (!channelName || pub.channel_name === channelName))
|
||||
);
|
||||
}
|
||||
|
||||
setLocal('pendingPublishes', Lbry.getPendingPublishes().filter(pub => !pubMatches(pub)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current list of pending publish attempts. Filters out any that have timed out and
|
||||
* removes them from the list.
|
||||
*/
|
||||
Lbry.getPendingPublishes = () => {
|
||||
const pendingPublishes = getLocal('pendingPublishes') || [];
|
||||
const newPendingPublishes = pendingPublishes.filter(
|
||||
pub => Date.now() - pub.time <= Lbry.pendingPublishTimeout
|
||||
);
|
||||
setLocal('pendingPublishes', newPendingPublishes);
|
||||
return newPendingPublishes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a pending publish attempt by its name or (fake) outpoint. A channel name can also be
|
||||
* provided along withe the name. If no pending publish is found, returns null.
|
||||
*/
|
||||
function getPendingPublish({ name, channelName, outpoint }) {
|
||||
const pendingPublishes = Lbry.getPendingPublishes();
|
||||
return (
|
||||
pendingPublishes.find(
|
||||
pub =>
|
||||
pub.outpoint === outpoint ||
|
||||
(pub.name === name && (!channelName || pub.channel_name === channelName))
|
||||
) || null
|
||||
);
|
||||
}
|
||||
|
||||
function pendingPublishToDummyClaim({ channelName, name, outpoint, claimId, txid, nout }) {
|
||||
return { name, outpoint, claimId, txid, nout, channelName };
|
||||
}
|
||||
|
||||
function pendingPublishToDummyFileInfo({ name, outpoint, claimId }) {
|
||||
return { name, outpoint, claimId, metadata: null };
|
||||
}
|
||||
|
||||
// core
|
||||
Lbry.connectPromise = null;
|
||||
Lbry.connect = () => {
|
||||
if (Lbry.connectPromise === null) {
|
||||
Lbry.connectPromise = new Promise((resolve, reject) => {
|
||||
let tryNum = 0;
|
||||
|
||||
// Check every half second to see if the daemon is accepting connections
|
||||
function checkDaemonStarted() {
|
||||
tryNum += 1;
|
||||
lbryProxy
|
||||
.status()
|
||||
.then(resolve)
|
||||
.catch(() => {
|
||||
if (tryNum <= CHECK_DAEMON_STARTED_TRY_NUMBER) {
|
||||
setTimeout(checkDaemonStarted, tryNum < 50 ? 400 : 1000);
|
||||
} else {
|
||||
reject(new Error('Unable to connect to LBRY'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
checkDaemonStarted();
|
||||
});
|
||||
}
|
||||
|
||||
return Lbry.connectPromise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Publishes a file. The optional fileListedCallback is called when the file becomes available in
|
||||
* lbry.file_list() during the publish process.
|
||||
*
|
||||
* This currently includes a work-around to cache the file in local storage so that the pending
|
||||
* publish can appear in the UI immediately.
|
||||
*/
|
||||
Lbry.publishDeprecated = (params, fileListedCallback, publishedCallback, errorCallback) => {
|
||||
// Give a short grace period in case publish() returns right away or (more likely) gives an error
|
||||
const returnPendingTimeout = setTimeout(
|
||||
() => {
|
||||
const { name, channel_name: channelName } = params;
|
||||
if (publishedCallback || fileListedCallback) {
|
||||
savePendingPublish({
|
||||
name,
|
||||
channelName,
|
||||
});
|
||||
publishedCallback(true);
|
||||
}
|
||||
},
|
||||
2000,
|
||||
{ once: true }
|
||||
);
|
||||
|
||||
lbryProxy.publish(params).then(
|
||||
result => {
|
||||
if (returnPendingTimeout) clearTimeout(returnPendingTimeout);
|
||||
publishedCallback(result);
|
||||
},
|
||||
err => {
|
||||
if (returnPendingTimeout) clearTimeout(returnPendingTimeout);
|
||||
errorCallback(err);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
Lbry.imagePath = file => `${staticResourcesPath}/img/${file}`;
|
||||
|
||||
Lbry.getMediaType = (contentType, fileName) => {
|
||||
if (contentType) {
|
||||
return /^[^/]+/.exec(contentType)[0];
|
||||
} else if (fileName) {
|
||||
const dotIndex = fileName.lastIndexOf('.');
|
||||
if (dotIndex === -1) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
const ext = fileName.substr(dotIndex + 1);
|
||||
if (/^mp4|m4v|webm|flv|f4v|ogv$/i.test(ext)) {
|
||||
return 'video';
|
||||
} else if (/^mp3|m4a|aac|wav|flac|ogg|opus$/i.test(ext)) {
|
||||
return 'audio';
|
||||
} else if (/^html|htm|xml|pdf|odf|doc|docx|md|markdown|txt|epub|org$/i.test(ext)) {
|
||||
return 'document';
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
return 'unknown';
|
||||
};
|
||||
|
||||
Lbry.getAppVersionInfo = () =>
|
||||
new Promise(resolve => {
|
||||
/*ipcRenderer.once('version-info-received', (event, versionInfo) => {
|
||||
resolve(versionInfo);
|
||||
});
|
||||
ipcRenderer.send('version-info-requested');*/
|
||||
});
|
||||
|
||||
/**
|
||||
* Wrappers for API methods to simulate missing or future behavior. Unlike the old-style stubs,
|
||||
* these are designed to be transparent wrappers around the corresponding API methods.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns results from the file_list API method, plus dummy entries for pending publishes.
|
||||
* (If a real publish with the same name is found, the pending publish will be ignored and removed.)
|
||||
*/
|
||||
Lbry.file_list = (params = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const { name, channel_name: channelName, outpoint } = params;
|
||||
|
||||
/**
|
||||
* If we're searching by outpoint, check first to see if there's a matching pending publish.
|
||||
* Pending publishes use their own faux outpoints that are always unique, so we don't need
|
||||
* to check if there's a real file.
|
||||
*/
|
||||
if (outpoint) {
|
||||
const pendingPublish = getPendingPublish({ outpoint });
|
||||
if (pendingPublish) {
|
||||
resolve([pendingPublishToDummyFileInfo(pendingPublish)]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
apiCall(
|
||||
'file_list',
|
||||
params,
|
||||
fileInfos => {
|
||||
removePendingPublishIfNeeded({ name, channelName, outpoint });
|
||||
|
||||
// if a naked file_list call, append the pending file infos
|
||||
if (!name && !channelName && !outpoint) {
|
||||
const dummyFileInfos = Lbry.getPendingPublishes().map(pendingPublishToDummyFileInfo);
|
||||
|
||||
resolve([...fileInfos, ...dummyFileInfos]);
|
||||
} else {
|
||||
resolve(fileInfos);
|
||||
}
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
|
||||
Lbry.claim_list_mine = (params = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
apiCall(
|
||||
'claim_list_mine',
|
||||
params,
|
||||
claims => {
|
||||
claims.forEach(({ name, channel_name: channelName, txid, nout }) => {
|
||||
removePendingPublishIfNeeded({
|
||||
name,
|
||||
channelName,
|
||||
outpoint: `${txid}:${nout}`,
|
||||
});
|
||||
});
|
||||
|
||||
const dummyClaims = Lbry.getPendingPublishes().map(pendingPublishToDummyClaim);
|
||||
resolve([...claims, ...dummyClaims]);
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
|
||||
Lbry.resolve = (params = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
apiCall(
|
||||
'resolve',
|
||||
params,
|
||||
data => {
|
||||
if ('uri' in params) {
|
||||
// If only a single URI was requested, don't nest the results in an object
|
||||
resolve(data && data[params.uri] ? data[params.uri] : {});
|
||||
} else {
|
||||
resolve(data || {});
|
||||
}
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
|
||||
export default lbryProxy;
|
176
src/lbryio.js
Normal file
176
src/lbryio.js
Normal file
|
@ -0,0 +1,176 @@
|
|||
import Lbry from 'lbry';
|
||||
import querystring from 'querystring';
|
||||
|
||||
const Lbryio = {
|
||||
enabled: true,
|
||||
authenticationPromise: null,
|
||||
exchangePromise: null,
|
||||
exchangeLastFetched: null,
|
||||
};
|
||||
|
||||
const CONNECTION_STRING = process.env.LBRY_APP_API_URL
|
||||
? process.env.LBRY_APP_API_URL.replace(/\/*$/, '/') // exactly one slash at the end
|
||||
: 'https://api.lbry.io/';
|
||||
|
||||
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
|
||||
|
||||
Lbryio.getExchangeRates = () => {
|
||||
if (
|
||||
!Lbryio.exchangeLastFetched ||
|
||||
Date.now() - Lbryio.exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
|
||||
) {
|
||||
Lbryio.exchangePromise = new Promise((resolve, reject) => {
|
||||
Lbryio.call('lbc', 'exchange_rate', {}, 'get', true)
|
||||
.then(({ lbc_usd: LBC_USD, lbc_btc: LBC_BTC, btc_usd: BTC_USD }) => {
|
||||
const rates = { LBC_USD, LBC_BTC, BTC_USD };
|
||||
resolve(rates);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
Lbryio.exchangeLastFetched = Date.now();
|
||||
}
|
||||
return Lbryio.exchangePromise;
|
||||
};
|
||||
|
||||
Lbryio.call = (resource, action, params = {}, method = 'get') => {
|
||||
if (!Lbryio.enabled) {
|
||||
console.log(__('Internal API disabled'));
|
||||
return Promise.reject(new Error(__('LBRY internal API is disabled')));
|
||||
}
|
||||
|
||||
if (!(method === 'get' || method === 'post')) {
|
||||
return Promise.reject(new Error(__('Invalid method')));
|
||||
}
|
||||
|
||||
function checkAndParse(response) {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response.json();
|
||||
}
|
||||
return response.json().then(json => {
|
||||
let error;
|
||||
if (json.error) {
|
||||
error = new Error(json.error);
|
||||
} else {
|
||||
error = new Error('Unknown API error signature');
|
||||
}
|
||||
error.response = response; // This is primarily a hack used in actions/user.js
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
function makeRequest(url, options) {
|
||||
return fetch(url, options).then(checkAndParse);
|
||||
}
|
||||
|
||||
return Lbryio.getAuthToken().then(token => {
|
||||
const fullParams = { auth_token: token, ...params };
|
||||
const qs = querystring.stringify(fullParams);
|
||||
let url = `${CONNECTION_STRING}${resource}/${action}?${qs}`;
|
||||
|
||||
let options = {
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
if (method === 'post') {
|
||||
options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: qs,
|
||||
};
|
||||
url = `${CONNECTION_STRING}${resource}/${action}`;
|
||||
}
|
||||
|
||||
return makeRequest(url, options).then(response => response.data);
|
||||
});
|
||||
};
|
||||
|
||||
Lbryio.authToken = null;
|
||||
|
||||
Lbryio.getAuthToken = () =>
|
||||
new Promise(resolve => {
|
||||
if (Lbryio.authToken) {
|
||||
resolve(Lbryio.authToken);
|
||||
} else {
|
||||
/*ipcRenderer.once('auth-token-response', (event, token) => {
|
||||
Lbryio.authToken = token;
|
||||
return resolve(token);
|
||||
});
|
||||
ipcRenderer.send('get-auth-token');*/
|
||||
}
|
||||
});
|
||||
|
||||
Lbryio.setAuthToken = token => {
|
||||
Lbryio.authToken = token ? token.toString().trim() : null;
|
||||
//ipcRenderer.send('set-auth-token', token);
|
||||
};
|
||||
|
||||
Lbryio.getCurrentUser = () => Lbryio.call('user', 'me');
|
||||
|
||||
Lbryio.authenticate = () => {
|
||||
if (!Lbryio.enabled) {
|
||||
return new Promise(resolve => {
|
||||
resolve({
|
||||
id: 1,
|
||||
language: 'en',
|
||||
primary_email: 'disabled@lbry.io',
|
||||
has_verified_email: true,
|
||||
is_identity_verified: true,
|
||||
is_reward_approved: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (Lbryio.authenticationPromise === null) {
|
||||
Lbryio.authenticationPromise = new Promise((resolve, reject) => {
|
||||
Lbryio.getAuthToken()
|
||||
.then(token => {
|
||||
if (!token || token.length > 60) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check that token works
|
||||
return Lbryio.getCurrentUser()
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
})
|
||||
.then(isTokenValid => {
|
||||
if (isTokenValid) {
|
||||
return reject;
|
||||
}
|
||||
|
||||
return Lbry.status()
|
||||
.then(status =>
|
||||
Lbryio.call(
|
||||
'user',
|
||||
'new',
|
||||
{
|
||||
auth_token: '',
|
||||
language: 'en',
|
||||
app_id: status.installation_id,
|
||||
},
|
||||
'post'
|
||||
)
|
||||
)
|
||||
.then(response => {
|
||||
if (!response.auth_token) {
|
||||
throw new Error(__('auth_token is missing from response'));
|
||||
}
|
||||
return Lbryio.setAuthToken(response.auth_token);
|
||||
});
|
||||
})
|
||||
.then(Lbryio.getCurrentUser)
|
||||
.then(resolve, reject);
|
||||
});
|
||||
}
|
||||
|
||||
return Lbryio.authenticationPromise;
|
||||
};
|
||||
|
||||
Lbryio.getStripeToken = () =>
|
||||
CONNECTION_STRING.startsWith('http://localhost:')
|
||||
? 'pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
|
||||
: 'pk_live_e8M4dRNnCCbmpZzduEUZBgJO';
|
||||
|
||||
export default Lbryio;
|
38
src/redux/actions/cost_info.js
Normal file
38
src/redux/actions/cost_info.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import Lbryio from 'lbryio';
|
||||
import { selectClaimsByUri } from 'redux/selectors/claims';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function doFetchCostInfoForUri(uri) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const claim = null; //selectClaimsByUri(state)[uri];
|
||||
|
||||
if (!claim) return;
|
||||
|
||||
function resolve(costInfo) {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_COST_INFO_COMPLETED,
|
||||
data: {
|
||||
uri,
|
||||
costInfo,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const fee =
|
||||
claim.value && claim.value.stream && claim.value.stream.metadata
|
||||
? claim.value.stream.metadata.fee
|
||||
: undefined;
|
||||
|
||||
if (fee === undefined) {
|
||||
resolve({ cost: 0, includesData: true });
|
||||
} else if (fee.currency === 'LBC') {
|
||||
resolve({ cost: fee.amount, includesData: true });
|
||||
} else {
|
||||
Lbryio.getExchangeRates().then(({ LBC_USD }) => {
|
||||
resolve({ cost: fee.amount / LBC_USD, includesData: true });
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
34
src/redux/reducers/cost_info.js
Normal file
34
src/redux/reducers/cost_info.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
const reducers = {};
|
||||
const defaultState = {};
|
||||
|
||||
reducers[ACTIONS.FETCH_COST_INFO_STARTED] = (state, action) => {
|
||||
const { uri } = action.data;
|
||||
const newFetching = Object.assign({}, state.fetching);
|
||||
newFetching[uri] = true;
|
||||
|
||||
return Object.assign({}, state, {
|
||||
fetching: newFetching,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.FETCH_COST_INFO_COMPLETED] = (state, action) => {
|
||||
const { uri, costInfo } = action.data;
|
||||
const newByUri = Object.assign({}, state.byUri);
|
||||
const newFetching = Object.assign({}, state.fetching);
|
||||
|
||||
newByUri[uri] = costInfo;
|
||||
delete newFetching[uri];
|
||||
|
||||
return Object.assign({}, state, {
|
||||
byUri: newByUri,
|
||||
fetching: newFetching,
|
||||
});
|
||||
};
|
||||
|
||||
export default function reducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
||||
return state;
|
||||
}
|
20
src/redux/selectors/cost_info.js
Normal file
20
src/redux/selectors/cost_info.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import { selectCurrentParams } from 'redux/selectors/navigation';
|
||||
|
||||
export const selectState = state => state.costInfo || {};
|
||||
|
||||
export const selectAllCostInfoByUri = createSelector(selectState, state => state.byUri || {});
|
||||
|
||||
export const makeSelectCostInfoForUri = uri =>
|
||||
createSelector(selectAllCostInfoByUri, costInfos => costInfos && costInfos[uri]);
|
||||
|
||||
export const selectCostForCurrentPageUri = createSelector(
|
||||
selectAllCostInfoByUri,
|
||||
selectCurrentParams,
|
||||
(costInfo, params) => (params.uri && costInfo[params.uri] ? costInfo[params.uri].cost : undefined)
|
||||
);
|
||||
|
||||
export const selectFetchingCostInfo = createSelector(selectState, state => state.fetching || {});
|
||||
|
||||
export const makeSelectFetchingCostInfoForUri = uri =>
|
||||
createSelector(selectFetchingCostInfo, fetchingByUri => fetchingByUri && fetchingByUri[uri]);
|
31
webpack.config.js
Normal file
31
webpack.config.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
const path = require('path');
|
||||
module.exports = {
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'build'),
|
||||
filename: 'index.js',
|
||||
libraryTarget: 'commonjs2'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
include: path.resolve(__dirname, 'src'),
|
||||
exclude: /(node_modules|bower_components|build)/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['env', 'react', 'stage-2']
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
modules: [path.resolve(__dirname, 'src/'), 'node_modules', __dirname],
|
||||
extensions: ['.js', '.jsx', '.scss'],
|
||||
},
|
||||
externals: {
|
||||
'react': 'commonjs react'
|
||||
}
|
||||
};
|
Loading…
Reference in a new issue