Compare commits
275 commits
authentica
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
0b4e41ef90 | ||
|
6fa738e3b8 | ||
|
8f9a58bfc8 | ||
|
560b97e9a5 | ||
|
c0cadee18d | ||
|
f5a0df82fc | ||
|
7faea40d87 | ||
|
767ffe90b0 | ||
|
eee2cb730e | ||
|
57b8625454 | ||
|
2a9d04b2ef | ||
|
9371b3181f | ||
|
904e630d43 | ||
|
801f159059 | ||
|
9bdf18eef6 | ||
|
d913ca0344 | ||
|
517c28a183 | ||
|
d91b971bfe | ||
|
1b6f280371 | ||
|
316e42f7b7 | ||
|
886f5f8f70 | ||
|
db0663fcc4 | ||
|
c78d416e16 | ||
|
4834749423 | ||
|
478b428a0f | ||
|
9440717a00 | ||
|
35df87d1e6 | ||
|
c8f3fe0511 | ||
|
1404901313 | ||
|
b96561477a | ||
|
6217edccf7 | ||
|
c1a08e2e97 | ||
|
63acb48182 | ||
|
cff5dd6093 | ||
|
9a9bc951db | ||
|
0f6fd2c338 | ||
|
72eee35f51 | ||
|
3ceb09549c | ||
|
41e94d4ce8 | ||
|
ca5984cd79 | ||
|
9c4f620ec0 | ||
|
c21bc3075c | ||
|
cb8ffc86a4 | ||
|
13068eca5c | ||
|
0529fb4635 | ||
|
39510e8b21 | ||
|
9c2939ab37 | ||
|
8bdf1ac44d | ||
|
6a52f8026c | ||
|
132455395e | ||
|
efde3e6914 | ||
|
31c51b804f | ||
|
cc62a4eec1 | ||
|
edd43c8dff | ||
|
43fadef68d | ||
|
7b1973dbad | ||
|
11ebc51ab6 | ||
|
12aefaa143 | ||
|
deb0303f62 | ||
|
75f992ef02 | ||
|
b411291da7 | ||
|
0addc624db | ||
|
546ecd1f77 | ||
|
19260fac56 | ||
|
e75f009af4 | ||
|
402a9a199f | ||
|
e2bba80797 | ||
|
9259233c6f | ||
|
49eb8a4df3 | ||
|
54906cb768 | ||
|
f82ef0e5cc | ||
|
a53cffb0d8 | ||
|
b27a99aaa3 | ||
|
275f35b31e | ||
|
3fc6530531 | ||
|
0dc8829a31 | ||
|
7813853736 | ||
|
1932c30a36 | ||
|
6a59102c52 | ||
|
138a053754 | ||
|
bd48a138c9 | ||
|
1b19fdf56a | ||
|
866a80a552 | ||
|
1912aa1834 | ||
|
e3520f5216 | ||
|
e7e800b2d2 | ||
|
018d149fb4 | ||
|
54ca3fa8a7 | ||
|
81dd94572d | ||
|
174465878b | ||
|
6042c6f7bb | ||
|
37b09fcce1 | ||
|
dd4d01b42b | ||
|
4cb26d8184 | ||
|
9fa349f3c1 | ||
|
053ca52f4f | ||
|
1e897d2c91 | ||
|
9ffb883cc1 | ||
|
9738f36bf2 | ||
|
2aedf5a188 | ||
|
d4829073e1 | ||
|
31299530d0 | ||
|
5e0bb245ed | ||
|
fcf02a2bfa | ||
|
896fafccef | ||
|
27b6fcb839 | ||
|
8d9d244d67 | ||
|
061c782ff4 | ||
|
c8b39b5d10 | ||
|
b4063b7684 | ||
|
2b968526c8 | ||
|
6e5729afd2 | ||
|
0d32654e2e | ||
|
6e1daf43d0 | ||
|
f731973f8f | ||
|
fb2e73ab31 | ||
|
aebad10a9c | ||
|
fc55460f2e | ||
|
367d987e1f | ||
|
7faaca45b9 | ||
|
b8e1708ee4 | ||
|
e31a50478e | ||
|
699dc63459 | ||
|
02d8571cd7 | ||
|
8a827c5e3a | ||
|
a29dd15c88 | ||
|
7e5192b053 | ||
|
5547bfeae4 | ||
|
d1dba98bb6 | ||
|
11205c5338 | ||
|
44b6373ada | ||
|
5aba3127c8 | ||
|
7c94a38683 | ||
|
d99232ebc7 | ||
|
c55a2c98ab | ||
|
4c00df007b | ||
|
67bb3e215b | ||
|
4a1647a41e | ||
|
d250096a6f | ||
|
32f8ded652 | ||
|
ebc8122c99 | ||
|
3b2776395b | ||
|
34b91fca23 | ||
|
1890985bec | ||
|
afdba5fd5d | ||
|
f8bba34b34 | ||
|
e17aa9d1be | ||
|
d87e594fe9 | ||
|
09c9a6ca2b | ||
|
66a719ebfb | ||
|
f6c017de77 | ||
|
b9f354ae50 | ||
|
c580288e35 | ||
|
8da2a86f39 | ||
|
534f02e54a | ||
|
5cd0fdff9b | ||
|
2df8868fcf | ||
|
f2dd7434db | ||
|
35ca4fbc3d | ||
|
396e8fff4f | ||
|
4c76108499 | ||
|
3035cbdde6 | ||
|
3fa1ed2796 | ||
|
ea72da47df | ||
|
aeac92275c | ||
|
1ce266b3c5 | ||
|
f372015bb2 | ||
|
1c2188ff21 | ||
|
5f94bb6bc9 | ||
|
17868d948a | ||
|
039d03dba8 | ||
|
1152826ccb | ||
|
a5e9b559b8 | ||
|
430c280789 | ||
|
c00e7355cf | ||
|
a93596c51c | ||
|
69f9562b01 | ||
|
284cff48c2 | ||
|
2cc861145e | ||
|
2d2755e914 | ||
|
eecbbfb121 | ||
|
a85b77b4bc | ||
|
c7b677279a | ||
|
5bffcaf83d | ||
|
dcba831c6b | ||
|
52c10ad71d | ||
|
7ba14f1d88 | ||
|
793c9c1084 | ||
|
43d382d9b7 | ||
|
9cbf3a9892 | ||
|
145e936cfc | ||
|
a8fd592c46 | ||
|
bec7ce1fba | ||
|
2c4d5d2e7e | ||
|
9994c9ba24 | ||
|
bb6be90560 | ||
|
9665f2d1c8 | ||
|
7f6bf155b7 | ||
|
4f2d4a5098 | ||
|
120d3bf6df | ||
|
d9f9035113 | ||
|
c30084da57 | ||
|
437260f3da | ||
|
c85059398b | ||
|
351d0a0880 | ||
|
e8411a0718 | ||
|
636f014f42 | ||
|
4f16f81017 | ||
|
b54e90f3d4 | ||
|
f590ddc077 | ||
|
ec26a1ff9c | ||
|
2334ad53e8 | ||
|
c95a1a922e | ||
|
fff1a8979f | ||
|
60cae0a8f4 | ||
|
4fca4fabb8 | ||
|
60d8040189 | ||
|
60fe5519dc | ||
|
49fc5249b1 | ||
|
da78018f72 | ||
|
2515fcf15d | ||
|
d970ba5372 | ||
|
375cc397d8 | ||
|
0cc1b6d78d | ||
|
faf8271cac | ||
|
83c275da7a | ||
|
dc67185a72 | ||
|
cb47ccb925 | ||
|
1627e26525 | ||
|
b3bb8c6774 | ||
|
cee70d482c | ||
|
50373001a9 | ||
|
68c8ce6b76 | ||
|
ba3df4749d | ||
|
2ffed3c768 | ||
|
64068e13f9 | ||
|
32a02f397a | ||
|
4b610b9f3b | ||
|
e2a0375f80 | ||
|
26adc910bf | ||
|
01ed21e5d8 | ||
|
50da4e2efd | ||
|
ef1c440b4a | ||
|
14a1612607 | ||
|
e9508e259f | ||
|
cbeb8ad918 | ||
|
be023e0627 | ||
|
82308ece97 | ||
|
5ad9d079a1 | ||
|
b0b8a2c809 | ||
|
7a458ea13c | ||
|
6d636e4c12 | ||
|
3f34af546e | ||
|
597ca492a6 | ||
|
514807ef70 | ||
|
d8e95e23b1 | ||
|
d968bebf7f | ||
|
7c726b4851 | ||
|
904d37d6f0 | ||
|
e35b93013f | ||
|
de7ff05560 | ||
|
a818633ea2 | ||
|
c09aa2645e | ||
|
7d4b02ad64 | ||
|
acf7654451 | ||
|
c53bef489c | ||
|
f2fff2a331 | ||
|
9132de92f6 | ||
|
7910b565d7 | ||
|
3ee7571617 | ||
|
678c5098e2 | ||
|
fb7c79d594 | ||
|
fe79a833c3 | ||
|
f5d23dc5ee | ||
|
62f0b58073 |
51 changed files with 9277 additions and 10586 deletions
.babelrc.eslintrc.json.flowconfig.gitignoreLICENSEREADME.md
dist
package.jsonrollup.config.jssrc
webpack.config.jsyarn.lock
6
.babelrc
6
.babelrc
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"presets": ["env", "stage-2"],
|
||||
"plugins": ["transform-flow-comments"]
|
||||
"presets": ["@babel/preset-env"],
|
||||
"plugins": [
|
||||
"@babel/plugin-transform-flow-strip-types"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -22,9 +22,13 @@
|
|||
"__": true
|
||||
},
|
||||
"rules": {
|
||||
"consistent-return": 0,
|
||||
"import/extensions": 0,
|
||||
"import/no-commonjs": "warn",
|
||||
"import/no-amd": "warn",
|
||||
"import/prefer-default-export": "ignore",
|
||||
"func-names": ["warn", "as-needed"]
|
||||
"flowtype/generic-spacing": 0,
|
||||
"func-names": ["warn", "as-needed"],
|
||||
"no-plusplus": 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[ignore]
|
||||
node_modules/
|
||||
|
||||
[include]
|
||||
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1,5 @@
|
|||
/node_modules
|
||||
yarn-error.log
|
||||
|
||||
# Jetbrains
|
||||
.idea/
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2018 LBRY Inc
|
||||
Copyright (c) 2017-2020 LBRY Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
|
|
|
@ -9,14 +9,17 @@ Add `lbryinc` as a dependency to your `package.json` file.
|
|||
If you intend to make changes to the module and test immediately, you can use `npm link` to add the package to your `node_modules` folder. This will create a symlink to the folder where `lbry-redux` was cloned to.
|
||||
```
|
||||
cd lbryinc
|
||||
sudo npm link
|
||||
cd /<path>/<to>/<project>/node_modules
|
||||
npm link lbryinc
|
||||
yarn link
|
||||
cd /<path>/<to>/<project> (ex: cd ~/lbry-desktop)
|
||||
yarn link lbryinc
|
||||
````
|
||||
|
||||
### Build
|
||||
Run `$ yarn build`. If the symlink does not work, just build the file and move the `bundle.js` file in to the `node_modules/` folder.
|
||||
|
||||
### Automatic rebuild
|
||||
To have the code automatically rebuild upon changes you can run `$ yarn dev` which will use `rollup` to watch the files and build upon detection of updated source code.
|
||||
|
||||
## License
|
||||
|
||||
[MIT © LBRY](LICENSE)
|
||||
|
|
1611
dist/bundle.es.js
vendored
Normal file
1611
dist/bundle.es.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
11730
dist/bundle.js
vendored
11730
dist/bundle.js
vendored
File diff suppressed because it is too large
Load diff
28
package.json
28
package.json
|
@ -19,23 +19,30 @@
|
|||
"email": "hello@lbry.io"
|
||||
},
|
||||
"main": "dist/bundle.js",
|
||||
"module": "dist/bundle.es.js",
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"build": "rollup --config && webpack",
|
||||
"dev": "rollup --config --watch",
|
||||
"precommit": "lint-staged",
|
||||
"preinstall": "yarn cache clean lbry-redux",
|
||||
"lint": "eslint 'src/**/*.js' --fix",
|
||||
"format": "prettier 'src/**/*.{js,json}' --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"lbry-redux": "lbryio/lbry-redux",
|
||||
"reselect": "^3.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"lbry-redux": "lbryio/lbry-redux"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.0",
|
||||
"@babel/core": "^7.4.3",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.4.0",
|
||||
"@babel/preset-env": "^7.4.3",
|
||||
"@babel/preset-stage-2": "^7.0.0",
|
||||
"babel-eslint": "^8.0.3",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-loader": "^8.0.5",
|
||||
"babel-plugin-module-resolver": "^3.0.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-stage-2": "^6.18.0",
|
||||
"cross-env": "^5.2.0",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-airbnb-base": "^12.1.0",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
|
@ -43,14 +50,19 @@
|
|||
"eslint-plugin-flowtype": "^2.40.1",
|
||||
"eslint-plugin-import": "^2.10.0",
|
||||
"eslint-plugin-prettier": "^2.4.0",
|
||||
"flow-babel-webpack-plugin": "^1.1.1",
|
||||
"flow-bin": "^0.69.0",
|
||||
"flow-typed": "^2.4.0",
|
||||
"husky": "^0.14.3",
|
||||
"lint-staged": "^7.0.4",
|
||||
"prettier": "^1.4.2",
|
||||
"rollup": "^1.8.0",
|
||||
"rollup-plugin-alias": "^2.0.0",
|
||||
"rollup-plugin-babel": "^4.3.2",
|
||||
"rollup-plugin-copy": "^3.1.0",
|
||||
"rollup-plugin-flow": "^1.1.1",
|
||||
"rollup-plugin-includepaths": "^0.2.3",
|
||||
"webpack": "^4.5.0",
|
||||
"webpack-cli": "^2.0.14"
|
||||
"webpack-cli": "^3.3.7"
|
||||
},
|
||||
"engines": {
|
||||
"yarn": "^1.3"
|
||||
|
|
38
rollup.config.js
Normal file
38
rollup.config.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
import babel from 'rollup-plugin-babel';
|
||||
import flow from 'rollup-plugin-flow';
|
||||
import includePaths from 'rollup-plugin-includepaths';
|
||||
import copy from 'rollup-plugin-copy';
|
||||
import alias from 'rollup-plugin-alias';
|
||||
|
||||
let includePathOptions = {
|
||||
include: {},
|
||||
paths: ['src'],
|
||||
external: [],
|
||||
extensions: ['.js'],
|
||||
};
|
||||
|
||||
export default {
|
||||
input: 'src/index.js',
|
||||
output: {
|
||||
file: 'dist/bundle.es.js',
|
||||
format: 'cjs',
|
||||
},
|
||||
plugins: [
|
||||
alias({
|
||||
entries: [
|
||||
{
|
||||
find: 'flow-typed',
|
||||
replacement: './flow-typed',
|
||||
},
|
||||
],
|
||||
}),
|
||||
flow({ all: true }),
|
||||
includePaths(includePathOptions),
|
||||
babel({
|
||||
babelrc: false,
|
||||
presets: [],
|
||||
}),
|
||||
copy({ targets: [{ src: './flow-typed', dest: 'dist' }] }),
|
||||
],
|
||||
external: ['lbry-redux'],
|
||||
};
|
|
@ -1,3 +1,97 @@
|
|||
// Claims
|
||||
export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';
|
||||
export const FETCH_FEATURED_CONTENT_COMPLETED = 'FETCH_FEATURED_CONTENT_COMPLETED';
|
||||
export const FETCH_TRENDING_CONTENT_STARTED = 'FETCH_TRENDING_CONTENT_STARTED';
|
||||
export const FETCH_TRENDING_CONTENT_COMPLETED = 'FETCH_TRENDING_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_STARTED = 'FETCH_CHANNEL_LIST_STARTED';
|
||||
export const FETCH_CHANNEL_LIST_COMPLETED = 'FETCH_CHANNEL_LIST_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 = 'SET_PLAYING_URI';
|
||||
export const SET_CONTENT_POSITION = 'SET_CONTENT_POSITION';
|
||||
export const SET_CONTENT_LAST_VIEWED = 'SET_CONTENT_LAST_VIEWED';
|
||||
export const CLEAR_CONTENT_HISTORY_URI = 'CLEAR_CONTENT_HISTORY_URI';
|
||||
export const CLEAR_CONTENT_HISTORY_ALL = 'CLEAR_CONTENT_HISTORY_ALL';
|
||||
|
||||
// Subscriptions
|
||||
export const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
|
||||
export const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
|
||||
export const CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS =
|
||||
'CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS';
|
||||
export const CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS =
|
||||
'CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS';
|
||||
export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
|
||||
export const SET_SUBSCRIPTION_LATEST = 'SET_SUBSCRIPTION_LATEST';
|
||||
export const UPDATE_SUBSCRIPTION_UNREADS = 'UPDATE_SUBSCRIPTION_UNREADS';
|
||||
export const REMOVE_SUBSCRIPTION_UNREADS = 'REMOVE_SUBSCRIPTION_UNREADS';
|
||||
export const CHECK_SUBSCRIPTION_STARTED = 'CHECK_SUBSCRIPTION_STARTED';
|
||||
export const CHECK_SUBSCRIPTION_COMPLETED = 'CHECK_SUBSCRIPTION_COMPLETED';
|
||||
export const CHECK_SUBSCRIPTIONS_SUBSCRIBE = 'CHECK_SUBSCRIPTIONS_SUBSCRIBE';
|
||||
export const FETCH_SUBSCRIPTIONS_START = 'FETCH_SUBSCRIPTIONS_START';
|
||||
export const FETCH_SUBSCRIPTIONS_FAIL = 'FETCH_SUBSCRIPTIONS_FAIL';
|
||||
export const FETCH_SUBSCRIPTIONS_SUCCESS = 'FETCH_SUBSCRIPTIONS_SUCCESS';
|
||||
export const SET_VIEW_MODE = 'SET_VIEW_MODE';
|
||||
export const GET_SUGGESTED_SUBSCRIPTIONS_START = 'GET_SUGGESTED_SUBSCRIPTIONS_START';
|
||||
export const GET_SUGGESTED_SUBSCRIPTIONS_SUCCESS = 'GET_SUGGESTED_SUBSCRIPTIONS_SUCCESS';
|
||||
export const GET_SUGGESTED_SUBSCRIPTIONS_FAIL = 'GET_SUGGESTED_SUBSCRIPTIONS_FAIL';
|
||||
export const SUBSCRIPTION_FIRST_RUN_COMPLETED = 'SUBSCRIPTION_FIRST_RUN_COMPLETED';
|
||||
export const VIEW_SUGGESTED_SUBSCRIPTIONS = 'VIEW_SUGGESTED_SUBSCRIPTIONS';
|
||||
|
||||
// Blacklist
|
||||
export const FETCH_BLACK_LISTED_CONTENT_STARTED = 'FETCH_BLACK_LISTED_CONTENT_STARTED';
|
||||
export const FETCH_BLACK_LISTED_CONTENT_COMPLETED = 'FETCH_BLACK_LISTED_CONTENT_COMPLETED';
|
||||
export const FETCH_BLACK_LISTED_CONTENT_FAILED = 'FETCH_BLACK_LISTED_CONTENT_FAILED';
|
||||
export const BLACK_LISTED_CONTENT_SUBSCRIBE = 'BLACK_LISTED_CONTENT_SUBSCRIBE';
|
||||
|
||||
// Filtered list
|
||||
export const FETCH_FILTERED_CONTENT_STARTED = 'FETCH_FILTERED_CONTENT_STARTED';
|
||||
export const FETCH_FILTERED_CONTENT_COMPLETED = 'FETCH_FILTERED_CONTENT_COMPLETED';
|
||||
export const FETCH_FILTERED_CONTENT_FAILED = 'FETCH_FILTERED_CONTENT_FAILED';
|
||||
export const FILTERED_CONTENT_SUBSCRIBE = 'FILTERED_CONTENT_SUBSCRIBE';
|
||||
|
||||
// Cost Info
|
||||
export const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED';
|
||||
export const FETCH_COST_INFO_COMPLETED = 'FETCH_COST_INFO_COMPLETED';
|
||||
|
||||
// Stats
|
||||
export const FETCH_VIEW_COUNT_STARTED = 'FETCH_VIEW_COUNT_STARTED';
|
||||
export const FETCH_VIEW_COUNT_FAILED = 'FETCH_VIEW_COUNT_FAILED';
|
||||
export const FETCH_VIEW_COUNT_COMPLETED = 'FETCH_VIEW_COUNT_COMPLETED';
|
||||
export const FETCH_SUB_COUNT_STARTED = 'FETCH_SUB_COUNT_STARTED';
|
||||
export const FETCH_SUB_COUNT_FAILED = 'FETCH_SUB_COUNT_FAILED';
|
||||
export const FETCH_SUB_COUNT_COMPLETED = 'FETCH_SUB_COUNT_COMPLETED';
|
||||
|
||||
// Cross-device Sync
|
||||
export const GET_SYNC_STARTED = 'GET_SYNC_STARTED';
|
||||
export const GET_SYNC_COMPLETED = 'GET_SYNC_COMPLETED';
|
||||
export const GET_SYNC_FAILED = 'GET_SYNC_FAILED';
|
||||
export const SET_SYNC_STARTED = 'SET_SYNC_STARTED';
|
||||
export const SET_SYNC_FAILED = 'SET_SYNC_FAILED';
|
||||
export const SET_SYNC_COMPLETED = 'SET_SYNC_COMPLETED';
|
||||
export const SET_DEFAULT_ACCOUNT = 'SET_DEFAULT_ACCOUNT';
|
||||
export const SYNC_APPLY_STARTED = 'SYNC_APPLY_STARTED';
|
||||
export const SYNC_APPLY_COMPLETED = 'SYNC_APPLY_COMPLETED';
|
||||
export const SYNC_APPLY_FAILED = 'SYNC_APPLY_FAILED';
|
||||
export const SYNC_APPLY_BAD_PASSWORD = 'SYNC_APPLY_BAD_PASSWORD';
|
||||
export const SYNC_RESET = 'SYNC_RESET';
|
||||
|
||||
// Lbry.tv
|
||||
export const UPDATE_UPLOAD_PROGRESS = 'UPDATE_UPLOAD_PROGRESS';
|
||||
|
||||
// User
|
||||
export const GENERATE_AUTH_TOKEN_FAILURE = 'GENERATE_AUTH_TOKEN_FAILURE';
|
||||
export const GENERATE_AUTH_TOKEN_STARTED = 'GENERATE_AUTH_TOKEN_STARTED';
|
||||
export const GENERATE_AUTH_TOKEN_SUCCESS = 'GENERATE_AUTH_TOKEN_SUCCESS';
|
||||
|
|
5
src/constants/claim.js
Normal file
5
src/constants/claim.js
Normal file
|
@ -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;
|
4
src/constants/errors.js
Normal file
4
src/constants/errors.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
export const ALREADY_CLAIMED =
|
||||
'once the invite reward has been claimed the referrer cannot be changed';
|
||||
export const REFERRER_NOT_FOUND =
|
||||
'A lbry.tv account could not be found for the referrer you provided.';
|
11
src/constants/youtube.js
Normal file
11
src/constants/youtube.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
export const YOUTUBE_SYNC_NOT_TRANSFERRED = 'not_transferred';
|
||||
export const YOUTUBE_SYNC_PENDING = 'pending';
|
||||
export const YOUTUBE_SYNC_PENDING_EMAIL = 'pendingemail';
|
||||
export const YOUTUBE_SYNC_PENDING_TRANSFER = 'pending_transfer';
|
||||
export const YOUTUBE_SYNC_COMPLETED_TRANSFER = 'completed_transfer';
|
||||
export const YOUTUBE_SYNC_QUEUED = 'queued';
|
||||
export const YOUTUBE_SYNC_SYNCING = 'syncing';
|
||||
export const YOUTUBE_SYNC_SYNCED = 'synced';
|
||||
export const YOUTUBE_SYNC_FAILED = 'failed';
|
||||
export const YOUTUBE_SYNC_PENDINGUPGRADE = 'pendingupgrade';
|
||||
export const YOUTUBE_SYNC_ABANDONDED = 'abandoned';
|
120
src/index.js
120
src/index.js
|
@ -1,71 +1,79 @@
|
|||
import * as LBRYINC_ACTIONS from 'constants/action_types';
|
||||
import * as YOUTUBE_STATUSES from 'constants/youtube';
|
||||
import * as ERRORS from 'constants/errors';
|
||||
import Lbryio from 'lbryio';
|
||||
|
||||
// constants
|
||||
export { LBRYINC_ACTIONS };
|
||||
|
||||
// Lbryio
|
||||
export { Lbryio };
|
||||
|
||||
// constants
|
||||
export { LBRYINC_ACTIONS, YOUTUBE_STATUSES, ERRORS };
|
||||
|
||||
// utils
|
||||
export { doTransifexUpload } from 'util/transifex-upload';
|
||||
|
||||
// actions
|
||||
export { doGenerateAuthToken } from 'redux/actions/auth';
|
||||
export { doFetchCostInfoForUri } from 'redux/actions/cost_info';
|
||||
export { doBlackListedOutpointsSubscribe } from 'redux/actions/blacklist';
|
||||
export { doFilteredOutpointsSubscribe } from 'redux/actions/filtered';
|
||||
export { doFetchFeaturedUris, doFetchTrendingUris } from 'redux/actions/homepage';
|
||||
export { doFetchViewCount, doFetchSubCount } from 'redux/actions/stats';
|
||||
export {
|
||||
doRewardList,
|
||||
doClaimRewardType,
|
||||
doClaimEligiblePurchaseRewards,
|
||||
doClaimRewardClearError,
|
||||
} from 'redux/actions/rewards';
|
||||
export { doFetchInviteStatus, doInstallNew, doAuthenticate, doUserFetch } from 'redux/actions/user';
|
||||
doCheckSync,
|
||||
doGetSync,
|
||||
doSetSync,
|
||||
doSetDefaultAccount,
|
||||
doSyncApply,
|
||||
doResetSync,
|
||||
doSyncEncryptAndDecrypt,
|
||||
} from 'redux/actions/sync';
|
||||
export { doUpdateUploadProgress } from 'redux/actions/web';
|
||||
|
||||
// reducers
|
||||
export { authReducer } from 'redux/reducers/auth';
|
||||
export { rewardsReducer } from 'redux/reducers/rewards';
|
||||
export { userReducer } from 'redux/reducers/user';
|
||||
export { costInfoReducer } from 'redux/reducers/cost_info';
|
||||
export { blacklistReducer } from 'redux/reducers/blacklist';
|
||||
export { filteredReducer } from 'redux/reducers/filtered';
|
||||
export { homepageReducer } from 'redux/reducers/homepage';
|
||||
export { statsReducer } from 'redux/reducers/stats';
|
||||
export { syncReducer } from 'redux/reducers/sync';
|
||||
export { webReducer } from 'redux/reducers/web';
|
||||
|
||||
// selectors
|
||||
export { selectAuthToken } from 'redux/selectors/auth';
|
||||
export { selectAuthToken, selectIsAuthenticating } from 'redux/selectors/auth';
|
||||
export {
|
||||
makeSelectClaimRewardError,
|
||||
makeSelectIsRewardClaimPending,
|
||||
makeSelectRewardAmountByType,
|
||||
makeSelectRewardByType,
|
||||
selectUnclaimedRewardsByType,
|
||||
selectClaimedRewardsById,
|
||||
selectClaimedRewards,
|
||||
selectClaimedRewardsByTransactionId,
|
||||
selectUnclaimedRewards,
|
||||
selectFetchingRewards,
|
||||
selectUnclaimedRewardValue,
|
||||
selectClaimsPendingByType,
|
||||
selectIsClaimRewardPending,
|
||||
selectClaimErrorsByType,
|
||||
selectClaimRewardError,
|
||||
selectRewardByType,
|
||||
} from 'redux/selectors/rewards';
|
||||
makeSelectFetchingCostInfoForUri,
|
||||
makeSelectCostInfoForUri,
|
||||
selectAllCostInfoByUri,
|
||||
selectFetchingCostInfo,
|
||||
} from 'redux/selectors/cost_info';
|
||||
export {
|
||||
selectAuthenticationIsPending,
|
||||
selectUserIsPending,
|
||||
selectUser,
|
||||
selectUserEmail,
|
||||
selectUserPhone,
|
||||
selectUserCountryCode,
|
||||
selectEmailToVerify,
|
||||
selectPhoneToVerify,
|
||||
selectUserIsRewardApproved,
|
||||
selectEmailNewIsPending,
|
||||
selectEmailNewErrorMessage,
|
||||
selectPhoneNewErrorMessage,
|
||||
selectEmailVerifyIsPending,
|
||||
selectEmailVerifyErrorMessage,
|
||||
selectPhoneVerifyErrorMessage,
|
||||
selectIdentityVerifyIsPending,
|
||||
selectIdentityVerifyErrorMessage,
|
||||
selectUserIsVerificationCandidate,
|
||||
selectAccessToken,
|
||||
selectUserInviteStatusIsPending,
|
||||
selectUserInvitesRemaining,
|
||||
selectUserInvitees,
|
||||
selectUserInviteStatusFailed,
|
||||
selectUserInviteNewIsPending,
|
||||
selectUserInviteNewErrorMessage,
|
||||
} from 'redux/selectors/user';
|
||||
selectBlackListedOutpoints,
|
||||
selectBlacklistedOutpointMap,
|
||||
} from 'redux/selectors/blacklist';
|
||||
export { selectFilteredOutpoints, selectFilteredOutpointMap } from 'redux/selectors/filtered';
|
||||
export {
|
||||
selectFeaturedUris,
|
||||
selectFetchingFeaturedUris,
|
||||
selectTrendingUris,
|
||||
selectFetchingTrendingUris,
|
||||
} from 'redux/selectors/homepage';
|
||||
export {
|
||||
selectViewCount,
|
||||
makeSelectViewCountForUri,
|
||||
makeSelectSubCountForUri,
|
||||
} from 'redux/selectors/stats';
|
||||
export {
|
||||
selectHasSyncedWallet,
|
||||
selectSyncData,
|
||||
selectSyncHash,
|
||||
selectSetSyncErrorMessage,
|
||||
selectGetSyncErrorMessage,
|
||||
selectGetSyncIsPending,
|
||||
selectSetSyncIsPending,
|
||||
selectSyncApplyIsPending,
|
||||
selectHashChanged,
|
||||
selectSyncApplyErrorMessage,
|
||||
selectSyncApplyPasswordError,
|
||||
} from 'redux/selectors/sync';
|
||||
export { selectCurrentUploads, selectUploadCount } from 'redux/selectors/web';
|
||||
|
|
176
src/lbryio.js
176
src/lbryio.js
|
@ -1,15 +1,22 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import { doGenerateAuthToken } from 'redux/actions/auth';
|
||||
import querystring from 'querystring';
|
||||
|
||||
const Lbryio = {
|
||||
enabled: true,
|
||||
authenticationPromise: null,
|
||||
exchangePromise: null,
|
||||
exchangeLastFetched: null,
|
||||
CONNECTION_STRING: 'https://api.lbry.com/',
|
||||
};
|
||||
|
||||
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;
|
||||
const INTERNAL_APIS_DOWN = 'internal_apis_down';
|
||||
|
||||
// We can't use env's because they aren't passed into node_modules
|
||||
Lbryio.setLocalApi = endpoint => {
|
||||
Lbryio.CONNECTION_STRING = endpoint.replace(/\/*$/, '/'); // exactly one slash at the end;
|
||||
};
|
||||
|
||||
Lbryio.call = (resource, action, params = {}, method = 'get') => {
|
||||
if (!Lbryio.enabled) {
|
||||
|
@ -24,16 +31,22 @@ Lbryio.call = (resource, action, params = {}, method = 'get') => {
|
|||
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);
|
||||
});
|
||||
|
||||
if (response.status === 500) {
|
||||
return Promise.reject(INTERNAL_APIS_DOWN);
|
||||
}
|
||||
|
||||
if (response)
|
||||
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) {
|
||||
|
@ -42,8 +55,15 @@ Lbryio.call = (resource, action, params = {}, method = 'get') => {
|
|||
|
||||
return Lbryio.getAuthToken().then(token => {
|
||||
const fullParams = { auth_token: token, ...params };
|
||||
Object.keys(fullParams).forEach(key => {
|
||||
const value = fullParams[key];
|
||||
if (typeof value === 'object') {
|
||||
fullParams[key] = JSON.stringify(value);
|
||||
}
|
||||
});
|
||||
|
||||
const qs = querystring.stringify(fullParams);
|
||||
let url = `${CONNECTION_STRING}${resource}/${action}?${qs}`;
|
||||
let url = `${Lbryio.CONNECTION_STRING}${resource}/${action}?${qs}`;
|
||||
|
||||
let options = {
|
||||
method: 'GET',
|
||||
|
@ -57,7 +77,7 @@ Lbryio.call = (resource, action, params = {}, method = 'get') => {
|
|||
},
|
||||
body: qs,
|
||||
};
|
||||
url = `${CONNECTION_STRING}${resource}/${action}`;
|
||||
url = `${Lbryio.CONNECTION_STRING}${resource}/${action}`;
|
||||
}
|
||||
|
||||
return makeRequest(url, options).then(response => response.data);
|
||||
|
@ -70,7 +90,11 @@ Lbryio.getAuthToken = () =>
|
|||
new Promise(resolve => {
|
||||
if (Lbryio.authToken) {
|
||||
resolve(Lbryio.authToken);
|
||||
} else {
|
||||
} else if (Lbryio.overrides.getAuthToken) {
|
||||
Lbryio.overrides.getAuthToken().then(token => {
|
||||
resolve(token);
|
||||
});
|
||||
} else if (typeof window !== 'undefined') {
|
||||
const { store } = window;
|
||||
if (store) {
|
||||
const state = store.getState();
|
||||
|
@ -79,23 +103,27 @@ Lbryio.getAuthToken = () =>
|
|||
resolve(token);
|
||||
}
|
||||
|
||||
resolve(null);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
|
||||
Lbryio.getCurrentUser = () => Lbryio.call('user', 'me');
|
||||
|
||||
Lbryio.authenticate = () => {
|
||||
Lbryio.authenticate = (domain, language) => {
|
||||
if (!Lbryio.enabled) {
|
||||
const params = {
|
||||
id: 1,
|
||||
primary_email: 'disabled@lbry.io',
|
||||
has_verified_email: true,
|
||||
is_identity_verified: true,
|
||||
is_reward_approved: false,
|
||||
language: language || 'en',
|
||||
};
|
||||
|
||||
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,
|
||||
});
|
||||
resolve(params);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -109,23 +137,66 @@ Lbryio.authenticate = () => {
|
|||
|
||||
// check that token works
|
||||
return Lbryio.getCurrentUser()
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
.then(user => user)
|
||||
.catch(error => {
|
||||
if (error === INTERNAL_APIS_DOWN) {
|
||||
throw new Error('Internal APIS down');
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
})
|
||||
.then(isTokenValid => {
|
||||
if (isTokenValid) {
|
||||
return reject;
|
||||
.then(user => {
|
||||
if (user) {
|
||||
return user;
|
||||
}
|
||||
|
||||
return Lbry.status().then(status => {
|
||||
const { store } = window;
|
||||
if (store) {
|
||||
store.dispatch(doGenerateAuthToken(status.installation_id));
|
||||
return resolve();
|
||||
}
|
||||
return Lbry.status()
|
||||
.then(
|
||||
status =>
|
||||
new Promise((res, rej) => {
|
||||
const appId =
|
||||
domain && domain !== 'lbry.tv'
|
||||
? (domain.replace(/[.]/gi, '') + status.installation_id).slice(0, 66)
|
||||
: status.installation_id;
|
||||
Lbryio.call(
|
||||
'user',
|
||||
'new',
|
||||
{
|
||||
auth_token: '',
|
||||
language: language || 'en',
|
||||
app_id: appId,
|
||||
},
|
||||
'post'
|
||||
)
|
||||
.then(response => {
|
||||
if (!response.auth_token) {
|
||||
throw new Error('auth_token was not set in the response');
|
||||
}
|
||||
|
||||
return reject();
|
||||
});
|
||||
const { store } = window;
|
||||
if (Lbryio.overrides.setAuthToken) {
|
||||
Lbryio.overrides.setAuthToken(response.auth_token);
|
||||
}
|
||||
|
||||
if (store) {
|
||||
store.dispatch({
|
||||
type: ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS,
|
||||
data: { authToken: response.auth_token },
|
||||
});
|
||||
}
|
||||
Lbryio.authToken = response.auth_token;
|
||||
return res(response);
|
||||
})
|
||||
.catch(error => rej(error));
|
||||
})
|
||||
)
|
||||
.then(newUser => {
|
||||
if (!newUser) {
|
||||
return Lbryio.getCurrentUser();
|
||||
}
|
||||
return newUser;
|
||||
});
|
||||
})
|
||||
.then(resolve, reject);
|
||||
});
|
||||
|
@ -135,8 +206,33 @@ Lbryio.authenticate = () => {
|
|||
};
|
||||
|
||||
Lbryio.getStripeToken = () =>
|
||||
CONNECTION_STRING.startsWith('http://localhost:')
|
||||
Lbryio.CONNECTION_STRING.startsWith('http://localhost:')
|
||||
? 'pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
|
||||
: 'pk_live_e8M4dRNnCCbmpZzduEUZBgJO';
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// Allow overriding lbryio methods
|
||||
// The desktop app will need to use it for getAuthToken because we use electron's ipcRenderer
|
||||
Lbryio.overrides = {};
|
||||
Lbryio.setOverride = (methodName, newMethod) => {
|
||||
Lbryio.overrides[methodName] = newMethod;
|
||||
};
|
||||
|
||||
export default Lbryio;
|
||||
|
|
52
src/redux/actions/blacklist.js
Normal file
52
src/redux/actions/blacklist.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import Lbryio from 'lbryio';
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
const CHECK_BLACK_LISTED_CONTENT_INTERVAL = 60 * 60 * 1000;
|
||||
|
||||
export function doFetchBlackListedOutpoints() {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_STARTED,
|
||||
});
|
||||
|
||||
const success = ({ outpoints }) => {
|
||||
const splitOutpoints = [];
|
||||
if (outpoints) {
|
||||
outpoints.forEach((outpoint, index) => {
|
||||
const [txid, nout] = outpoint.split(':');
|
||||
|
||||
splitOutpoints[index] = { txid, nout: Number.parseInt(nout, 10) };
|
||||
});
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_COMPLETED,
|
||||
data: {
|
||||
outpoints: splitOutpoints,
|
||||
success: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const failure = ({ error }) => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_FAILED,
|
||||
data: {
|
||||
error,
|
||||
success: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Lbryio.call('file', 'list_blocked', {
|
||||
auth_token: '',
|
||||
}).then(success, failure);
|
||||
};
|
||||
}
|
||||
|
||||
export function doBlackListedOutpointsSubscribe() {
|
||||
return dispatch => {
|
||||
dispatch(doFetchBlackListedOutpoints());
|
||||
setInterval(() => dispatch(doFetchBlackListedOutpoints()), CHECK_BLACK_LISTED_CONTENT_INTERVAL);
|
||||
};
|
||||
}
|
35
src/redux/actions/cost_info.js
Normal file
35
src/redux/actions/cost_info.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import Lbryio from 'lbryio';
|
||||
import { selectClaimsByUri } from 'lbry-redux';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function doFetchCostInfoForUri(uri) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const claim = 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.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 });
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
47
src/redux/actions/filtered.js
Normal file
47
src/redux/actions/filtered.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
import Lbryio from 'lbryio';
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
const CHECK_FILTERED_CONTENT_INTERVAL = 60 * 60 * 1000;
|
||||
|
||||
export function doFetchFilteredOutpoints() {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_FILTERED_CONTENT_STARTED,
|
||||
});
|
||||
|
||||
const success = ({ outpoints }) => {
|
||||
let formattedOutpoints = [];
|
||||
if (outpoints) {
|
||||
formattedOutpoints = outpoints.map(outpoint => {
|
||||
const [txid, nout] = outpoint.split(':');
|
||||
return { txid, nout: Number.parseInt(nout, 10) };
|
||||
});
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_FILTERED_CONTENT_COMPLETED,
|
||||
data: {
|
||||
outpoints: formattedOutpoints,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const failure = ({ error }) => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_FILTERED_CONTENT_FAILED,
|
||||
data: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Lbryio.call('file', 'list_filtered', { auth_token: '' }).then(success, failure);
|
||||
};
|
||||
}
|
||||
|
||||
export function doFilteredOutpointsSubscribe() {
|
||||
return dispatch => {
|
||||
dispatch(doFetchFilteredOutpoints());
|
||||
setInterval(() => dispatch(doFetchFilteredOutpoints()), CHECK_FILTERED_CONTENT_INTERVAL);
|
||||
};
|
||||
}
|
78
src/redux/actions/homepage.js
Normal file
78
src/redux/actions/homepage.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
import Lbryio from 'lbryio';
|
||||
import { batchActions, doResolveUris } from 'lbry-redux';
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
export function doFetchFeaturedUris(offloadResolve = false) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_FEATURED_CONTENT_STARTED,
|
||||
});
|
||||
|
||||
const success = ({ Uris }) => {
|
||||
let urisToResolve = [];
|
||||
Object.keys(Uris).forEach(category => {
|
||||
urisToResolve = [...urisToResolve, ...Uris[category]];
|
||||
});
|
||||
|
||||
const actions = [
|
||||
{
|
||||
type: ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED,
|
||||
data: {
|
||||
uris: Uris,
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
if (urisToResolve.length && !offloadResolve) {
|
||||
actions.push(doResolveUris(urisToResolve));
|
||||
}
|
||||
|
||||
dispatch(batchActions(...actions));
|
||||
};
|
||||
|
||||
const failure = () => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED,
|
||||
data: {
|
||||
uris: {},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Lbryio.call('file', 'list_homepage').then(success, failure);
|
||||
};
|
||||
}
|
||||
|
||||
export function doFetchTrendingUris() {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_TRENDING_CONTENT_STARTED,
|
||||
});
|
||||
|
||||
const success = data => {
|
||||
const urisToResolve = data.map(uri => uri.url);
|
||||
const actions = [
|
||||
doResolveUris(urisToResolve),
|
||||
{
|
||||
type: ACTIONS.FETCH_TRENDING_CONTENT_COMPLETED,
|
||||
data: {
|
||||
uris: data,
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
dispatch(batchActions(...actions));
|
||||
};
|
||||
|
||||
const failure = () => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_TRENDING_CONTENT_COMPLETED,
|
||||
data: {
|
||||
uris: [],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Lbryio.call('file', 'list_trending').then(success, failure);
|
||||
};
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
import Lbryio from 'lbryio';
|
||||
import { doNotify, ACTIONS, MODALS } from 'lbry-redux';
|
||||
import { selectUnclaimedRewards } from 'redux/selectors/rewards';
|
||||
import { selectUserIsRewardApproved } from 'redux/selectors/user';
|
||||
import rewards from 'rewards';
|
||||
|
||||
export function doRewardList() {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_REWARDS_STARTED,
|
||||
});
|
||||
|
||||
Lbryio.call('reward', 'list', { multiple_rewards_per_type: true })
|
||||
.then(userRewards => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_REWARDS_COMPLETED,
|
||||
data: { userRewards },
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_REWARDS_COMPLETED,
|
||||
data: { userRewards: [] },
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimRewardType(rewardType, options) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const unclaimedRewards = selectUnclaimedRewards(state);
|
||||
const reward = unclaimedRewards.find(ur => ur.reward_type === rewardType);
|
||||
const userIsRewardApproved = selectUserIsRewardApproved(state);
|
||||
|
||||
if (!reward || reward.transaction_id) {
|
||||
// already claimed or doesn't exist, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userIsRewardApproved && rewardType !== rewards.TYPE_CONFIRM_EMAIL) {
|
||||
const action = doNotify({
|
||||
id: MODALS.REWARD_APPROVAL_REQUIRED,
|
||||
isError: false,
|
||||
});
|
||||
dispatch(action);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.CLAIM_REWARD_STARTED,
|
||||
data: { reward },
|
||||
});
|
||||
|
||||
const success = successReward => {
|
||||
dispatch({
|
||||
type: ACTIONS.CLAIM_REWARD_SUCCESS,
|
||||
data: {
|
||||
reward: successReward,
|
||||
},
|
||||
});
|
||||
if (successReward.reward_type === rewards.TYPE_NEW_USER) {
|
||||
const action = doNotify({
|
||||
id: MODALS.FIRST_REWARD,
|
||||
isError: false,
|
||||
});
|
||||
dispatch(action);
|
||||
}
|
||||
};
|
||||
|
||||
const failure = error => {
|
||||
dispatch({
|
||||
type: ACTIONS.CLAIM_REWARD_FAILURE,
|
||||
data: {
|
||||
reward,
|
||||
error: !options || !options.failSilently ? error : undefined,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
rewards.claimReward(rewardType).then(success, failure);
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimEligiblePurchaseRewards() {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const unclaimedRewards = selectUnclaimedRewards(state);
|
||||
const userIsRewardApproved = selectUserIsRewardApproved(state);
|
||||
|
||||
if (!userIsRewardApproved || !Lbryio.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (unclaimedRewards.find(ur => ur.reward_type === rewards.TYPE_FIRST_STREAM)) {
|
||||
dispatch(doClaimRewardType(rewards.TYPE_FIRST_STREAM));
|
||||
} else {
|
||||
[rewards.TYPE_MANY_DOWNLOADS, rewards.TYPE_FEATURED_DOWNLOAD].forEach(type => {
|
||||
dispatch(doClaimRewardType(type, { failSilently: true }));
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function doClaimRewardClearError(reward) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.CLAIM_REWARD_CLEAR_ERROR,
|
||||
data: { reward },
|
||||
});
|
||||
};
|
||||
}
|
32
src/redux/actions/stats.js
Normal file
32
src/redux/actions/stats.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
// @flow
|
||||
import Lbryio from 'lbryio';
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
export const doFetchViewCount = (claimIdCsv: string) => dispatch => {
|
||||
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_STARTED });
|
||||
|
||||
return Lbryio.call('file', 'view_count', { claim_id: claimIdCsv })
|
||||
.then((result: Array<number>) => {
|
||||
const viewCounts = result;
|
||||
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_COMPLETED, data: { claimIdCsv, viewCounts } });
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_FAILED, data: error });
|
||||
});
|
||||
};
|
||||
|
||||
export const doFetchSubCount = (claimId: string) => dispatch => {
|
||||
dispatch({ type: ACTIONS.FETCH_SUB_COUNT_STARTED });
|
||||
|
||||
return Lbryio.call('subscription', 'sub_count', { claim_id: claimId })
|
||||
.then((result: Array<number>) => {
|
||||
const subCount = result[0];
|
||||
dispatch({
|
||||
type: ACTIONS.FETCH_SUB_COUNT_COMPLETED,
|
||||
data: { claimId, subCount },
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch({ type: ACTIONS.FETCH_SUB_COUNT_FAILED, data: error });
|
||||
});
|
||||
};
|
288
src/redux/actions/sync.js
Normal file
288
src/redux/actions/sync.js
Normal file
|
@ -0,0 +1,288 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import Lbryio from 'lbryio';
|
||||
import { Lbry, doWalletEncrypt, doWalletDecrypt } from 'lbry-redux';
|
||||
|
||||
const NO_WALLET_ERROR = 'no wallet found for this user';
|
||||
|
||||
export function doSetDefaultAccount(success, failure) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.SET_DEFAULT_ACCOUNT,
|
||||
});
|
||||
|
||||
Lbry.account_list()
|
||||
.then(accountList => {
|
||||
const { lbc_mainnet: accounts } = accountList;
|
||||
let defaultId;
|
||||
for (let i = 0; i < accounts.length; ++i) {
|
||||
if (accounts[i].satoshis > 0) {
|
||||
defaultId = accounts[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// In a case where there's no balance on either account
|
||||
// assume the second (which is created after sync) as default
|
||||
if (!defaultId && accounts.length > 1) {
|
||||
defaultId = accounts[1].id;
|
||||
}
|
||||
|
||||
// Set the default account
|
||||
if (defaultId) {
|
||||
Lbry.account_set({ account_id: defaultId, default: true })
|
||||
.then(() => {
|
||||
if (success) {
|
||||
success();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if (failure) {
|
||||
failure(err);
|
||||
}
|
||||
});
|
||||
} else if (failure) {
|
||||
// no default account to set
|
||||
failure('Could not set a default account'); // fail
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if (failure) {
|
||||
failure(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doSetSync(oldHash, newHash, data) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.SET_SYNC_STARTED,
|
||||
});
|
||||
|
||||
return Lbryio.call('sync', 'set', { old_hash: oldHash, new_hash: newHash, data }, 'post')
|
||||
.then(response => {
|
||||
if (!response.hash) {
|
||||
throw Error('No hash returned for sync/set.');
|
||||
}
|
||||
|
||||
return dispatch({
|
||||
type: ACTIONS.SET_SYNC_COMPLETED,
|
||||
data: { syncHash: response.hash },
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch({
|
||||
type: ACTIONS.SET_SYNC_FAILED,
|
||||
data: { error },
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doGetSync(passedPassword, callback) {
|
||||
const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword;
|
||||
|
||||
function handleCallback(error, hasNewData) {
|
||||
if (callback) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error('Second argument passed to "doGetSync" must be a function');
|
||||
}
|
||||
|
||||
callback(error, hasNewData);
|
||||
}
|
||||
}
|
||||
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.GET_SYNC_STARTED,
|
||||
});
|
||||
|
||||
const data = {};
|
||||
|
||||
Lbry.wallet_status()
|
||||
.then(status => {
|
||||
if (status.is_locked) {
|
||||
return Lbry.wallet_unlock({ password });
|
||||
}
|
||||
|
||||
// Wallet is already unlocked
|
||||
return true;
|
||||
})
|
||||
.then(isUnlocked => {
|
||||
if (isUnlocked) {
|
||||
return Lbry.sync_hash();
|
||||
}
|
||||
data.unlockFailed = true;
|
||||
throw new Error();
|
||||
})
|
||||
.then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||
.then(response => {
|
||||
const syncHash = response.hash;
|
||||
data.syncHash = syncHash;
|
||||
data.syncData = response.data;
|
||||
data.changed = response.changed;
|
||||
data.hasSyncedWallet = true;
|
||||
|
||||
if (response.changed) {
|
||||
return Lbry.sync_apply({ password, data: response.data, blocking: true });
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response) {
|
||||
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||
handleCallback(null, data.changed);
|
||||
return;
|
||||
}
|
||||
|
||||
const { hash: walletHash, data: walletData } = response;
|
||||
|
||||
if (walletHash !== data.syncHash) {
|
||||
// different local hash, need to synchronise
|
||||
dispatch(doSetSync(data.syncHash, walletHash, walletData));
|
||||
}
|
||||
|
||||
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||
handleCallback(null, data.changed);
|
||||
})
|
||||
.catch(syncAttemptError => {
|
||||
if (data.unlockFailed) {
|
||||
dispatch({ type: ACTIONS.GET_SYNC_FAILED, data: { error: syncAttemptError } });
|
||||
|
||||
if (password !== '') {
|
||||
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
|
||||
}
|
||||
|
||||
handleCallback(syncAttemptError);
|
||||
} else if (data.hasSyncedWallet) {
|
||||
const error =
|
||||
(syncAttemptError && syncAttemptError.message) || 'Error getting synced wallet';
|
||||
dispatch({
|
||||
type: ACTIONS.GET_SYNC_FAILED,
|
||||
data: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
|
||||
// Temp solution until we have a bad password error code
|
||||
// Don't fail on blank passwords so we don't show a "password error" message
|
||||
// before users have ever entered a password
|
||||
if (password !== '') {
|
||||
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
|
||||
}
|
||||
|
||||
handleCallback(error);
|
||||
} else {
|
||||
// user doesn't have a synced wallet
|
||||
dispatch({
|
||||
type: ACTIONS.GET_SYNC_COMPLETED,
|
||||
data: { hasSyncedWallet: false, syncHash: null },
|
||||
});
|
||||
|
||||
// call sync_apply to get data to sync
|
||||
// first time sync. use any string for old hash
|
||||
if (syncAttemptError.message === NO_WALLET_ERROR) {
|
||||
Lbry.sync_apply({ password })
|
||||
.then(({ hash: walletHash, data: syncApplyData }) => {
|
||||
dispatch(doSetSync('', walletHash, syncApplyData, password));
|
||||
handleCallback();
|
||||
})
|
||||
.catch(syncApplyError => {
|
||||
handleCallback(syncApplyError);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doSyncApply(syncHash, syncData, password) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.SYNC_APPLY_STARTED,
|
||||
});
|
||||
|
||||
Lbry.sync_apply({ password, data: syncData })
|
||||
.then(({ hash: walletHash, data: walletData }) => {
|
||||
dispatch({
|
||||
type: ACTIONS.SYNC_APPLY_COMPLETED,
|
||||
});
|
||||
|
||||
if (walletHash !== syncHash) {
|
||||
// different local hash, need to synchronise
|
||||
dispatch(doSetSync(syncHash, walletHash, walletData));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch({
|
||||
type: ACTIONS.SYNC_APPLY_FAILED,
|
||||
data: {
|
||||
error:
|
||||
'Invalid password specified. Please enter the password for your previously synchronised wallet.',
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doCheckSync() {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.GET_SYNC_STARTED,
|
||||
});
|
||||
|
||||
Lbry.sync_hash().then(hash => {
|
||||
Lbryio.call('sync', 'get', { hash }, 'post')
|
||||
.then(response => {
|
||||
const data = {
|
||||
hasSyncedWallet: true,
|
||||
syncHash: response.hash,
|
||||
syncData: response.data,
|
||||
hashChanged: response.changed,
|
||||
};
|
||||
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
||||
})
|
||||
.catch(() => {
|
||||
// user doesn't have a synced wallet
|
||||
dispatch({
|
||||
type: ACTIONS.GET_SYNC_COMPLETED,
|
||||
data: { hasSyncedWallet: false, syncHash: null },
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doResetSync() {
|
||||
return dispatch =>
|
||||
new Promise(resolve => {
|
||||
dispatch({ type: ACTIONS.SYNC_RESET });
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
export function doSyncEncryptAndDecrypt(oldPassword, newPassword, encrypt) {
|
||||
return dispatch => {
|
||||
const data = {};
|
||||
return Lbry.sync_hash()
|
||||
.then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
|
||||
.then(syncGetResponse => {
|
||||
data.oldHash = syncGetResponse.hash;
|
||||
|
||||
return Lbry.sync_apply({ password: oldPassword, data: syncGetResponse.data });
|
||||
})
|
||||
.then(() => {
|
||||
if (encrypt) {
|
||||
dispatch(doWalletEncrypt(newPassword));
|
||||
} else {
|
||||
dispatch(doWalletDecrypt());
|
||||
}
|
||||
})
|
||||
.then(() => Lbry.sync_apply({ password: newPassword }))
|
||||
.then(syncApplyResponse => {
|
||||
if (syncApplyResponse.hash !== data.oldHash) {
|
||||
return dispatch(doSetSync(data.oldHash, syncApplyResponse.hash, syncApplyResponse.data));
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
import { ACTIONS, MODALS, Lbry, doNotify } from 'lbry-redux';
|
||||
import { doRewardList } from 'redux/actions/rewards';
|
||||
import Lbryio from 'lbryio';
|
||||
|
||||
export function doFetchInviteStatus() {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.USER_INVITE_STATUS_FETCH_STARTED,
|
||||
});
|
||||
|
||||
Lbryio.call('user', 'invite_status')
|
||||
.then(status => {
|
||||
dispatch({
|
||||
type: ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS,
|
||||
data: {
|
||||
invitesRemaining: status.invites_remaining ? status.invites_remaining : 0,
|
||||
invitees: status.invitees,
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch({
|
||||
type: ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE,
|
||||
data: { error },
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doInstallNew(appVersion, deviceId = null) {
|
||||
const payload = { app_version: appVersion, device_id: deviceId };
|
||||
Lbry.status().then(status => {
|
||||
payload.app_id = status.installation_id;
|
||||
payload.node_id = status.lbry_id;
|
||||
Lbry.version().then(version => {
|
||||
payload.daemon_version = version.lbrynet_version;
|
||||
payload.operating_system = version.os_system;
|
||||
payload.platform = version.platform;
|
||||
Lbryio.call('install', 'new', payload);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Call doInstallNew separately so we don't have to pass appVersion and deviceId params?
|
||||
export function doAuthenticate(appVersion, deviceId = null) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.AUTHENTICATION_STARTED,
|
||||
});
|
||||
Lbryio.authenticate()
|
||||
.then(user => {
|
||||
// analytics.setUser(user);
|
||||
dispatch({
|
||||
type: ACTIONS.AUTHENTICATION_SUCCESS,
|
||||
data: { user },
|
||||
});
|
||||
dispatch(doRewardList());
|
||||
dispatch(doFetchInviteStatus());
|
||||
doInstallNew(appVersion, deviceId);
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch(doNotify({ id: MODALS.AUTHENTICATION_FAILURE }));
|
||||
dispatch({
|
||||
type: ACTIONS.AUTHENTICATION_FAILURE,
|
||||
data: { error },
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doUserFetch() {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.USER_FETCH_STARTED,
|
||||
});
|
||||
Lbryio.getCurrentUser()
|
||||
.then(user => {
|
||||
// analytics.setUser(user);
|
||||
dispatch(doRewardList());
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.USER_FETCH_SUCCESS,
|
||||
data: { user },
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch({
|
||||
type: ACTIONS.USER_FETCH_FAILURE,
|
||||
data: { error },
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
12
src/redux/actions/web.js
Normal file
12
src/redux/actions/web.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
// @flow
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
export const doUpdateUploadProgress = (
|
||||
progress: string,
|
||||
params: { [key: string]: any },
|
||||
xhr: any
|
||||
) => (dispatch: Dispatch) =>
|
||||
dispatch({
|
||||
type: ACTIONS.UPDATE_UPLOAD_PROGRESS,
|
||||
data: { progress, params, xhr },
|
||||
});
|
|
@ -18,7 +18,7 @@ reducers[ACTIONS.GENERATE_AUTH_TOKEN_STARTED] = state =>
|
|||
|
||||
reducers[ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
authToken: action.authToken,
|
||||
authToken: action.data.authToken,
|
||||
authenticating: false,
|
||||
});
|
||||
|
||||
|
|
37
src/redux/reducers/blacklist.js
Normal file
37
src/redux/reducers/blacklist.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import { handleActions } from 'util/redux-utils';
|
||||
|
||||
const defaultState = {
|
||||
fetchingBlackListedOutpoints: false,
|
||||
fetchingBlackListedOutpointsSucceed: undefined,
|
||||
blackListedOutpoints: undefined,
|
||||
};
|
||||
|
||||
export const blacklistReducer = handleActions(
|
||||
{
|
||||
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_STARTED]: state => ({
|
||||
...state,
|
||||
fetchingBlackListedOutpoints: true,
|
||||
}),
|
||||
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_COMPLETED]: (state, action) => {
|
||||
const { outpoints, success } = action.data;
|
||||
return {
|
||||
...state,
|
||||
fetchingBlackListedOutpoints: false,
|
||||
fetchingBlackListedOutpointsSucceed: success,
|
||||
blackListedOutpoints: outpoints,
|
||||
};
|
||||
},
|
||||
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_FAILED]: (state, action) => {
|
||||
const { error, success } = action.data;
|
||||
|
||||
return {
|
||||
...state,
|
||||
fetchingBlackListedOutpoints: false,
|
||||
fetchingBlackListedOutpointsSucceed: success,
|
||||
fetchingBlackListedOutpointsError: error,
|
||||
};
|
||||
},
|
||||
},
|
||||
defaultState
|
||||
);
|
38
src/redux/reducers/cost_info.js
Normal file
38
src/redux/reducers/cost_info.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { handleActions } from 'util/redux-utils';
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
const defaultState = {
|
||||
fetching: {},
|
||||
byUri: {},
|
||||
};
|
||||
|
||||
export const costInfoReducer = handleActions(
|
||||
{
|
||||
[ACTIONS.FETCH_COST_INFO_STARTED]: (state, action) => {
|
||||
const { uri } = action.data;
|
||||
const newFetching = Object.assign({}, state.fetching);
|
||||
newFetching[uri] = true;
|
||||
|
||||
return {
|
||||
...state,
|
||||
fetching: newFetching,
|
||||
};
|
||||
},
|
||||
|
||||
[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 {
|
||||
...state,
|
||||
byUri: newByUri,
|
||||
fetching: newFetching,
|
||||
};
|
||||
},
|
||||
},
|
||||
defaultState
|
||||
);
|
34
src/redux/reducers/filtered.js
Normal file
34
src/redux/reducers/filtered.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import { handleActions } from 'util/redux-utils';
|
||||
|
||||
const defaultState = {
|
||||
loading: false,
|
||||
filteredOutpoints: undefined,
|
||||
};
|
||||
|
||||
export const filteredReducer = handleActions(
|
||||
{
|
||||
[ACTIONS.FETCH_FILTERED_CONTENT_STARTED]: state => ({
|
||||
...state,
|
||||
loading: true,
|
||||
}),
|
||||
[ACTIONS.FETCH_FILTERED_CONTENT_COMPLETED]: (state, action) => {
|
||||
const { outpoints } = action.data;
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
filteredOutpoints: outpoints,
|
||||
};
|
||||
},
|
||||
[ACTIONS.FETCH_FILTERED_CONTENT_FAILED]: (state, action) => {
|
||||
const { error } = action.data;
|
||||
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
fetchingFilteredOutpointsError: error,
|
||||
};
|
||||
},
|
||||
},
|
||||
defaultState
|
||||
);
|
48
src/redux/reducers/homepage.js
Normal file
48
src/redux/reducers/homepage.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { handleActions } from 'util/redux-utils';
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
const defaultState = {
|
||||
fetchingFeaturedContent: false,
|
||||
fetchingFeaturedContentFailed: false,
|
||||
featuredUris: undefined,
|
||||
fetchingTrendingContent: false,
|
||||
fetchingTrendingContentFailed: false,
|
||||
trendingUris: undefined,
|
||||
};
|
||||
|
||||
export const homepageReducer = handleActions(
|
||||
{
|
||||
[ACTIONS.FETCH_FEATURED_CONTENT_STARTED]: state => ({
|
||||
...state,
|
||||
fetchingFeaturedContent: true,
|
||||
}),
|
||||
|
||||
[ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED]: (state, action) => {
|
||||
const { uris, success } = action.data;
|
||||
|
||||
return {
|
||||
...state,
|
||||
fetchingFeaturedContent: false,
|
||||
fetchingFeaturedContentFailed: !success,
|
||||
featuredUris: uris,
|
||||
};
|
||||
},
|
||||
|
||||
[ACTIONS.FETCH_TRENDING_CONTENT_STARTED]: state => ({
|
||||
...state,
|
||||
fetchingTrendingContent: true,
|
||||
}),
|
||||
|
||||
[ACTIONS.FETCH_TRENDING_CONTENT_COMPLETED]: (state, action) => {
|
||||
const { uris, success } = action.data;
|
||||
|
||||
return {
|
||||
...state,
|
||||
fetchingTrendingContent: false,
|
||||
fetchingTrendingContentFailed: !success,
|
||||
trendingUris: uris,
|
||||
};
|
||||
},
|
||||
},
|
||||
defaultState
|
||||
);
|
|
@ -1,98 +0,0 @@
|
|||
import { ACTIONS } from 'lbry-redux';
|
||||
|
||||
const reducers = {};
|
||||
const defaultState = {
|
||||
fetching: false,
|
||||
claimedRewardsById: {}, // id => reward
|
||||
unclaimedRewards: [],
|
||||
claimPendingByType: {},
|
||||
claimErrorsByType: {},
|
||||
};
|
||||
|
||||
reducers[ACTIONS.FETCH_REWARDS_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
fetching: true,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = (state, action) => {
|
||||
const { userRewards } = action.data;
|
||||
|
||||
const unclaimedRewards = [];
|
||||
const claimedRewards = {};
|
||||
userRewards.forEach(reward => {
|
||||
if (reward.transaction_id) {
|
||||
claimedRewards[reward.id] = reward;
|
||||
} else {
|
||||
unclaimedRewards.push(reward);
|
||||
}
|
||||
});
|
||||
|
||||
return Object.assign({}, state, {
|
||||
claimedRewardsById: claimedRewards,
|
||||
unclaimedRewards,
|
||||
fetching: false,
|
||||
});
|
||||
};
|
||||
|
||||
function setClaimRewardState(state, reward, isClaiming, errorMessage = '') {
|
||||
const newClaimPendingByType = Object.assign({}, state.claimPendingByType);
|
||||
const newClaimErrorsByType = Object.assign({}, state.claimErrorsByType);
|
||||
if (isClaiming) {
|
||||
newClaimPendingByType[reward.reward_type] = isClaiming;
|
||||
} else {
|
||||
delete newClaimPendingByType[reward.reward_type];
|
||||
}
|
||||
if (errorMessage) {
|
||||
newClaimErrorsByType[reward.reward_type] = errorMessage;
|
||||
} else {
|
||||
delete newClaimErrorsByType[reward.reward_type];
|
||||
}
|
||||
|
||||
return Object.assign({}, state, {
|
||||
claimPendingByType: newClaimPendingByType,
|
||||
claimErrorsByType: newClaimErrorsByType,
|
||||
});
|
||||
}
|
||||
|
||||
reducers[ACTIONS.CLAIM_REWARD_STARTED] = (state, action) => {
|
||||
const { reward } = action.data;
|
||||
|
||||
return setClaimRewardState(state, reward, true, '');
|
||||
};
|
||||
|
||||
reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = (state, action) => {
|
||||
const { reward } = action.data;
|
||||
const { unclaimedRewards } = state;
|
||||
|
||||
const index = unclaimedRewards.findIndex(ur => ur.reward_type === reward.reward_type);
|
||||
unclaimedRewards.splice(index, 1);
|
||||
|
||||
const { claimedRewardsById } = state;
|
||||
claimedRewardsById[reward.id] = reward;
|
||||
|
||||
const newState = {
|
||||
...state,
|
||||
unclaimedRewards: [...unclaimedRewards],
|
||||
claimedRewardsById: { ...claimedRewardsById },
|
||||
};
|
||||
|
||||
return setClaimRewardState(newState, reward, false, '');
|
||||
};
|
||||
|
||||
reducers[ACTIONS.CLAIM_REWARD_FAILURE] = (state, action) => {
|
||||
const { reward, error } = action.data;
|
||||
|
||||
return setClaimRewardState(state, reward, false, error ? error.message : '');
|
||||
};
|
||||
|
||||
reducers[ACTIONS.CLAIM_REWARD_CLEAR_ERROR] = (state, action) => {
|
||||
const { reward } = action.data;
|
||||
|
||||
return setClaimRewardState(state, reward, state.claimPendingByType[reward.reward_type], '');
|
||||
};
|
||||
|
||||
export function rewardsReducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
||||
return state;
|
||||
}
|
55
src/redux/reducers/stats.js
Normal file
55
src/redux/reducers/stats.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
import { handleActions } from 'util/redux-utils';
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
const defaultState = {
|
||||
fetchingViewCount: false,
|
||||
viewCountError: undefined,
|
||||
viewCountById: {},
|
||||
fetchingSubCount: false,
|
||||
subCountError: undefined,
|
||||
subCountById: {},
|
||||
};
|
||||
|
||||
export const statsReducer = handleActions(
|
||||
{
|
||||
[ACTIONS.FETCH_VIEW_COUNT_STARTED]: state => ({ ...state, fetchingViewCount: true }),
|
||||
[ACTIONS.FETCH_VIEW_COUNT_FAILED]: (state, action) => ({
|
||||
...state,
|
||||
viewCountError: action.data,
|
||||
}),
|
||||
[ACTIONS.FETCH_VIEW_COUNT_COMPLETED]: (state, action) => {
|
||||
const { claimIdCsv, viewCounts } = action.data;
|
||||
|
||||
const viewCountById = Object.assign({}, state.viewCountById);
|
||||
const claimIds = claimIdCsv.split(',');
|
||||
|
||||
if (claimIds.length === viewCounts.length) {
|
||||
claimIds.forEach((claimId, index) => {
|
||||
viewCountById[claimId] = viewCounts[index];
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
fetchingViewCount: false,
|
||||
viewCountById,
|
||||
};
|
||||
},
|
||||
[ACTIONS.FETCH_SUB_COUNT_STARTED]: state => ({ ...state, fetchingSubCount: true }),
|
||||
[ACTIONS.FETCH_SUB_COUNT_FAILED]: (state, action) => ({
|
||||
...state,
|
||||
subCountError: action.data,
|
||||
}),
|
||||
[ACTIONS.FETCH_SUB_COUNT_COMPLETED]: (state, action) => {
|
||||
const { claimId, subCount } = action.data;
|
||||
|
||||
const subCountById = { ...state.subCountById, [claimId]: subCount };
|
||||
return {
|
||||
...state,
|
||||
fetchingSubCount: false,
|
||||
subCountById,
|
||||
};
|
||||
},
|
||||
},
|
||||
defaultState
|
||||
);
|
89
src/redux/reducers/sync.js
Normal file
89
src/redux/reducers/sync.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
const reducers = {};
|
||||
const defaultState = {
|
||||
hasSyncedWallet: false,
|
||||
syncHash: null,
|
||||
syncData: null,
|
||||
setSyncErrorMessage: null,
|
||||
getSyncErrorMessage: null,
|
||||
syncApplyErrorMessage: '',
|
||||
syncApplyIsPending: false,
|
||||
syncApplyPasswordError: false,
|
||||
getSyncIsPending: false,
|
||||
setSyncIsPending: false,
|
||||
hashChanged: false,
|
||||
};
|
||||
|
||||
reducers[ACTIONS.GET_SYNC_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
getSyncIsPending: true,
|
||||
getSyncErrorMessage: null,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.GET_SYNC_COMPLETED] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
syncHash: action.data.syncHash,
|
||||
syncData: action.data.syncData,
|
||||
hasSyncedWallet: action.data.hasSyncedWallet,
|
||||
getSyncIsPending: false,
|
||||
hashChanged: action.data.hashChanged,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.GET_SYNC_FAILED] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
getSyncIsPending: false,
|
||||
getSyncErrorMessage: action.data.error,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SET_SYNC_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
setSyncIsPending: true,
|
||||
setSyncErrorMessage: null,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SET_SYNC_FAILED] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
setSyncIsPending: false,
|
||||
setSyncErrorMessage: action.data.error,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SET_SYNC_COMPLETED] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
setSyncIsPending: false,
|
||||
setSyncErrorMessage: null,
|
||||
hasSyncedWallet: true, // sync was successful, so the user has a synced wallet at this point
|
||||
syncHash: action.data.syncHash,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SYNC_APPLY_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
syncApplyPasswordError: false,
|
||||
syncApplyIsPending: true,
|
||||
syncApplyErrorMessage: '',
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SYNC_APPLY_COMPLETED] = state =>
|
||||
Object.assign({}, state, {
|
||||
syncApplyIsPending: false,
|
||||
syncApplyErrorMessage: '',
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SYNC_APPLY_FAILED] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
syncApplyIsPending: false,
|
||||
syncApplyErrorMessage: action.data.error,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SYNC_APPLY_BAD_PASSWORD] = state =>
|
||||
Object.assign({}, state, {
|
||||
syncApplyPasswordError: true,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SYNC_RESET] = () => defaultState;
|
||||
|
||||
export function syncReducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
||||
return state;
|
||||
}
|
|
@ -1,222 +0,0 @@
|
|||
import { ACTIONS } from 'lbry-redux';
|
||||
|
||||
const reducers = {};
|
||||
|
||||
const defaultState = {
|
||||
authenticationIsPending: false,
|
||||
userIsPending: false,
|
||||
emailNewIsPending: false,
|
||||
emailNewErrorMessage: '',
|
||||
emailToVerify: '',
|
||||
inviteNewErrorMessage: '',
|
||||
inviteNewIsPending: false,
|
||||
inviteStatusIsPending: false,
|
||||
invitesRemaining: undefined,
|
||||
invitees: undefined,
|
||||
user: undefined,
|
||||
};
|
||||
|
||||
reducers[ACTIONS.AUTHENTICATION_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
authenticationIsPending: true,
|
||||
userIsPending: true,
|
||||
user: defaultState.user,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.AUTHENTICATION_SUCCESS] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
authenticationIsPending: false,
|
||||
userIsPending: false,
|
||||
user: action.data.user,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.AUTHENTICATION_FAILURE] = state =>
|
||||
Object.assign({}, state, {
|
||||
authenticationIsPending: false,
|
||||
userIsPending: false,
|
||||
user: null,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_FETCH_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
userIsPending: true,
|
||||
user: defaultState.user,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_FETCH_SUCCESS] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
userIsPending: false,
|
||||
user: action.data.user,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_FETCH_FAILURE] = state =>
|
||||
Object.assign({}, state, {
|
||||
userIsPending: true,
|
||||
user: null,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_NEW_STARTED] = (state, action) => {
|
||||
const user = Object.assign({}, state.user);
|
||||
user.country_code = action.data.country_code;
|
||||
return Object.assign({}, state, {
|
||||
phoneNewIsPending: true,
|
||||
phoneNewErrorMessage: '',
|
||||
user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_NEW_SUCCESS] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
phoneToVerify: action.data.phone,
|
||||
phoneNewIsPending: false,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_RESET] = state =>
|
||||
Object.assign({}, state, {
|
||||
phoneToVerify: null,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_NEW_FAILURE] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
phoneNewIsPending: false,
|
||||
phoneNewErrorMessage: action.data.error,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_VERIFY_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
phoneVerifyIsPending: true,
|
||||
phoneVerifyErrorMessage: '',
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_VERIFY_SUCCESS] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
phoneToVerify: '',
|
||||
phoneVerifyIsPending: false,
|
||||
user: action.data.user,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_PHONE_VERIFY_FAILURE] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
phoneVerifyIsPending: false,
|
||||
phoneVerifyErrorMessage: action.data.error,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
emailNewIsPending: true,
|
||||
emailNewErrorMessage: '',
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_EMAIL_NEW_SUCCESS] = (state, action) => {
|
||||
const user = Object.assign({}, state.user);
|
||||
user.primary_email = action.data.email;
|
||||
return Object.assign({}, state, {
|
||||
emailToVerify: action.data.email,
|
||||
emailNewIsPending: false,
|
||||
user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.USER_EMAIL_NEW_EXISTS] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
emailToVerify: action.data.email,
|
||||
emailNewIsPending: false,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_EMAIL_NEW_FAILURE] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
emailNewIsPending: false,
|
||||
emailNewErrorMessage: action.data.error,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_EMAIL_VERIFY_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
emailVerifyIsPending: true,
|
||||
emailVerifyErrorMessage: '',
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_EMAIL_VERIFY_SUCCESS] = (state, action) => {
|
||||
const user = Object.assign({}, state.user);
|
||||
user.primary_email = action.data.email;
|
||||
return Object.assign({}, state, {
|
||||
emailToVerify: '',
|
||||
emailVerifyIsPending: false,
|
||||
user,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.USER_EMAIL_VERIFY_FAILURE] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
emailVerifyIsPending: false,
|
||||
emailVerifyErrorMessage: action.data.error,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_IDENTITY_VERIFY_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
identityVerifyIsPending: true,
|
||||
identityVerifyErrorMessage: '',
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_IDENTITY_VERIFY_SUCCESS] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
identityVerifyIsPending: false,
|
||||
identityVerifyErrorMessage: '',
|
||||
user: action.data.user,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_IDENTITY_VERIFY_FAILURE] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
identityVerifyIsPending: false,
|
||||
identityVerifyErrorMessage: action.data.error,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS] = (state, action) => {
|
||||
const { token } = action.data;
|
||||
|
||||
return Object.assign({}, state, {
|
||||
accessToken: token,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
inviteStatusIsPending: true,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
inviteStatusIsPending: false,
|
||||
invitesRemaining: action.data.invitesRemaining,
|
||||
invitees: action.data.invitees,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_INVITE_NEW_STARTED] = state =>
|
||||
Object.assign({}, state, {
|
||||
inviteNewIsPending: true,
|
||||
inviteNewErrorMessage: '',
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_INVITE_NEW_SUCCESS] = state =>
|
||||
Object.assign({}, state, {
|
||||
inviteNewIsPending: false,
|
||||
inviteNewErrorMessage: '',
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_INVITE_NEW_FAILURE] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
inviteNewIsPending: false,
|
||||
inviteNewErrorMessage: action.data.error.message,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE] = state =>
|
||||
Object.assign({}, state, {
|
||||
inviteStatusIsPending: false,
|
||||
invitesRemaining: null,
|
||||
invitees: null,
|
||||
});
|
||||
|
||||
export function userReducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
||||
return state;
|
||||
}
|
62
src/redux/reducers/web.js
Normal file
62
src/redux/reducers/web.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
// @flow
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
|
||||
/*
|
||||
test mock:
|
||||
currentUploads: {
|
||||
'test#upload': {
|
||||
progress: 50,
|
||||
params: {
|
||||
name: 'steve',
|
||||
thumbnail_url: 'https://dev2.spee.ch/4/KMNtoSZ009fawGz59VG8PrID.jpeg',
|
||||
},
|
||||
},
|
||||
},
|
||||
*/
|
||||
|
||||
export type Params = {
|
||||
channel?: string,
|
||||
name: string,
|
||||
thumbnail_url: ?string,
|
||||
title: ?string,
|
||||
};
|
||||
|
||||
export type UploadItem = {
|
||||
progess: string,
|
||||
params: Params,
|
||||
xhr?: any,
|
||||
};
|
||||
|
||||
export type TvState = {
|
||||
currentUploads: { [key: string]: UploadItem },
|
||||
};
|
||||
|
||||
const reducers = {};
|
||||
|
||||
const defaultState: TvState = {
|
||||
currentUploads: {},
|
||||
};
|
||||
|
||||
reducers[ACTIONS.UPDATE_UPLOAD_PROGRESS] = (state: TvState, action) => {
|
||||
const { progress, params, xhr } = action.data;
|
||||
const key = params.channel ? `${params.name}#${params.channel}` : `${params.name}#anonymous`;
|
||||
let currentUploads;
|
||||
if (!progress) {
|
||||
currentUploads = Object.assign({}, state.currentUploads);
|
||||
Object.keys(currentUploads).forEach(k => {
|
||||
if (k === key) {
|
||||
delete currentUploads[key];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
currentUploads = Object.assign({}, state.currentUploads);
|
||||
currentUploads[key] = { progress, params, xhr };
|
||||
}
|
||||
return { ...state, currentUploads };
|
||||
};
|
||||
|
||||
export function webReducer(state = defaultState, action) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
||||
return state;
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
const selectState = state => state.authToken || {};
|
||||
const selectState = state => state.auth || {};
|
||||
|
||||
export const selectAuthToken = createSelector(selectState, state => state.authToken);
|
||||
|
||||
export const selectIsAuthenticating = createSelector(selectState, state => state.authenticating);
|
||||
|
|
20
src/redux/selectors/blacklist.js
Normal file
20
src/redux/selectors/blacklist.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
export const selectState = state => state.blacklist || {};
|
||||
|
||||
export const selectBlackListedOutpoints = createSelector(
|
||||
selectState,
|
||||
state => state.blackListedOutpoints
|
||||
);
|
||||
|
||||
export const selectBlacklistedOutpointMap = createSelector(
|
||||
selectBlackListedOutpoints,
|
||||
outpoints =>
|
||||
outpoints
|
||||
? outpoints.reduce((acc, val) => {
|
||||
const outpoint = `${val.txid}:${val.nout}`;
|
||||
acc[outpoint] = 1;
|
||||
return acc;
|
||||
}, {})
|
||||
: {}
|
||||
);
|
13
src/redux/selectors/cost_info.js
Normal file
13
src/redux/selectors/cost_info.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
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 selectFetchingCostInfo = createSelector(selectState, state => state.fetching || {});
|
||||
|
||||
export const makeSelectFetchingCostInfoForUri = uri =>
|
||||
createSelector(selectFetchingCostInfo, fetchingByUri => fetchingByUri && fetchingByUri[uri]);
|
20
src/redux/selectors/filtered.js
Normal file
20
src/redux/selectors/filtered.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
export const selectState = state => state.filtered || {};
|
||||
|
||||
export const selectFilteredOutpoints = createSelector(
|
||||
selectState,
|
||||
state => state.filteredOutpoints
|
||||
);
|
||||
|
||||
export const selectFilteredOutpointMap = createSelector(
|
||||
selectFilteredOutpoints,
|
||||
outpoints =>
|
||||
outpoints
|
||||
? outpoints.reduce((acc, val) => {
|
||||
const outpoint = `${val.txid}:${val.nout}`;
|
||||
acc[outpoint] = 1;
|
||||
return acc;
|
||||
}, {})
|
||||
: {}
|
||||
);
|
17
src/redux/selectors/homepage.js
Normal file
17
src/redux/selectors/homepage.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
const selectState = state => state.homepage || {};
|
||||
|
||||
export const selectFeaturedUris = createSelector(selectState, state => state.featuredUris);
|
||||
|
||||
export const selectFetchingFeaturedUris = createSelector(
|
||||
selectState,
|
||||
state => state.fetchingFeaturedContent
|
||||
);
|
||||
|
||||
export const selectTrendingUris = createSelector(selectState, state => state.trendingUris);
|
||||
|
||||
export const selectFetchingTrendingUris = createSelector(
|
||||
selectState,
|
||||
state => state.fetchingTrendingContent
|
||||
);
|
|
@ -1,64 +0,0 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
const selectState = state => state.rewards || {};
|
||||
|
||||
export const selectUnclaimedRewardsByType = createSelector(
|
||||
selectState,
|
||||
state => state.unclaimedRewardsByType
|
||||
);
|
||||
|
||||
export const selectClaimedRewardsById = createSelector(
|
||||
selectState,
|
||||
state => state.claimedRewardsById
|
||||
);
|
||||
|
||||
export const selectClaimedRewards = createSelector(
|
||||
selectClaimedRewardsById,
|
||||
byId => Object.values(byId) || []
|
||||
);
|
||||
|
||||
export const selectClaimedRewardsByTransactionId = createSelector(selectClaimedRewards, rewards =>
|
||||
rewards.reduce((mapParam, reward) => {
|
||||
const map = mapParam;
|
||||
map[reward.transaction_id] = reward;
|
||||
return map;
|
||||
}, {})
|
||||
);
|
||||
|
||||
export const selectUnclaimedRewards = createSelector(selectState, state => state.unclaimedRewards);
|
||||
|
||||
export const selectFetchingRewards = createSelector(selectState, state => !!state.fetching);
|
||||
|
||||
export const selectUnclaimedRewardValue = createSelector(selectUnclaimedRewards, rewards =>
|
||||
rewards.reduce((sum, reward) => sum + reward.reward_amount, 0)
|
||||
);
|
||||
|
||||
export const selectClaimsPendingByType = createSelector(
|
||||
selectState,
|
||||
state => state.claimPendingByType
|
||||
);
|
||||
|
||||
const selectIsClaimRewardPending = (state, props) =>
|
||||
selectClaimsPendingByType(state, props)[props.reward_type];
|
||||
|
||||
export const makeSelectIsRewardClaimPending = () =>
|
||||
createSelector(selectIsClaimRewardPending, isClaiming => isClaiming);
|
||||
|
||||
export const selectClaimErrorsByType = createSelector(
|
||||
selectState,
|
||||
state => state.claimErrorsByType
|
||||
);
|
||||
|
||||
const selectClaimRewardError = (state, props) =>
|
||||
selectClaimErrorsByType(state, props)[props.reward_type];
|
||||
|
||||
export const makeSelectClaimRewardError = () =>
|
||||
createSelector(selectClaimRewardError, errorMessage => errorMessage);
|
||||
|
||||
const selectRewardByType = (state, rewardType) =>
|
||||
selectUnclaimedRewards(state).find(reward => reward.reward_type === rewardType);
|
||||
|
||||
export const makeSelectRewardByType = () => createSelector(selectRewardByType, reward => reward);
|
||||
|
||||
export const makeSelectRewardAmountByType = () =>
|
||||
createSelector(selectRewardByType, reward => (reward ? reward.reward_amount : 0));
|
20
src/redux/selectors/stats.js
Normal file
20
src/redux/selectors/stats.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import { makeSelectClaimForUri } from 'lbry-redux';
|
||||
|
||||
const selectState = state => state.stats || {};
|
||||
export const selectViewCount = createSelector(selectState, state => state.viewCountById);
|
||||
export const selectSubCount = createSelector(selectState, state => state.subCountById);
|
||||
|
||||
export const makeSelectViewCountForUri = uri =>
|
||||
createSelector(
|
||||
makeSelectClaimForUri(uri),
|
||||
selectViewCount,
|
||||
(claim, viewCountById) => (claim ? viewCountById[claim.claim_id] || 0 : 0)
|
||||
);
|
||||
|
||||
export const makeSelectSubCountForUri = uri =>
|
||||
createSelector(
|
||||
makeSelectClaimForUri(uri),
|
||||
selectSubCount,
|
||||
(claim, subCountById) => (claim ? subCountById[claim.claim_id] || 0 : 0)
|
||||
);
|
40
src/redux/selectors/sync.js
Normal file
40
src/redux/selectors/sync.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
const selectState = state => state.sync || {};
|
||||
|
||||
export const selectHasSyncedWallet = createSelector(selectState, state => state.hasSyncedWallet);
|
||||
|
||||
export const selectSyncHash = createSelector(selectState, state => state.syncHash);
|
||||
|
||||
export const selectSyncData = createSelector(selectState, state => state.syncData);
|
||||
|
||||
export const selectSetSyncErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.setSyncErrorMessage
|
||||
);
|
||||
|
||||
export const selectGetSyncErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.getSyncErrorMessage
|
||||
);
|
||||
|
||||
export const selectGetSyncIsPending = createSelector(selectState, state => state.getSyncIsPending);
|
||||
|
||||
export const selectSetSyncIsPending = createSelector(selectState, state => state.setSyncIsPending);
|
||||
|
||||
export const selectHashChanged = createSelector(selectState, state => state.hashChanged);
|
||||
|
||||
export const selectSyncApplyIsPending = createSelector(
|
||||
selectState,
|
||||
state => state.syncApplyIsPending
|
||||
);
|
||||
|
||||
export const selectSyncApplyErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.syncApplyErrorMessage
|
||||
);
|
||||
|
||||
export const selectSyncApplyPasswordError = createSelector(
|
||||
selectState,
|
||||
state => state.syncApplyPasswordError
|
||||
);
|
|
@ -1,118 +0,0 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
export const selectState = state => state.user || {};
|
||||
|
||||
export const selectAuthenticationIsPending = createSelector(
|
||||
selectState,
|
||||
state => state.authenticationIsPending
|
||||
);
|
||||
|
||||
export const selectUserIsPending = createSelector(selectState, state => state.userIsPending);
|
||||
|
||||
export const selectUser = createSelector(selectState, state => state.user);
|
||||
|
||||
export const selectUserEmail = createSelector(
|
||||
selectUser,
|
||||
user => (user ? user.primary_email : null)
|
||||
);
|
||||
|
||||
export const selectUserPhone = createSelector(
|
||||
selectUser,
|
||||
user => (user ? user.phone_number : null)
|
||||
);
|
||||
|
||||
export const selectUserCountryCode = createSelector(
|
||||
selectUser,
|
||||
user => (user ? user.country_code : null)
|
||||
);
|
||||
|
||||
export const selectEmailToVerify = createSelector(
|
||||
selectState,
|
||||
selectUserEmail,
|
||||
(state, userEmail) => state.emailToVerify || userEmail
|
||||
);
|
||||
|
||||
export const selectPhoneToVerify = createSelector(
|
||||
selectState,
|
||||
selectUserPhone,
|
||||
(state, userPhone) => state.phoneToVerify || userPhone
|
||||
);
|
||||
|
||||
export const selectUserIsRewardApproved = createSelector(
|
||||
selectUser,
|
||||
user => user && user.is_reward_approved
|
||||
);
|
||||
|
||||
export const selectEmailNewIsPending = createSelector(
|
||||
selectState,
|
||||
state => state.emailNewIsPending
|
||||
);
|
||||
|
||||
export const selectEmailNewErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.emailNewErrorMessage
|
||||
);
|
||||
|
||||
export const selectPhoneNewErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.phoneNewErrorMessage
|
||||
);
|
||||
|
||||
export const selectEmailVerifyIsPending = createSelector(
|
||||
selectState,
|
||||
state => state.emailVerifyIsPending
|
||||
);
|
||||
|
||||
export const selectEmailVerifyErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.emailVerifyErrorMessage
|
||||
);
|
||||
|
||||
export const selectPhoneVerifyErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.phoneVerifyErrorMessage
|
||||
);
|
||||
|
||||
export const selectIdentityVerifyIsPending = createSelector(
|
||||
selectState,
|
||||
state => state.identityVerifyIsPending
|
||||
);
|
||||
|
||||
export const selectIdentityVerifyErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.identityVerifyErrorMessage
|
||||
);
|
||||
|
||||
export const selectUserIsVerificationCandidate = createSelector(
|
||||
selectUser,
|
||||
user => user && (!user.has_verified_email || !user.is_identity_verified)
|
||||
);
|
||||
|
||||
export const selectAccessToken = createSelector(selectState, state => state.accessToken);
|
||||
|
||||
export const selectUserInviteStatusIsPending = createSelector(
|
||||
selectState,
|
||||
state => state.inviteStatusIsPending
|
||||
);
|
||||
|
||||
export const selectUserInvitesRemaining = createSelector(
|
||||
selectState,
|
||||
state => state.invitesRemaining
|
||||
);
|
||||
|
||||
export const selectUserInvitees = createSelector(selectState, state => state.invitees);
|
||||
|
||||
export const selectUserInviteStatusFailed = createSelector(
|
||||
selectUserInvitesRemaining,
|
||||
() => selectUserInvitesRemaining === null
|
||||
);
|
||||
|
||||
export const selectUserInviteNewIsPending = createSelector(
|
||||
selectState,
|
||||
state => state.inviteNewIsPending
|
||||
);
|
||||
|
||||
export const selectUserInviteNewErrorMessage = createSelector(
|
||||
selectState,
|
||||
state => state.inviteNewErrorMessage
|
||||
);
|
10
src/redux/selectors/web.js
Normal file
10
src/redux/selectors/web.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
const selectState = state => state.web || {};
|
||||
|
||||
export const selectCurrentUploads = createSelector(selectState, state => state.currentUploads);
|
||||
|
||||
export const selectUploadCount = createSelector(
|
||||
selectCurrentUploads,
|
||||
currentUploads => currentUploads && Object.keys(currentUploads).length
|
||||
);
|
112
src/rewards.js
112
src/rewards.js
|
@ -1,112 +0,0 @@
|
|||
import { Lbry, doNotify } from 'lbry-redux';
|
||||
import Lbryio from 'lbryio';
|
||||
|
||||
const rewards = {};
|
||||
|
||||
rewards.TYPE_NEW_DEVELOPER = 'new_developer';
|
||||
rewards.TYPE_NEW_USER = 'new_user';
|
||||
rewards.TYPE_CONFIRM_EMAIL = 'verified_email';
|
||||
rewards.TYPE_FIRST_CHANNEL = 'new_channel';
|
||||
rewards.TYPE_FIRST_STREAM = 'first_stream';
|
||||
rewards.TYPE_MANY_DOWNLOADS = 'many_downloads';
|
||||
rewards.TYPE_FIRST_PUBLISH = 'first_publish';
|
||||
rewards.TYPE_FEATURED_DOWNLOAD = 'featured_download';
|
||||
rewards.TYPE_REFERRAL = 'referral';
|
||||
rewards.YOUTUBE_CREATOR = 'youtube_creator';
|
||||
|
||||
rewards.claimReward = type => {
|
||||
function requestReward(resolve, reject, params) {
|
||||
if (!Lbryio.enabled) {
|
||||
reject(new Error(__('Rewards are not enabled.')));
|
||||
return;
|
||||
}
|
||||
Lbryio.call('reward', 'new', params, 'post').then(reward => {
|
||||
const message =
|
||||
reward.reward_notification || `You have claimed a ${reward.reward_amount} LBC reward.`;
|
||||
|
||||
// Display global notice
|
||||
const action = doNotify({
|
||||
message,
|
||||
linkText: __('Show All'),
|
||||
linkTarget: '/rewards',
|
||||
isError: false,
|
||||
displayType: ['snackbar'],
|
||||
});
|
||||
window.store.dispatch(action);
|
||||
|
||||
// Add more events here to display other places
|
||||
|
||||
resolve(reward);
|
||||
}, reject);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
Lbry.wallet_unused_address().then(address => {
|
||||
const params = {
|
||||
reward_type: type,
|
||||
wallet_address: address,
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case rewards.TYPE_FIRST_CHANNEL:
|
||||
Lbry.claim_list_mine()
|
||||
.then(claims => {
|
||||
const claim = claims
|
||||
.reverse()
|
||||
.find(
|
||||
foundClaim =>
|
||||
foundClaim.name.length &&
|
||||
foundClaim.name[0] === '@' &&
|
||||
foundClaim.txid.length &&
|
||||
foundClaim.category === 'claim'
|
||||
);
|
||||
if (claim) {
|
||||
params.transaction_id = claim.txid;
|
||||
requestReward(resolve, reject, params);
|
||||
} else {
|
||||
reject(new Error(__('Please create a channel identity first.')));
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
break;
|
||||
|
||||
case rewards.TYPE_FIRST_PUBLISH:
|
||||
Lbry.claim_list_mine()
|
||||
.then(claims => {
|
||||
const claim = claims
|
||||
.reverse()
|
||||
.find(
|
||||
foundClaim =>
|
||||
foundClaim.name.length &&
|
||||
foundClaim.name[0] !== '@' &&
|
||||
foundClaim.txid.length &&
|
||||
foundClaim.category === 'claim'
|
||||
);
|
||||
if (claim) {
|
||||
params.transaction_id = claim.txid;
|
||||
requestReward(resolve, reject, params);
|
||||
} else {
|
||||
reject(
|
||||
claims.length
|
||||
? new Error(
|
||||
__(
|
||||
'Please publish something and wait for confirmation by the network to claim this reward.'
|
||||
)
|
||||
)
|
||||
: new Error(__('Please publish something to claim this reward.'))
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
break;
|
||||
|
||||
case rewards.TYPE_FIRST_STREAM:
|
||||
case rewards.TYPE_NEW_USER:
|
||||
default:
|
||||
requestReward(resolve, reject, params);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default rewards;
|
17
src/util/redux-utils.js
Normal file
17
src/util/redux-utils.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
// 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
|
||||
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;
|
||||
};
|
10
src/util/swap-json.js
Normal file
10
src/util/swap-json.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
export function swapKeyAndValue(dict) {
|
||||
const ret = {};
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const key in dict) {
|
||||
if (dict.hasOwnProperty(key)) {
|
||||
ret[dict[key]] = key;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
78
src/util/transifex-upload.js
Normal file
78
src/util/transifex-upload.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
const apiBaseUrl = 'https://www.transifex.com/api/2/project';
|
||||
const resource = 'app-strings';
|
||||
|
||||
export function doTransifexUpload(contents, project, token, success, fail) {
|
||||
const url = `${apiBaseUrl}/${project}/resources/`;
|
||||
const updateUrl = `${apiBaseUrl}/${project}/resource/${resource}/content/`;
|
||||
const headers = {
|
||||
Authorization: `Basic ${Buffer.from(`api:${token}`).toString('base64')}`,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const req = {
|
||||
accept_translations: true,
|
||||
i18n_type: 'KEYVALUEJSON',
|
||||
name: resource,
|
||||
slug: resource,
|
||||
content: contents,
|
||||
};
|
||||
|
||||
function handleResponse(text) {
|
||||
let json;
|
||||
try {
|
||||
// transifex api returns Python dicts for some reason.
|
||||
// Any way to get the api to return valid JSON?
|
||||
json = JSON.parse(text);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (success) {
|
||||
success(json || text);
|
||||
}
|
||||
}
|
||||
|
||||
function handleError(err) {
|
||||
if (fail) {
|
||||
fail(err.message ? err.message : 'Could not upload strings resource to Transifex');
|
||||
}
|
||||
}
|
||||
|
||||
// check if the resource exists
|
||||
fetch(updateUrl, { headers })
|
||||
.then(response => response.json())
|
||||
.then(() => {
|
||||
// perform an update
|
||||
fetch(updateUrl, {
|
||||
method: 'PUT',
|
||||
headers,
|
||||
body: JSON.stringify({ content: contents }),
|
||||
})
|
||||
.then(response => {
|
||||
if (response.status !== 200 && response.status !== 201) {
|
||||
throw new Error('failed to update transifex');
|
||||
}
|
||||
|
||||
return response.text();
|
||||
})
|
||||
.then(handleResponse)
|
||||
.catch(handleError);
|
||||
})
|
||||
.catch(() => {
|
||||
// resource doesn't exist, create a fresh resource
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(req),
|
||||
})
|
||||
.then(response => {
|
||||
if (response.status !== 200 && response.status !== 201) {
|
||||
throw new Error('failed to upload to transifex');
|
||||
}
|
||||
|
||||
return response.text();
|
||||
})
|
||||
.then(handleResponse)
|
||||
.catch(handleError);
|
||||
});
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
/* eslint-disable import/no-commonjs */
|
||||
const path = require('path');
|
||||
const FlowBabelWebpackPlugin = require('flow-babel-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: 'none',
|
||||
|
@ -8,7 +7,7 @@ module.exports = {
|
|||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
|
@ -21,6 +20,9 @@ module.exports = {
|
|||
},
|
||||
resolve: {
|
||||
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
|
||||
alias: {
|
||||
'flow-typed': path.resolve(__dirname, './flow-typed'),
|
||||
},
|
||||
},
|
||||
plugins: [new FlowBabelWebpackPlugin()],
|
||||
externals: 'lbry-redux',
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue