diff --git a/.flowconfig b/.flowconfig index 677258c1d65..0d771cc940b 100644 --- a/.flowconfig +++ b/.flowconfig @@ -24,6 +24,7 @@ module.name_mapper='^modal\(.*\)$' -> '/ui/modal\1' module.name_mapper='^app\(.*\)$' -> '/ui/app\1' module.name_mapper='^native\(.*\)$' -> '/ui/native\1' module.name_mapper='^analytics\(.*\)$' -> '/ui/analytics\1' +module.name_mapper='^rewards\(.*\)$' -> '/ui/rewards\1' module.name_mapper='^i18n\(.*\)$' -> '/ui/i18n\1' module.name_mapper='^effects\(.*\)$' -> '/ui/effects\1' module.name_mapper='^config\(.*\)$' -> '/config\1' diff --git a/package.json b/package.json index 1d00091dad2..d0e4af11dcc 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "json-loader": "^0.5.4", "lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-redux": "lbryio/lbry-redux#f8ac5359d9d05fba2c3a536003a9d4c64b86c9f0", - "lbryinc": "lbryio/lbryinc#3ceb09549cb5ec22927ce3bea44ae8dbe2e4a006", + "lbryinc": "lbryio/lbryinc#72eee35f5181940eb4a468a27ddb2a2a4e362fb0", "lint-staged": "^7.0.2", "localforage": "^1.7.1", "lodash-es": "^4.17.14", diff --git a/ui/component/app/index.js b/ui/component/app/index.js index e8ce190c262..af954ce5831 100644 --- a/ui/component/app/index.js +++ b/ui/component/app/index.js @@ -1,16 +1,10 @@ import * as SETTINGS from 'constants/settings'; import { hot } from 'react-hot-loader/root'; import { connect } from 'react-redux'; -import { - selectUser, - selectAccessToken, - doFetchAccessToken, - selectGetSyncErrorMessage, - selectUploadCount, - selectUnclaimedRewards, - doUserSetReferrer, - selectUserVerifiedEmail, -} from 'lbryinc'; +import { selectGetSyncErrorMessage, selectUploadCount } from 'lbryinc'; +import { doFetchAccessToken, doUserSetReferrer } from 'redux/actions/user'; +import { selectUser, selectAccessToken, selectUserVerifiedEmail } from 'redux/selectors/user'; +import { selectUnclaimedRewards } from 'redux/selectors/rewards'; import { doFetchChannelListMine } from 'lbry-redux'; import { makeSelectClientSetting, selectLoadedLanguages, selectThemePath } from 'redux/selectors/settings'; import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app'; diff --git a/ui/component/app/view.jsx b/ui/component/app/view.jsx index 36d2121ec73..e9cd89bbfed 100644 --- a/ui/component/app/view.jsx +++ b/ui/component/app/view.jsx @@ -14,7 +14,7 @@ import FileRenderFloating from 'component/fileRenderFloating'; import { withRouter } from 'react-router'; import usePrevious from 'effects/use-previous'; import Nag from 'component/common/nag'; -import { rewards as REWARDS } from 'lbryinc'; +import REWARDS from 'rewards'; import usePersistedState from 'effects/use-persisted-state'; import FileDrop from 'component/fileDrop'; // @if TARGET='web' diff --git a/ui/component/blockButton/index.js b/ui/component/blockButton/index.js index e83040917e1..33653b2770b 100644 --- a/ui/component/blockButton/index.js +++ b/ui/component/blockButton/index.js @@ -2,11 +2,11 @@ import { connect } from 'react-redux'; import { selectChannelIsBlocked, doToggleBlockChannel, - doToast, makeSelectClaimIsMine, makeSelectShortUrlForUri, makeSelectPermanentUrlForUri, } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import BlockButton from './view'; const select = (state, props) => ({ @@ -16,10 +16,7 @@ const select = (state, props) => ({ permanentUrl: makeSelectPermanentUrlForUri(props.uri)(state), }); -export default connect( - select, - { - toggleBlockChannel: doToggleBlockChannel, - doToast, - } -)(BlockButton); +export default connect(select, { + toggleBlockChannel: doToggleBlockChannel, + doToast, +})(BlockButton); diff --git a/ui/component/button/index.js b/ui/component/button/index.js index 79c8c0aff8b..a2c913df73e 100644 --- a/ui/component/button/index.js +++ b/ui/component/button/index.js @@ -1,7 +1,7 @@ import Button from './view'; import React, { forwardRef } from 'react'; import { connect } from 'react-redux'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; const mapStateToProps = state => ({ pathname: state.router.location.pathname, diff --git a/ui/component/cardVerify/index.js b/ui/component/cardVerify/index.js index 5a770a0e180..6ef330031ef 100644 --- a/ui/component/cardVerify/index.js +++ b/ui/component/cardVerify/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectUserEmail } from 'lbryinc'; +import { selectUserEmail } from 'redux/selectors/user'; import CardVerify from './view'; const select = state => ({ @@ -8,7 +8,4 @@ const select = state => ({ const perform = () => ({}); -export default connect( - select, - perform -)(CardVerify); +export default connect(select, perform)(CardVerify); diff --git a/ui/component/channelContent/index.js b/ui/component/channelContent/index.js index e115f2a6de2..f51f2ef4822 100644 --- a/ui/component/channelContent/index.js +++ b/ui/component/channelContent/index.js @@ -9,7 +9,7 @@ import { makeSelectClaimForUri, } from 'lbry-redux'; import { withRouter } from 'react-router'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import ChannelPage from './view'; const select = (state, props) => { diff --git a/ui/component/channelForm/index.js b/ui/component/channelForm/index.js index dd0be32f232..ef09534fc34 100644 --- a/ui/component/channelForm/index.js +++ b/ui/component/channelForm/index.js @@ -12,7 +12,7 @@ import { doPrepareEdit, } from 'lbry-redux'; import { doPublishDesktop } from 'redux/actions/publish'; -import { selectUnclaimedRewardValue } from 'lbryinc'; +import { selectUnclaimedRewardValue } from 'redux/selectors/rewards'; import ChannelForm from './view'; const select = state => ({ diff --git a/ui/component/claimLink/index.js b/ui/component/claimLink/index.js index d22c46fea4d..9bc006330e1 100644 --- a/ui/component/claimLink/index.js +++ b/ui/component/claimLink/index.js @@ -1,9 +1,6 @@ import { connect } from 'react-redux'; - import { doResolveUri, makeSelectTitleForUri, makeSelectClaimForUri, makeSelectIsUriResolving } from 'lbry-redux'; - import { selectBlackListedOutpoints } from 'lbryinc'; - import ClaimLink from './view'; const select = (state, props) => { @@ -20,7 +17,4 @@ const perform = dispatch => ({ resolveUri: uri => dispatch(doResolveUri(uri)), }); -export default connect( - select, - perform -)(ClaimLink); +export default connect(select, perform)(ClaimLink); diff --git a/ui/component/claimUri/index.js b/ui/component/claimUri/index.js index 60e8b720cfe..622b9048e73 100644 --- a/ui/component/claimUri/index.js +++ b/ui/component/claimUri/index.js @@ -1,14 +1,12 @@ import { connect } from 'react-redux'; -import { makeSelectCanonicalUrlForUri, doToast } from 'lbry-redux'; +import { makeSelectCanonicalUrlForUri } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import ClaimUri from './view'; const select = (state, props) => ({ shortUrl: makeSelectCanonicalUrlForUri(props.uri)(state), }); -export default connect( - select, - { - doToast, - } -)(ClaimUri); +export default connect(select, { + doToast, +})(ClaimUri); diff --git a/ui/component/commentCreate/index.js b/ui/component/commentCreate/index.js index 832468a3cb1..4d5f9085b0e 100644 --- a/ui/component/commentCreate/index.js +++ b/ui/component/commentCreate/index.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import { doCommentCreate, makeSelectClaimForUri, selectMyChannelClaims } from 'lbry-redux'; import { doOpenModal } from 'redux/actions/app'; import { CommentCreate } from './view'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; const select = (state, props) => ({ commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true, diff --git a/ui/component/copyableText/index.js b/ui/component/copyableText/index.js index 070ea0e9964..c0b2ac443be 100644 --- a/ui/component/copyableText/index.js +++ b/ui/component/copyableText/index.js @@ -1,10 +1,7 @@ import { connect } from 'react-redux'; -import { doToast } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import CopyableText from './view'; -export default connect( - null, - { - doToast, - } -)(CopyableText); +export default connect(null, { + doToast, +})(CopyableText); diff --git a/ui/component/emailCollection/index.js b/ui/component/emailCollection/index.js index e867618fc12..1e495c69325 100644 --- a/ui/component/emailCollection/index.js +++ b/ui/component/emailCollection/index.js @@ -2,7 +2,7 @@ import * as SETTINGS from 'constants/settings'; import { connect } from 'react-redux'; import { doSetClientSetting } from 'redux/actions/settings'; import { makeSelectClientSetting } from 'redux/selectors/settings'; -import { selectEmailToVerify, selectUser } from 'lbryinc'; +import { selectEmailToVerify, selectUser } from 'redux/selectors/user'; import FirstRunEmailCollection from './view'; const select = state => ({ @@ -17,7 +17,4 @@ const perform = dispatch => () => ({ }, }); -export default connect( - select, - perform -)(FirstRunEmailCollection); +export default connect(select, perform)(FirstRunEmailCollection); diff --git a/ui/component/embedTextArea/index.js b/ui/component/embedTextArea/index.js index 78059cb2696..8916d164210 100644 --- a/ui/component/embedTextArea/index.js +++ b/ui/component/embedTextArea/index.js @@ -1,10 +1,7 @@ import { connect } from 'react-redux'; -import { doToast } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import EmbedTextArea from './view'; -export default connect( - null, - { - doToast, - } -)(EmbedTextArea); +export default connect(null, { + doToast, +})(EmbedTextArea); diff --git a/ui/component/fileDescription/index.js b/ui/component/fileDescription/index.js index 7bef4337119..43afce05b1e 100644 --- a/ui/component/fileDescription/index.js +++ b/ui/component/fileDescription/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { makeSelectClaimForUri, makeSelectMetadataForUri, makeSelectTagsForUri } from 'lbry-redux'; -import { selectUser } from 'lbryinc'; +import { selectUser } from 'redux/selectors/user'; import FileDescription from './view'; const select = (state, props) => ({ diff --git a/ui/component/fileDetails/index.js b/ui/component/fileDetails/index.js index 12fa77f9ee9..c0d15cbc569 100644 --- a/ui/component/fileDetails/index.js +++ b/ui/component/fileDetails/index.js @@ -5,7 +5,7 @@ import { makeSelectMetadataForUri, makeSelectFileInfoForUri, } from 'lbry-redux'; -import { selectUser } from 'lbryinc'; +import { selectUser } from 'redux/selectors/user'; import { doOpenFileInFolder } from 'redux/actions/file'; import FileDetails from './view'; @@ -21,7 +21,4 @@ const perform = dispatch => ({ openFolder: path => dispatch(doOpenFileInFolder(path)), }); -export default connect( - select, - perform -)(FileDetails); +export default connect(select, perform)(FileDetails); diff --git a/ui/component/fileRenderInitiator/index.js b/ui/component/fileRenderInitiator/index.js index 27a6deb237c..6f64fa57272 100644 --- a/ui/component/fileRenderInitiator/index.js +++ b/ui/component/fileRenderInitiator/index.js @@ -8,7 +8,8 @@ import { makeSelectStreamingUrlForUri, makeSelectClaimWasPurchased, } from 'lbry-redux'; -import { makeSelectCostInfoForUri, selectUserVerifiedEmail } from 'lbryinc'; +import { makeSelectCostInfoForUri } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import { withRouter } from 'react-router'; import { diff --git a/ui/component/fileRenderInline/index.js b/ui/component/fileRenderInline/index.js index c0be3b81e5c..934f114d7b6 100644 --- a/ui/component/fileRenderInline/index.js +++ b/ui/component/fileRenderInline/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { makeSelectFileInfoForUri, makeSelectStreamingUrlForUri } from 'lbry-redux'; -import { doClaimEligiblePurchaseRewards } from 'lbryinc'; +import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards'; import { makeSelectFileRenderModeForUri, makeSelectIsPlaying } from 'redux/selectors/content'; import { withRouter } from 'react-router'; import { doAnalyticsView } from 'redux/actions/app'; diff --git a/ui/component/fileValues/index.js b/ui/component/fileValues/index.js index 49a2b8084ea..24658d85d15 100644 --- a/ui/component/fileValues/index.js +++ b/ui/component/fileValues/index.js @@ -7,7 +7,7 @@ import { makeSelectPendingAmountByUri, makeSelectClaimIsMine, } from 'lbry-redux'; -import { selectUser } from 'lbryinc'; +import { selectUser } from 'redux/selectors/user'; import { doOpenModal } from 'redux/actions/app'; import FileValues from './view'; diff --git a/ui/component/header/index.js b/ui/component/header/index.js index 2339a5f3662..1ab115301d5 100644 --- a/ui/component/header/index.js +++ b/ui/component/header/index.js @@ -2,14 +2,9 @@ import * as SETTINGS from 'constants/settings'; import * as MODALS from 'constants/modal_types'; import { connect } from 'react-redux'; import { selectBalance, formatCredits } from 'lbry-redux'; -import { - selectUserVerifiedEmail, - selectGetSyncErrorMessage, - selectUserEmail, - doClearEmailEntry, - doClearPasswordEntry, - selectEmailToVerify, -} from 'lbryinc'; +import { selectGetSyncErrorMessage } from 'lbryinc'; +import { selectUserVerifiedEmail, selectUserEmail, selectEmailToVerify } from 'redux/selectors/user'; +import { doClearEmailEntry, doClearPasswordEntry } from 'redux/actions/user'; import { doSetClientSetting } from 'redux/actions/settings'; import { doSignOut, doOpenModal } from 'redux/actions/app'; import { makeSelectClientSetting } from 'redux/selectors/settings'; diff --git a/ui/component/inviteList/index.js b/ui/component/inviteList/index.js index c332d49b7b1..253a8c2660a 100644 --- a/ui/component/inviteList/index.js +++ b/ui/component/inviteList/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; -import { selectReferralReward, selectUserInvitees, selectUserInviteStatusIsPending } from 'lbryinc'; +import { selectReferralReward } from 'redux/selectors/rewards'; +import { selectUserInvitees, selectUserInviteStatusIsPending } from 'redux/selectors/user'; import InviteList from './view'; const select = state => ({ @@ -10,7 +11,4 @@ const select = state => ({ const perform = () => ({}); -export default connect( - select, - perform -)(InviteList); +export default connect(select, perform)(InviteList); diff --git a/ui/component/inviteNew/index.js b/ui/component/inviteNew/index.js index fdd549d7050..68d316e6296 100644 --- a/ui/component/inviteNew/index.js +++ b/ui/component/inviteNew/index.js @@ -5,8 +5,8 @@ import { selectUserInviteNewErrorMessage, selectUserInviteReferralLink, selectUserInviteReferralCode, - doUserInviteNew, -} from 'lbryinc'; +} from 'redux/selectors/user'; +import { doUserInviteNew } from 'redux/actions/user'; import { selectMyChannelClaims, selectFetchingMyChannels, doFetchChannelListMine } from 'lbry-redux'; import InviteNew from './view'; @@ -25,7 +25,4 @@ const perform = dispatch => ({ fetchChannelListMine: () => dispatch(doFetchChannelListMine()), }); -export default connect( - select, - perform -)(InviteNew); +export default connect(select, perform)(InviteNew); diff --git a/ui/component/invited/index.js b/ui/component/invited/index.js index 84c04b498a0..7d76abf08a0 100644 --- a/ui/component/invited/index.js +++ b/ui/component/invited/index.js @@ -1,17 +1,13 @@ import { connect } from 'react-redux'; -import { - selectUser, - doClaimRewardType, - doUserSetReferrer, - selectSetReferrerPending, - selectSetReferrerError, - rewards as REWARDS, - selectUnclaimedRewards, -} from 'lbryinc'; +import { withRouter } from 'react-router'; +import REWARDS from 'rewards'; +import { selectUser, selectSetReferrerPending, selectSetReferrerError } from 'redux/selectors/user'; +import { doClaimRewardType } from 'redux/actions/rewards'; +import { selectUnclaimedRewards } from 'redux/selectors/rewards'; +import { doUserSetReferrer } from 'redux/actions/user'; import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions'; import { doChannelSubscribe } from 'redux/actions/subscriptions'; import Invited from './view'; -import { withRouter } from 'react-router'; const select = (state, props) => { return { @@ -31,9 +27,4 @@ const perform = dispatch => ({ channelSubscribe: uri => dispatch(doChannelSubscribe(uri)), }); -export default withRouter( - connect( - select, - perform - )(Invited) -); +export default withRouter(connect(select, perform)(Invited)); diff --git a/ui/component/invited/view.jsx b/ui/component/invited/view.jsx index ce51cd16230..d6667250f1c 100644 --- a/ui/component/invited/view.jsx +++ b/ui/component/invited/view.jsx @@ -5,7 +5,8 @@ import Button from 'component/button'; import ClaimPreview from 'component/claimPreview'; import Card from 'component/common/card'; import { buildURI, parseURI } from 'lbry-redux'; -import { rewards as REWARDS, ERRORS } from 'lbryinc'; +import { ERRORS } from 'lbryinc'; +import REWARDS from 'rewards'; import { formatLbryUrlForWeb } from 'util/url'; import ChannelContent from 'component/channelContent'; import I18nMessage from 'component/i18nMessage'; diff --git a/ui/component/previewLink/index.js b/ui/component/previewLink/index.js index 2785d69ae47..b8c4cb8d9dd 100644 --- a/ui/component/previewLink/index.js +++ b/ui/component/previewLink/index.js @@ -1,5 +1,4 @@ import { connect } from 'react-redux'; - import { doResolveUri, makeSelectClaimIsMine, @@ -9,9 +8,7 @@ import { makeSelectIsUriResolving, makeSelectMetadataItemForUri, } from 'lbry-redux'; - import { selectBlackListedOutpoints } from 'lbryinc'; - import PreviewLink from './view'; const select = (state, props) => { @@ -31,7 +28,4 @@ const perform = dispatch => ({ resolveUri: uri => dispatch(doResolveUri(uri)), }); -export default connect( - select, - perform -)(PreviewLink); +export default connect(select, perform)(PreviewLink); diff --git a/ui/component/privacyAgreement/index.js b/ui/component/privacyAgreement/index.js index d5b7e143c4e..7a69014e9ad 100644 --- a/ui/component/privacyAgreement/index.js +++ b/ui/component/privacyAgreement/index.js @@ -3,7 +3,7 @@ import { doSetDaemonSetting } from 'redux/actions/settings'; import { doSetWelcomeVersion, doToggle3PAnalytics, doSignOut } from 'redux/actions/app'; import { DAEMON_SETTINGS } from 'lbry-redux'; import { WELCOME_VERSION } from 'config.js'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import PrivacyAgreement from './view'; const select = state => ({ @@ -17,7 +17,4 @@ const perform = dispatch => ({ signOut: () => dispatch(doSignOut()), }); -export default connect( - select, - perform -)(PrivacyAgreement); +export default connect(select, perform)(PrivacyAgreement); diff --git a/ui/component/publishFile/index.js b/ui/component/publishFile/index.js index bcd1727b68f..27bf633221a 100644 --- a/ui/component/publishFile/index.js +++ b/ui/component/publishFile/index.js @@ -4,9 +4,9 @@ import { selectIsStillEditing, makeSelectPublishFormValue, doUpdatePublishForm, - doToast, doClearPublish, } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import { selectFfmpegStatus } from 'redux/selectors/settings'; import PublishPage from './view'; diff --git a/ui/component/publishForm/index.js b/ui/component/publishForm/index.js index 8cd741839a3..effcdf9d2b2 100644 --- a/ui/component/publishForm/index.js +++ b/ui/component/publishForm/index.js @@ -13,7 +13,7 @@ import { doCheckPublishNameAvailability, } from 'lbry-redux'; import { doPublishDesktop } from 'redux/actions/publish'; -import { selectUnclaimedRewardValue } from 'lbryinc'; +import { selectUnclaimedRewardValue } from 'redux/selectors/rewards'; import PublishPage from './view'; const select = state => ({ diff --git a/ui/component/recommendedContent/index.js b/ui/component/recommendedContent/index.js index 87a19286e9a..35bdc735d85 100644 --- a/ui/component/recommendedContent/index.js +++ b/ui/component/recommendedContent/index.js @@ -6,7 +6,7 @@ import { makeSelectRecommendedContentForUri, selectIsSearching, } from 'lbry-redux'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import RecommendedVideos from './view'; const select = (state, props) => ({ diff --git a/ui/component/rewardAuthIntro/index.js b/ui/component/rewardAuthIntro/index.js index 4acfa933c11..014a436d1f9 100644 --- a/ui/component/rewardAuthIntro/index.js +++ b/ui/component/rewardAuthIntro/index.js @@ -1,12 +1,9 @@ import { connect } from 'react-redux'; -import { selectUnclaimedRewardValue } from 'lbryinc'; +import { selectUnclaimedRewardValue } from 'redux/selectors/rewards'; import RewardAuthIntro from './view'; const select = state => ({ totalRewardValue: selectUnclaimedRewardValue(state), }); -export default connect( - select, - null -)(RewardAuthIntro); +export default connect(select, null)(RewardAuthIntro); diff --git a/ui/component/rewardLink/index.js b/ui/component/rewardLink/index.js index d727f3846ea..c240b28ec12 100644 --- a/ui/component/rewardLink/index.js +++ b/ui/component/rewardLink/index.js @@ -1,5 +1,7 @@ import { connect } from 'react-redux'; -import { makeSelectRewardByClaimCode, makeSelectIsRewardClaimPending, doClaimRewardType } from 'lbryinc'; +import { makeSelectRewardByClaimCode, makeSelectIsRewardClaimPending } from 'redux/selectors/rewards'; +import { doClaimRewardType } from 'redux/actions/rewards'; + import RewardLink from './view'; const select = (state, props) => ({ diff --git a/ui/component/rewardListClaimed/index.js b/ui/component/rewardListClaimed/index.js index 1bab4ea82f7..062d5ade680 100644 --- a/ui/component/rewardListClaimed/index.js +++ b/ui/component/rewardListClaimed/index.js @@ -1,12 +1,9 @@ import { connect } from 'react-redux'; -import { selectClaimedRewards } from 'lbryinc'; +import { selectClaimedRewards } from 'redux/selectors/rewards'; import RewardListClaimed from './view'; const select = state => ({ rewards: selectClaimedRewards(state), }); -export default connect( - select, - null -)(RewardListClaimed); +export default connect(select, null)(RewardListClaimed); diff --git a/ui/component/rewardSummary/index.js b/ui/component/rewardSummary/index.js index d58cd8f0745..b9635fa580f 100644 --- a/ui/component/rewardSummary/index.js +++ b/ui/component/rewardSummary/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectUnclaimedRewardValue, selectFetchingRewards } from 'lbryinc'; +import { selectUnclaimedRewardValue, selectFetchingRewards } from 'redux/selectors/rewards'; import RewardSummary from './view'; const select = state => ({ @@ -7,7 +7,4 @@ const select = state => ({ fetching: selectFetchingRewards(state), }); -export default connect( - select, - null -)(RewardSummary); +export default connect(select, null)(RewardSummary); diff --git a/ui/component/rewardTile/index.js b/ui/component/rewardTile/index.js index 9fe7fb90773..53138953792 100644 --- a/ui/component/rewardTile/index.js +++ b/ui/component/rewardTile/index.js @@ -1,8 +1,8 @@ import * as MODALS from 'constants/modal_types'; import { connect } from 'react-redux'; import { doOpenModal } from 'redux/actions/app'; +import { selectUser } from 'redux/selectors/user'; import RewardTile from './view'; -import { selectUser } from 'lbryinc'; const select = state => ({ user: selectUser(state), @@ -12,7 +12,4 @@ const perform = dispatch => ({ openSetReferrerModal: () => dispatch(doOpenModal(MODALS.SET_REFERRER)), }); -export default connect( - select, - perform -)(RewardTile); +export default connect(select, perform)(RewardTile); diff --git a/ui/component/rewardTile/view.jsx b/ui/component/rewardTile/view.jsx index 1b2831298b8..6d324c9ad83 100644 --- a/ui/component/rewardTile/view.jsx +++ b/ui/component/rewardTile/view.jsx @@ -5,7 +5,7 @@ import Icon from 'component/common/icon'; import RewardLink from 'component/rewardLink'; import Button from 'component/button'; import Card from 'component/common/card'; -import { rewards } from 'lbryinc'; +import rewards from 'rewards'; type Props = { openRewardCodeModal: () => void, diff --git a/ui/component/rewardTotal/index.js b/ui/component/rewardTotal/index.js index 6228a14edeb..460b457296a 100644 --- a/ui/component/rewardTotal/index.js +++ b/ui/component/rewardTotal/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; -import { selectUnclaimedRewardValue, selectFetchingRewards, doRewardList, selectClaimedRewards } from 'lbryinc'; +import { selectUnclaimedRewardValue, selectFetchingRewards, selectClaimedRewards } from 'redux/selectors/rewards'; +import { doRewardList } from 'redux/actions/rewards'; import RewardSummary from './view'; const select = state => ({ @@ -12,7 +13,4 @@ const perform = dispatch => ({ fetchRewards: () => dispatch(doRewardList()), }); -export default connect( - select, - perform -)(RewardSummary); +export default connect(select, perform)(RewardSummary); diff --git a/ui/component/router/index.js b/ui/component/router/index.js index 0a9ac9a2c49..159f57c8930 100644 --- a/ui/component/router/index.js +++ b/ui/component/router/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectScrollStartingPosition, selectWelcomeVersion } from 'redux/selectors/app'; import Router from './view'; import { normalizeURI, makeSelectTitleForUri } from 'lbry-redux'; diff --git a/ui/component/selectChannel/index.js b/ui/component/selectChannel/index.js index 2947e4db139..bf2c9a69a88 100644 --- a/ui/component/selectChannel/index.js +++ b/ui/component/selectChannel/index.js @@ -7,7 +7,7 @@ import { doFetchChannelListMine, doCreateChannel, } from 'lbry-redux'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; const select = state => ({ channels: selectMyChannelClaims(state), @@ -21,7 +21,4 @@ const perform = dispatch => ({ fetchChannelListMine: () => dispatch(doFetchChannelListMine()), }); -export default connect( - select, - perform -)(SelectChannel); +export default connect(select, perform)(SelectChannel); diff --git a/ui/component/settingAccountPassword/index.js b/ui/component/settingAccountPassword/index.js index 180f05cde3b..fcc5ddd81ec 100644 --- a/ui/component/settingAccountPassword/index.js +++ b/ui/component/settingAccountPassword/index.js @@ -1,12 +1,7 @@ import { connect } from 'react-redux'; -import { - selectUser, - selectPasswordSetSuccess, - selectPasswordSetError, - doUserPasswordSet, - doClearPasswordEntry, -} from 'lbryinc'; -import { doToast } from 'lbry-redux'; +import { selectUser, selectPasswordSetSuccess, selectPasswordSetError } from 'redux/selectors/user'; +import { doUserPasswordSet, doClearPasswordEntry } from 'redux/actions/user'; +import { doToast } from 'redux/actions/notifications'; import UserSignIn from './view'; const select = state => ({ diff --git a/ui/component/settingAutoLaunch/index.js b/ui/component/settingAutoLaunch/index.js index 5a08f94f98c..b8360f89250 100644 --- a/ui/component/settingAutoLaunch/index.js +++ b/ui/component/settingAutoLaunch/index.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import * as SETTINGS from 'constants/settings'; import { doSetAutoLaunch } from 'redux/actions/settings'; import { makeSelectClientSetting } from 'redux/selectors/settings'; -import { doToast } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import SettingAutoLaunch from './view'; const select = state => ({ @@ -14,7 +14,4 @@ const perform = dispatch => ({ setAutoLaunch: value => dispatch(doSetAutoLaunch(value)), }); -export default connect( - select, - perform -)(SettingAutoLaunch); +export default connect(select, perform)(SettingAutoLaunch); diff --git a/ui/component/shareButton/index.js b/ui/component/shareButton/index.js index 81ed8bf9448..a8dd393c454 100644 --- a/ui/component/shareButton/index.js +++ b/ui/component/shareButton/index.js @@ -1,17 +1,14 @@ import { connect } from 'react-redux'; import { doChannelSubscribe, doChannelUnsubscribe } from 'redux/actions/subscriptions'; import { doOpenModal } from 'redux/actions/app'; -import { doToast } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import ShareButton from './view'; const select = (state, props) => ({}); -export default connect( - select, - { - doChannelSubscribe, - doChannelUnsubscribe, - doOpenModal, - doToast, - } -)(ShareButton); +export default connect(select, { + doChannelSubscribe, + doChannelUnsubscribe, + doOpenModal, + doToast, +})(ShareButton); diff --git a/ui/component/sideNavigation/index.js b/ui/component/sideNavigation/index.js index 0b883d7cc42..743c11a5ab5 100644 --- a/ui/component/sideNavigation/index.js +++ b/ui/component/sideNavigation/index.js @@ -2,7 +2,8 @@ import * as SETTINGS from 'constants/settings'; import { connect } from 'react-redux'; import { selectSubscriptions } from 'redux/selectors/subscriptions'; import { selectFollowedTags, selectPurchaseUriSuccess, doClearPurchasedUriSuccess } from 'lbry-redux'; -import { selectUploadCount, selectUserVerifiedEmail } from 'lbryinc'; +import { selectUploadCount } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import { doSignOut } from 'redux/actions/app'; import SideNavigation from './view'; diff --git a/ui/component/snackBar/index.js b/ui/component/snackBar/index.js index 45bfbade665..9bda252ce6c 100644 --- a/ui/component/snackBar/index.js +++ b/ui/component/snackBar/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; -import { selectToast, doDismissToast } from 'lbry-redux'; +import { doDismissToast } from 'redux/actions/notifications'; +import { selectToast } from 'redux/selectors/notifications'; import SnackBar from './view'; const perform = dispatch => ({ @@ -10,7 +11,4 @@ const select = state => ({ snack: selectToast(state), }); -export default connect( - select, - perform -)(SnackBar); +export default connect(select, perform)(SnackBar); diff --git a/ui/component/socialShare/index.js b/ui/component/socialShare/index.js index 839fb5f4204..7440086992b 100644 --- a/ui/component/socialShare/index.js +++ b/ui/component/socialShare/index.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import { makeSelectClaimForUri, makeSelectTitleForUri } from 'lbry-redux'; import SocialShare from './view'; -import { selectUserInviteReferralCode, selectUser } from 'lbryinc'; +import { selectUserInviteReferralCode, selectUser } from 'redux/selectors/user'; import { makeSelectContentPositionForUri } from 'redux/selectors/content'; const select = (state, props) => ({ diff --git a/ui/component/splash/index.js b/ui/component/splash/index.js index 5f8788e0055..052e0811d13 100644 --- a/ui/component/splash/index.js +++ b/ui/component/splash/index.js @@ -4,7 +4,8 @@ import { selectDaemonVersionMatched, selectModal } from 'redux/selectors/app'; import { doCheckDaemonVersion, doOpenModal, doHideModal } from 'redux/actions/app'; import { doSetClientSetting, doClearDaemonSetting } from 'redux/actions/settings'; import * as settings from 'constants/settings'; -import { doToast, DAEMON_SETTINGS } from 'lbry-redux'; +import { DAEMON_SETTINGS } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import SplashScreen from './view'; import { makeSelectClientSetting } from 'redux/selectors/settings'; @@ -25,7 +26,4 @@ const perform = dispatch => ({ doShowSnackBar: message => dispatch(doToast({ isError: true, message })), }); -export default connect( - select, - perform -)(SplashScreen); +export default connect(select, perform)(SplashScreen); diff --git a/ui/component/subscribeButton/index.js b/ui/component/subscribeButton/index.js index 8186e198b5b..2a0af6b5ba9 100644 --- a/ui/component/subscribeButton/index.js +++ b/ui/component/subscribeButton/index.js @@ -2,7 +2,8 @@ import { connect } from 'react-redux'; import { doChannelSubscribe, doChannelUnsubscribe } from 'redux/actions/subscriptions'; import { doOpenModal } from 'redux/actions/app'; import { selectSubscriptions, makeSelectIsSubscribed, selectFirstRunCompleted } from 'redux/selectors/subscriptions'; -import { doToast, makeSelectPermanentUrlForUri } from 'lbry-redux'; +import { makeSelectPermanentUrlForUri } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import SubscribeButton from './view'; const select = (state, props) => ({ @@ -12,12 +13,9 @@ const select = (state, props) => ({ permanentUrl: makeSelectPermanentUrlForUri(props.uri)(state), }); -export default connect( - select, - { - doChannelSubscribe, - doChannelUnsubscribe, - doOpenModal, - doToast, - } -)(SubscribeButton); +export default connect(select, { + doChannelSubscribe, + doChannelUnsubscribe, + doOpenModal, + doToast, +})(SubscribeButton); diff --git a/ui/component/syncPassword/index.js b/ui/component/syncPassword/index.js index c18555c2df1..a7bf5f4b342 100644 --- a/ui/component/syncPassword/index.js +++ b/ui/component/syncPassword/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; -import { doGetSync, selectGetSyncIsPending, selectUserEmail, selectSyncApplyPasswordError } from 'lbryinc'; +import { doGetSync, selectGetSyncIsPending, selectSyncApplyPasswordError } from 'lbryinc'; +import { selectUserEmail } from 'redux/selectors/user'; import { doSetClientSetting } from 'redux/actions/settings'; import { doSignOut, doHandleSyncComplete } from 'redux/actions/app'; import SyncPassword from './view'; diff --git a/ui/component/syncToggle/index.js b/ui/component/syncToggle/index.js index 4461730beef..a7e3699f51d 100644 --- a/ui/component/syncToggle/index.js +++ b/ui/component/syncToggle/index.js @@ -1,6 +1,7 @@ import * as SETTINGS from 'constants/settings'; import { connect } from 'react-redux'; -import { selectUserVerifiedEmail, selectGetSyncErrorMessage } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; +import { selectGetSyncErrorMessage } from 'lbryinc'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import { doSetClientSetting } from 'redux/actions/settings'; import SyncToggle from './view'; @@ -15,7 +16,4 @@ const perform = dispatch => ({ setSyncEnabled: value => dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, value)), }); -export default connect( - select, - perform -)(SyncToggle); +export default connect(select, perform)(SyncToggle); diff --git a/ui/component/transactionListTable/index.js b/ui/component/transactionListTable/index.js index 3e1ea773683..ff8d36db401 100644 --- a/ui/component/transactionListTable/index.js +++ b/ui/component/transactionListTable/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectClaimedRewardsByTransactionId } from 'lbryinc'; +import { selectClaimedRewardsByTransactionId } from 'redux/selectors/rewards'; import { doOpenModal } from 'redux/actions/app'; import { selectIsFetchingTxos } from 'lbry-redux'; import TransactionListTable from './view'; diff --git a/ui/component/userEmail/index.js b/ui/component/userEmail/index.js index 195598badcc..4db989dd024 100644 --- a/ui/component/userEmail/index.js +++ b/ui/component/userEmail/index.js @@ -1,12 +1,6 @@ import { connect } from 'react-redux'; -import { - selectEmailToVerify, - doUserResendVerificationEmail, - doUserCheckEmailVerified, - selectUser, - doFetchAccessToken, - selectAccessToken, -} from 'lbryinc'; +import { doUserResendVerificationEmail, doUserCheckEmailVerified, doFetchAccessToken } from 'redux/actions/user'; +import { selectEmailToVerify, selectUser, selectAccessToken } from 'redux/selectors/user'; import UserEmailVerify from './view'; const select = state => ({ diff --git a/ui/component/userEmailNew/index.js b/ui/component/userEmailNew/index.js index e1a830b076b..3261ffaaec5 100644 --- a/ui/component/userEmailNew/index.js +++ b/ui/component/userEmailNew/index.js @@ -1,13 +1,12 @@ import * as SETTINGS from 'constants/settings'; import { connect } from 'react-redux'; +import { doClearEmailEntry, doUserSignUp } from 'redux/actions/user'; import { selectEmailNewIsPending, selectEmailNewErrorMessage, selectEmailAlreadyExists, - doUserSignUp, - doClearEmailEntry, selectUser, -} from 'lbryinc'; +} from 'redux/selectors/user'; import { DAEMON_SETTINGS } from 'lbry-redux'; import { doSetClientSetting, doSetDaemonSetting } from 'redux/actions/settings'; import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings'; diff --git a/ui/component/userEmailReturning/index.js b/ui/component/userEmailReturning/index.js index 2e8855b426d..43b83b5f816 100644 --- a/ui/component/userEmailReturning/index.js +++ b/ui/component/userEmailReturning/index.js @@ -2,12 +2,11 @@ import { connect } from 'react-redux'; import { selectEmailNewErrorMessage, selectEmailToVerify, - doUserCheckIfEmailExists, - doClearEmailEntry, selectEmailDoesNotExist, selectEmailAlreadyExists, selectUser, -} from 'lbryinc'; +} from 'redux/selectors/user'; +import { doUserCheckIfEmailExists, doClearEmailEntry } from 'redux/actions/user'; import { doSetClientSetting } from 'redux/actions/settings'; import UserEmailReturning from './view'; diff --git a/ui/component/userEmailVerify/index.js b/ui/component/userEmailVerify/index.js index 1fc6ecfd129..844fe25b6e1 100644 --- a/ui/component/userEmailVerify/index.js +++ b/ui/component/userEmailVerify/index.js @@ -1,13 +1,12 @@ import { connect } from 'react-redux'; +import { doUserResendVerificationEmail, doUserCheckEmailVerified } from 'redux/actions/user'; import { - selectEmailAlreadyExists, selectEmailToVerify, - doUserResendVerificationEmail, - doUserCheckEmailVerified, + selectEmailAlreadyExists, selectUser, selectResendingVerificationEmail, -} from 'lbryinc'; -import { doToast } from 'lbry-redux'; +} from 'redux/selectors/user'; +import { doToast } from 'redux/actions/notifications'; import UserEmailVerify from './view'; const select = state => ({ diff --git a/ui/component/userFirstChannel/index.js b/ui/component/userFirstChannel/index.js index 1ef53cad5f2..cc7639d1491 100644 --- a/ui/component/userFirstChannel/index.js +++ b/ui/component/userFirstChannel/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectUser, selectEmailToVerify } from 'lbryinc'; +import { selectUser, selectEmailToVerify } from 'redux/selectors/user'; import { doCreateChannel, selectCreatingChannel, selectMyChannelClaims, selectCreateChannelError } from 'lbry-redux'; import UserFirstChannel from './view'; @@ -15,7 +15,4 @@ const perform = dispatch => ({ createChannel: (name, amount) => dispatch(doCreateChannel(name, amount)), }); -export default connect( - select, - perform -)(UserFirstChannel); +export default connect(select, perform)(UserFirstChannel); diff --git a/ui/component/userPasswordReset/index.js b/ui/component/userPasswordReset/index.js index f6799bd8de0..86559a9cd84 100644 --- a/ui/component/userPasswordReset/index.js +++ b/ui/component/userPasswordReset/index.js @@ -1,14 +1,12 @@ import { connect } from 'react-redux'; import { - doUserPasswordReset, selectPasswordResetSuccess, selectPasswordResetIsPending, selectPasswordResetError, - doClearPasswordEntry, - doClearEmailEntry, selectEmailToVerify, -} from 'lbryinc'; -import { doToast } from 'lbry-redux'; +} from 'redux/selectors/user'; +import { doUserPasswordReset, doClearPasswordEntry, doClearEmailEntry } from 'redux/actions/user'; +import { doToast } from 'redux/actions/notifications'; import UserSignIn from './view'; const select = state => ({ diff --git a/ui/component/userPasswordSet/index.js b/ui/component/userPasswordSet/index.js index ead9767aad5..061ebb1765d 100644 --- a/ui/component/userPasswordSet/index.js +++ b/ui/component/userPasswordSet/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; -import { doClearEmailEntry, doUserFetch } from 'lbryinc'; -import { doToast } from 'lbry-redux'; +import { doClearEmailEntry, doUserFetch } from 'redux/actions/user'; +import { doToast } from 'redux/actions/notifications'; import UserSignIn from './view'; const select = state => ({ diff --git a/ui/component/userPhoneNew/index.js b/ui/component/userPhoneNew/index.js index 12b2fdf2dec..de168b790d9 100644 --- a/ui/component/userPhoneNew/index.js +++ b/ui/component/userPhoneNew/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; -import { selectPhoneNewErrorMessage, doUserPhoneNew } from 'lbryinc'; +import { selectPhoneNewErrorMessage } from 'redux/selectors/user'; +import { doUserPhoneNew } from 'redux/actions/user'; import UserPhoneNew from './view'; const select = state => ({ @@ -10,7 +11,4 @@ const perform = dispatch => ({ addUserPhone: (phone, countryCode) => dispatch(doUserPhoneNew(phone, countryCode)), }); -export default connect( - select, - perform -)(UserPhoneNew); +export default connect(select, perform)(UserPhoneNew); diff --git a/ui/component/userPhoneVerify/index.js b/ui/component/userPhoneVerify/index.js index a916182f2a4..7aa50987239 100644 --- a/ui/component/userPhoneVerify/index.js +++ b/ui/component/userPhoneVerify/index.js @@ -1,11 +1,6 @@ import { connect } from 'react-redux'; -import { - doUserPhoneVerify, - doUserPhoneReset, - selectPhoneToVerify, - selectPhoneVerifyErrorMessage, - selectUserCountryCode, -} from 'lbryinc'; +import { doUserPhoneVerify, doUserPhoneReset } from 'redux/actions/user'; +import { selectPhoneToVerify, selectPhoneVerifyErrorMessage, selectUserCountryCode } from 'redux/selectors/user'; import UserPhoneVerify from './view'; const select = state => ({ @@ -19,7 +14,4 @@ const perform = dispatch => ({ verifyUserPhone: code => dispatch(doUserPhoneVerify(code)), }); -export default connect( - select, - perform -)(UserPhoneVerify); +export default connect(select, perform)(UserPhoneVerify); diff --git a/ui/component/userSignIn/index.js b/ui/component/userSignIn/index.js index e0a55909855..81a2d581890 100644 --- a/ui/component/userSignIn/index.js +++ b/ui/component/userSignIn/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; -import { selectUser, selectUserIsPending, selectEmailToVerify, selectPasswordExists, doUserSignIn } from 'lbryinc'; +import { selectUser, selectUserIsPending, selectEmailToVerify, selectPasswordExists } from 'redux/selectors/user'; +import { doUserSignIn } from 'redux/actions/user'; import UserSignIn from './view'; const select = state => ({ diff --git a/ui/component/userSignInPassword/index.js b/ui/component/userSignInPassword/index.js index e394d1e963d..fe2a8a88df9 100644 --- a/ui/component/userSignInPassword/index.js +++ b/ui/component/userSignInPassword/index.js @@ -1,12 +1,6 @@ import { connect } from 'react-redux'; -import { - selectUser, - selectUserIsPending, - selectEmailToVerify, - selectEmailNewErrorMessage, - doUserSignIn, - doClearEmailEntry, -} from 'lbryinc'; +import { selectUser, selectUserIsPending, selectEmailToVerify, selectEmailNewErrorMessage } from 'redux/selectors/user'; +import { doUserSignIn, doClearEmailEntry } from 'redux/actions/user'; import UserSignIn from './view'; const select = state => ({ diff --git a/ui/component/userSignOutButton/index.js b/ui/component/userSignOutButton/index.js index b4f98eda7eb..2980a6d6f10 100644 --- a/ui/component/userSignOutButton/index.js +++ b/ui/component/userSignOutButton/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { doSignOut } from 'redux/actions/app'; -import { doClearEmailEntry, doClearPasswordEntry } from 'lbryinc'; +import { doClearEmailEntry, doClearPasswordEntry } from 'redux/actions/user'; import UserSignOutButton from './view'; const select = state => ({}); diff --git a/ui/component/userSignUp/index.js b/ui/component/userSignUp/index.js index deaeb731c42..8c637d7261c 100644 --- a/ui/component/userSignUp/index.js +++ b/ui/component/userSignUp/index.js @@ -1,20 +1,17 @@ import * as SETTINGS from 'constants/settings'; +import REWARD_TYPES from 'rewards'; import { connect } from 'react-redux'; +import { selectGetSyncIsPending, selectGetSyncErrorMessage, selectSyncHash } from 'lbryinc'; +import { doClaimRewardType } from 'redux/actions/rewards'; +import { selectClaimedRewards, makeSelectIsRewardClaimPending } from 'redux/selectors/rewards'; +import { doUserFetch } from 'redux/actions/user'; import { + selectUserIsPending, + selectYoutubeChannels, selectEmailToVerify, selectUser, selectAccessToken, - makeSelectIsRewardClaimPending, - selectClaimedRewards, - rewards as REWARD_TYPES, - doClaimRewardType, - doUserFetch, - selectUserIsPending, - selectYoutubeChannels, - selectGetSyncIsPending, - selectGetSyncErrorMessage, - selectSyncHash, -} from 'lbryinc'; +} from 'redux/selectors/user'; import { selectMyChannelClaims, selectBalance, selectFetchingMyChannels, selectCreatingChannel } from 'lbry-redux'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import UserSignIn from './view'; diff --git a/ui/component/userSignUp/view.jsx b/ui/component/userSignUp/view.jsx index f0afe83fe68..b7d5f96ffa3 100644 --- a/ui/component/userSignUp/view.jsx +++ b/ui/component/userSignUp/view.jsx @@ -8,7 +8,8 @@ import UserFirstChannel from 'component/userFirstChannel'; import UserChannelFollowIntro from 'component/userChannelFollowIntro'; import UserTagFollowIntro from 'component/userTagFollowIntro'; import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view'; -import { rewards as REWARDS, YOUTUBE_STATUSES } from 'lbryinc'; +import { YOUTUBE_STATUSES } from 'lbryinc'; +import REWARDS from 'rewards'; import UserVerify from 'component/userVerify'; import Spinner from 'component/spinner'; import YoutubeTransferStatus from 'component/youtubeTransferStatus'; diff --git a/ui/component/userVerify/index.js b/ui/component/userVerify/index.js index 312d05dad9a..3a928174ae2 100644 --- a/ui/component/userVerify/index.js +++ b/ui/component/userVerify/index.js @@ -1,14 +1,10 @@ import * as MODALS from 'constants/modal_types'; import { connect } from 'react-redux'; import { doOpenModal } from 'redux/actions/app'; -import { - doUserIdentityVerify, - doUserFetch, - rewards, - makeSelectRewardByType, - selectIdentityVerifyIsPending, - selectIdentityVerifyErrorMessage, -} from 'lbryinc'; +import { doUserIdentityVerify, doUserFetch } from 'redux/actions/user'; +import { makeSelectRewardByType } from 'redux/selectors/rewards'; +import rewards from 'rewards'; +import { selectIdentityVerifyIsPending, selectIdentityVerifyErrorMessage } from 'redux/selectors/user'; import UserVerify from './view'; const select = state => { @@ -27,7 +23,4 @@ const perform = dispatch => ({ fetchUser: () => dispatch(doUserFetch()), }); -export default connect( - select, - perform -)(UserVerify); +export default connect(select, perform)(UserVerify); diff --git a/ui/component/viewers/videoViewer/index.js b/ui/component/viewers/videoViewer/index.js index a89ff3b0e3a..28cf5155f21 100644 --- a/ui/component/viewers/videoViewer/index.js +++ b/ui/component/viewers/videoViewer/index.js @@ -6,7 +6,7 @@ import { savePosition, clearPosition } from 'redux/actions/content'; import { makeSelectContentPositionForUri } from 'redux/selectors/content'; import VideoViewer from './view'; import { withRouter } from 'react-router'; -import { doClaimEligiblePurchaseRewards } from 'lbryinc'; +import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import * as SETTINGS from 'constants/settings'; diff --git a/ui/component/walletBalance/index.js b/ui/component/walletBalance/index.js index 48d2fd64a0d..0af1a9c6691 100644 --- a/ui/component/walletBalance/index.js +++ b/ui/component/walletBalance/index.js @@ -1,7 +1,8 @@ import { connect } from 'react-redux'; import { selectBalance, selectClaimsBalance, selectSupportsBalance, selectTipsBalance } from 'lbry-redux'; import { doOpenModal } from 'redux/actions/app'; -import { selectClaimedRewards, selectSyncHash } from 'lbryinc'; +import { selectSyncHash } from 'lbryinc'; +import { selectClaimedRewards } from 'redux/selectors/rewards'; import WalletBalance from './view'; const select = state => ({ diff --git a/ui/component/wunderbar/index.js b/ui/component/wunderbar/index.js index 2895ee45034..bcad5368cc7 100644 --- a/ui/component/wunderbar/index.js +++ b/ui/component/wunderbar/index.js @@ -3,11 +3,11 @@ import { doFocusSearchInput, doBlurSearchInput, doUpdateSearchQuery, - doToast, selectSearchValue, selectSearchSuggestions, selectSearchBarFocused, } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import analytics from 'analytics'; import Wunderbar from './view'; import { withRouter } from 'react-router-dom'; @@ -36,9 +36,4 @@ const perform = (dispatch, ownProps) => ({ doBlur: () => dispatch(doBlurSearchInput()), }); -export default withRouter( - connect( - select, - perform - )(Wunderbar) -); +export default withRouter(connect(select, perform)(Wunderbar)); diff --git a/ui/component/youtubeTransferStatus/index.js b/ui/component/youtubeTransferStatus/index.js index b3f8ad8a02f..2b9c0ddb88b 100644 --- a/ui/component/youtubeTransferStatus/index.js +++ b/ui/component/youtubeTransferStatus/index.js @@ -1,13 +1,11 @@ import { connect } from 'react-redux'; +import { doClaimYoutubeChannels, doUserFetch, doCheckYoutubeTransfer } from 'redux/actions/user'; import { selectYoutubeChannels, + selectYouTubeImportVideosComplete, selectYouTubeImportPending, selectUserIsPending, - doClaimYoutubeChannels, - doUserFetch, - selectYouTubeImportVideosComplete, - doCheckYoutubeTransfer, -} from 'lbryinc'; +} from 'redux/selectors/user'; import YoutubeChannelList from './view'; const select = state => ({ @@ -23,7 +21,4 @@ const perform = dispatch => ({ checkYoutubeTransfer: () => dispatch(doCheckYoutubeTransfer()), }); -export default connect( - select, - perform -)(YoutubeChannelList); +export default connect(select, perform)(YoutubeChannelList); diff --git a/ui/constants/action_types.js b/ui/constants/action_types.js index 7750fa3f530..5092c4bd2e2 100644 --- a/ui/constants/action_types.js +++ b/ui/constants/action_types.js @@ -129,11 +129,24 @@ export const USER_EMAIL_DECLINE = 'USER_EMAIL_DECLINE'; export const USER_EMAIL_NEW_STARTED = 'USER_EMAIL_NEW_STARTED'; export const USER_EMAIL_NEW_SUCCESS = 'USER_EMAIL_NEW_SUCCESS'; export const USER_EMAIL_NEW_EXISTS = 'USER_EMAIL_NEW_EXISTS'; +export const USER_EMAIL_NEW_DOES_NOT_EXIST = 'USER_EMAIL_NEW_DOES_NOT_EXIST'; export const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE'; +export const USER_EMAIL_NEW_CLEAR_ENTRY = 'USER_EMAIL_NEW_CLEAR_ENTRY'; +export const USER_EMAIL_VERIFY_SET = 'USER_EMAIL_VERIFY_SET'; export const USER_EMAIL_VERIFY_STARTED = 'USER_EMAIL_VERIFY_STARTED'; export const USER_EMAIL_VERIFY_SUCCESS = 'USER_EMAIL_VERIFY_SUCCESS'; export const USER_EMAIL_VERIFY_FAILURE = 'USER_EMAIL_VERIFY_FAILURE'; -export const USER_EMAIL_VERIFY_RETRY = 'USER_EMAIL_VERIFY_RETRY'; +export const USER_EMAIL_VERIFY_RETRY_STARTED = 'USER_EMAIL_VERIFY_RETRY_STARTED'; +export const USER_EMAIL_VERIFY_RETRY_FAILURE = 'USER_EMAIL_VERIFY_RETRY_FAILURE'; +export const USER_EMAIL_VERIFY_RETRY_SUCCESS = 'USER_EMAIL_VERIFY_RETRY_SUCCESS'; +export const USER_PASSWORD_EXISTS = 'USER_PASSWORD_EXISTS'; +export const USER_PASSWORD_RESET_STARTED = 'USER_PASSWORD_RESET_STARTED'; +export const USER_PASSWORD_RESET_SUCCESS = 'USER_PASSWORD_RESET_SUCCESS'; +export const USER_PASSWORD_RESET_FAILURE = 'USER_PASSWORD_RESET_FAILURE'; +export const USER_PASSWORD_SET_STARTED = 'USER_PASSWORD_SET_STARTED'; +export const USER_PASSWORD_SET_SUCCESS = 'USER_PASSWORD_SET_SUCCESS'; +export const USER_PASSWORD_SET_FAILURE = 'USER_PASSWORD_SET_FAILURE'; +export const USER_PASSWORD_SET_CLEAR = 'USER_PASSWORD_SET_CLEAR'; export const USER_PHONE_RESET = 'USER_PHONE_RESET'; export const USER_PHONE_NEW_STARTED = 'USER_PHONE_NEW_STARTED'; export const USER_PHONE_NEW_SUCCESS = 'USER_PHONE_NEW_SUCCESS'; @@ -154,6 +167,13 @@ export const USER_INVITE_NEW_STARTED = 'USER_INVITE_NEW_STARTED'; export const USER_INVITE_NEW_SUCCESS = 'USER_INVITE_NEW_SUCCESS'; export const USER_INVITE_NEW_FAILURE = 'USER_INVITE_NEW_FAILURE'; export const FETCH_ACCESS_TOKEN_SUCCESS = 'FETCH_ACCESS_TOKEN_SUCCESS'; +export const USER_YOUTUBE_IMPORT_STARTED = 'USER_YOUTUBE_IMPORT_STARTED'; +export const USER_YOUTUBE_IMPORT_FAILURE = 'USER_YOUTUBE_IMPORT_FAILURE'; +export const USER_YOUTUBE_IMPORT_SUCCESS = 'USER_YOUTUBE_IMPORT_SUCCESS'; +export const USER_SET_REFERRER_STARTED = 'USER_SET_REFERRER_STARTED'; +export const USER_SET_REFERRER_SUCCESS = 'USER_SET_REFERRER_SUCCESS'; +export const USER_SET_REFERRER_FAILURE = 'USER_SET_REFERRER_FAILURE'; +export const USER_SET_REFERRER_RESET = 'USER_SET_REFERRER_RESET'; // Rewards export const FETCH_REWARDS_STARTED = 'FETCH_REWARDS_STARTED'; @@ -198,3 +218,13 @@ export const DO_PREPARE_EDIT = 'DO_PREPARE_EDIT'; // media export const MEDIA_PLAY = 'MEDIA_PLAY'; export const MEDIA_PAUSE = 'MEDIA_PAUSE'; + +// Notifications +export const CREATE_NOTIFICATION = 'CREATE_NOTIFICATION'; +export const EDIT_NOTIFICATION = 'EDIT_NOTIFICATION'; +export const DELETE_NOTIFICATION = 'DELETE_NOTIFICATION'; +export const DISMISS_NOTIFICATION = 'DISMISS_NOTIFICATION'; +export const CREATE_TOAST = 'CREATE_TOAST'; +export const DISMISS_TOAST = 'DISMISS_TOAST'; +export const CREATE_ERROR = 'CREATE_ERROR'; +export const DISMISS_ERROR = 'DISMISS_ERROR'; diff --git a/ui/index.jsx b/ui/index.jsx index 96ef3c5834f..68b566aab8c 100644 --- a/ui/index.jsx +++ b/ui/index.jsx @@ -14,9 +14,10 @@ import React, { Fragment, useState, useEffect } from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { doDaemonReady, doAutoUpdate, doOpenModal, doHideModal, doToggle3PAnalytics } from 'redux/actions/app'; -import { Lbry, doToast, isURIValid, setSearchApi, apiCall } from 'lbry-redux'; +import { Lbry, isURIValid, setSearchApi, apiCall } from 'lbry-redux'; import { doSetLanguage, doFetchLanguage, doUpdateIsNightAsync } from 'redux/actions/settings'; -import { Lbryio, rewards, doBlackListedOutpointsSubscribe, doFilteredOutpointsSubscribe } from 'lbryinc'; +import { Lbryio, doBlackListedOutpointsSubscribe, doFilteredOutpointsSubscribe } from 'lbryinc'; +import rewards from 'rewards'; import { store, persistor, history } from 'store'; import app from './app'; import doLogWarningConsoleMessage from './logWarningConsoleMessage'; @@ -24,6 +25,7 @@ import { ConnectedRouter, push } from 'connected-react-router'; import { formatLbryUrlForWeb, formatInAppUrl } from 'util/url'; import { PersistGate } from 'redux-persist/integration/react'; import analytics from 'analytics'; +import { doToast } from 'redux/actions/notifications'; import { getAuthToken, setAuthToken, @@ -54,7 +56,7 @@ if (process.env.NODE_ENV === 'production') { } if (process.env.SDK_API_URL) { - console.warn('SDK_API_URL env var is deprecated. Use SDK_API_HOST instead'); + console.warn('SDK_API_URL env var is deprecated. Use SDK_API_HOST instead'); // @eslint-disable-line } let sdkAPIHost = process.env.SDK_API_HOST || process.env.SDK_API_URL; diff --git a/ui/modal/modalAutoGenerateThumbnail/index.js b/ui/modal/modalAutoGenerateThumbnail/index.js index 727140e3bd5..d769e869bf1 100644 --- a/ui/modal/modalAutoGenerateThumbnail/index.js +++ b/ui/modal/modalAutoGenerateThumbnail/index.js @@ -1,6 +1,7 @@ import { connect } from 'react-redux'; import { doHideModal } from 'redux/actions/app'; -import { doToast, doUploadThumbnail } from 'lbry-redux'; +import { doUploadThumbnail } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import ModalAutoGenerateThumbnail from './view'; const perform = dispatch => ({ @@ -9,7 +10,4 @@ const perform = dispatch => ({ showToast: options => dispatch(doToast(options)), }); -export default connect( - null, - perform -)(ModalAutoGenerateThumbnail); +export default connect(null, perform)(ModalAutoGenerateThumbnail); diff --git a/ui/modal/modalError/index.js b/ui/modal/modalError/index.js index f0a590f3590..484a8114798 100644 --- a/ui/modal/modalError/index.js +++ b/ui/modal/modalError/index.js @@ -1,12 +1,9 @@ import { connect } from 'react-redux'; -import { doDismissError } from 'lbry-redux'; +import { doDismissError } from 'redux/actions/notifications'; import ModalError from './view'; const perform = dispatch => ({ closeModal: () => dispatch(doDismissError()), }); -export default connect( - null, - perform -)(ModalError); +export default connect(null, perform)(ModalError); diff --git a/ui/modal/modalFirstReward/index.js b/ui/modal/modalFirstReward/index.js index 0eb74947a65..42f1d488c60 100644 --- a/ui/modal/modalFirstReward/index.js +++ b/ui/modal/modalFirstReward/index.js @@ -1,4 +1,5 @@ -import { rewards, makeSelectRewardByType } from 'lbryinc'; +import { makeSelectRewardByType } from 'redux/selectors/rewards'; +import rewards from 'rewards'; import { connect } from 'react-redux'; import { doHideModal } from 'redux/actions/app'; import ModalFirstReward from './view'; @@ -15,7 +16,4 @@ const perform = dispatch => ({ closeModal: () => dispatch(doHideModal()), }); -export default connect( - select, - perform -)(ModalFirstReward); +export default connect(select, perform)(ModalFirstReward); diff --git a/ui/modal/modalFirstSubscription/index.js b/ui/modal/modalFirstSubscription/index.js index 39911c71d0d..4bd4fc514ac 100644 --- a/ui/modal/modalFirstSubscription/index.js +++ b/ui/modal/modalFirstSubscription/index.js @@ -1,21 +1,17 @@ import { connect } from 'react-redux'; import { doHideModal } from 'redux/actions/app'; -import { selectAccessToken, selectUser } from 'lbryinc'; +import { selectAccessToken, selectUser } from 'redux/selectors/user'; import { withRouter } from 'react-router'; - import ModalFirstSubscription from './view'; const select = state => ({ accessToken: selectAccessToken(state), user: selectUser(state), -}) +}); const perform = dispatch => () => ({ closeModal: () => dispatch(doHideModal()), }); -export default withRouter(connect( - select, - perform -)(ModalFirstSubscription)); +export default withRouter(connect(select, perform)(ModalFirstSubscription)); diff --git a/ui/modal/modalPhoneCollection/index.js b/ui/modal/modalPhoneCollection/index.js index b901e44e827..4a4916546ba 100644 --- a/ui/modal/modalPhoneCollection/index.js +++ b/ui/modal/modalPhoneCollection/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { doHideModal } from 'redux/actions/app'; -import { selectPhoneToVerify, selectUser } from 'lbryinc'; +import { selectPhoneToVerify, selectUser } from 'redux/selectors/user'; import ModalPhoneCollection from './view'; const select = state => ({ @@ -12,7 +12,4 @@ const perform = dispatch => () => ({ closeModal: () => dispatch(doHideModal()), }); -export default connect( - select, - perform -)(ModalPhoneCollection); +export default connect(select, perform)(ModalPhoneCollection); diff --git a/ui/modal/modalRepost/index.js b/ui/modal/modalRepost/index.js index a5c76eb69da..054a266ede9 100644 --- a/ui/modal/modalRepost/index.js +++ b/ui/modal/modalRepost/index.js @@ -9,10 +9,10 @@ import { selectRepostError, selectRepostLoading, doClearRepostError, - doToast, selectMyClaimsWithoutChannels, doCheckPublishNameAvailability, } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import ModalRepost from './view'; const select = (state, props) => ({ diff --git a/ui/modal/modalRewardCode/index.js b/ui/modal/modalRewardCode/index.js index f0cb538ad73..63c6a22ea47 100644 --- a/ui/modal/modalRewardCode/index.js +++ b/ui/modal/modalRewardCode/index.js @@ -1,26 +1,21 @@ +import REWARDS from 'rewards'; import { connect } from 'react-redux'; import { doHideModal } from 'redux/actions/app'; -import { - makeSelectClaimRewardError, - doClaimRewardType, - makeSelectIsRewardClaimPending, - rewards as REWARD_TYPES, -} from 'lbryinc'; +import { doClaimRewardType } from 'redux/actions/rewards'; +import { makeSelectClaimRewardError, makeSelectIsRewardClaimPending } from 'redux/selectors/rewards'; + import ModalRewardCode from './view'; const select = state => ({ rewardIsPending: makeSelectIsRewardClaimPending()(state, { - reward_type: REWARD_TYPES.TYPE_REWARD_CODE, + reward_type: REWARDS.TYPE_REWARD_CODE, }), - error: makeSelectClaimRewardError()(state, { reward_type: REWARD_TYPES.TYPE_REWARD_CODE }), + error: makeSelectClaimRewardError()(state, { reward_type: REWARDS.TYPE_REWARD_CODE }), }); const perform = dispatch => ({ closeModal: () => dispatch(doHideModal()), - submitRewardCode: code => dispatch(doClaimRewardType(REWARD_TYPES.TYPE_REWARD_CODE, { params: { code } })), + submitRewardCode: code => dispatch(doClaimRewardType(REWARDS.TYPE_REWARD_CODE, { params: { code } })), }); -export default connect( - select, - perform -)(ModalRewardCode); +export default connect(select, perform)(ModalRewardCode); diff --git a/ui/modal/modalSetReferrer/index.js b/ui/modal/modalSetReferrer/index.js index 8d1f835d59c..4de02df5699 100644 --- a/ui/modal/modalSetReferrer/index.js +++ b/ui/modal/modalSetReferrer/index.js @@ -1,6 +1,7 @@ import { connect } from 'react-redux'; import { doHideModal } from 'redux/actions/app'; -import { doUserSetReferrer, selectSetReferrerError, selectSetReferrerPending, doUserSetReferrerReset } from 'lbryinc'; +import { selectSetReferrerError, selectSetReferrerPending } from 'redux/selectors/user'; +import { doUserSetReferrer, doUserSetReferrerReset } from 'redux/actions/user'; import ModalSetReferrer from './view'; const select = state => ({ @@ -14,7 +15,4 @@ const perform = dispatch => ({ resetReferrerError: () => dispatch(doUserSetReferrerReset()), }); -export default connect( - select, - perform -)(ModalSetReferrer); +export default connect(select, perform)(ModalSetReferrer); diff --git a/ui/page/buy/index.js b/ui/page/buy/index.js index b1770fb4c14..97b12d9d2fe 100644 --- a/ui/page/buy/index.js +++ b/ui/page/buy/index.js @@ -1,6 +1,7 @@ import { connect } from 'react-redux'; import { selectGettingNewAddress, selectReceiveAddress, doGetNewAddress } from 'lbry-redux'; -import { selectUserEmail, selectUser, doUserSetCountry } from 'lbryinc'; +import { selectUserEmail, selectUser } from 'redux/selectors/user'; +import { doUserSetCountry } from 'redux/actions/user'; import BuyPage from './view'; const select = state => ({ diff --git a/ui/page/channels/index.js b/ui/page/channels/index.js index bed565cf3d8..7ccae5a2ced 100644 --- a/ui/page/channels/index.js +++ b/ui/page/channels/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { selectMyChannelClaims, doFetchChannelListMine, selectFetchingMyChannels } from 'lbry-redux'; -import { selectYoutubeChannels } from 'lbryinc'; +import { selectYoutubeChannels } from 'redux/selectors/user'; import { doOpenModal } from 'redux/actions/app'; import ChannelsPage from './view'; @@ -15,7 +15,4 @@ const perform = dispatch => ({ fetchChannelListMine: () => dispatch(doFetchChannelListMine()), }); -export default connect( - select, - perform -)(ChannelsPage); +export default connect(select, perform)(ChannelsPage); diff --git a/ui/page/discover/index.js b/ui/page/discover/index.js index 546a90d53f4..8110bd86484 100644 --- a/ui/page/discover/index.js +++ b/ui/page/discover/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { makeSelectClaimForUri, selectFollowedTags, doResolveUri } from 'lbry-redux'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { doToggleTagFollowDesktop } from 'redux/actions/tags'; import * as CS from 'constants/claim_search'; import Tags from './view'; diff --git a/ui/page/help/index.js b/ui/page/help/index.js index d9657802159..f4b1ced38c1 100644 --- a/ui/page/help/index.js +++ b/ui/page/help/index.js @@ -1,6 +1,7 @@ import * as PAGES from 'constants/pages'; import { connect } from 'react-redux'; -import { doFetchAccessToken, selectAccessToken, selectUser } from 'lbryinc'; +import { doFetchAccessToken } from 'redux/actions/user'; +import { selectAccessToken, selectUser } from 'redux/selectors/user'; import { selectDaemonSettings } from 'redux/selectors/settings'; import HelpPage from './view'; @@ -15,7 +16,4 @@ const perform = (dispatch, ownProps) => ({ fetchAccessToken: () => dispatch(doFetchAccessToken()), }); -export default connect( - select, - perform -)(HelpPage); +export default connect(select, perform)(HelpPage); diff --git a/ui/page/home/index.js b/ui/page/home/index.js index 1f6963001e4..154505f653e 100644 --- a/ui/page/home/index.js +++ b/ui/page/home/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { selectFollowedTags } from 'lbry-redux'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectSubscriptions } from 'redux/selectors/subscriptions'; import DiscoverPage from './view'; @@ -12,7 +12,4 @@ const select = state => ({ const perform = {}; -export default connect( - select, - perform -)(DiscoverPage); +export default connect(select, perform)(DiscoverPage); diff --git a/ui/page/invite/index.js b/ui/page/invite/index.js index 6887b0c5e15..506e49632c1 100644 --- a/ui/page/invite/index.js +++ b/ui/page/invite/index.js @@ -1,11 +1,11 @@ import * as SETTINGS from 'constants/settings'; import { connect } from 'react-redux'; import { - doFetchInviteStatus, selectUserInviteStatusFailed, selectUserInviteStatusIsPending, selectUserVerifiedEmail, -} from 'lbryinc'; +} from 'redux/selectors/user'; +import { doFetchInviteStatus } from 'redux/actions/user'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import { doSetClientSetting } from 'redux/actions/settings'; import InvitePage from './view'; @@ -22,7 +22,4 @@ const perform = dispatch => ({ acknowledgeInivte: () => dispatch(doSetClientSetting(SETTINGS.INVITE_ACKNOWLEDGED, true)), }); -export default connect( - select, - perform -)(InvitePage); +export default connect(select, perform)(InvitePage); diff --git a/ui/page/publish/index.js b/ui/page/publish/index.js index 6ca15704702..1de5ad0a883 100644 --- a/ui/page/publish/index.js +++ b/ui/page/publish/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { selectBalance } from 'lbry-redux'; -import { selectUnclaimedRewardValue } from 'lbryinc'; +import { selectUnclaimedRewardValue } from 'redux/selectors/rewards'; import PublishPage from './view'; const select = state => ({ @@ -8,7 +8,4 @@ const select = state => ({ totalRewardValue: selectUnclaimedRewardValue(state), }); -export default connect( - select, - null -)(PublishPage); +export default connect(select, null)(PublishPage); diff --git a/ui/page/report/view.jsx b/ui/page/report/view.jsx index 2dd124505d7..53f219d4f70 100644 --- a/ui/page/report/view.jsx +++ b/ui/page/report/view.jsx @@ -1,7 +1,7 @@ import React, { Fragment } from 'react'; import Button from 'component/button'; import { FormField } from 'component/common/form'; -import { doToast } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import { Lbryio } from 'lbryinc'; import Page from 'component/page'; import Card from 'component/common/card'; diff --git a/ui/page/rewards/index.js b/ui/page/rewards/index.js index c19001ee150..9c13ece2da7 100644 --- a/ui/page/rewards/index.js +++ b/ui/page/rewards/index.js @@ -1,12 +1,8 @@ import { connect } from 'react-redux'; -import { - selectFetchingRewards, - selectUnclaimedRewards, - selectClaimedRewards, - selectUser, - doRewardList, - doUserFetch, -} from 'lbryinc'; +import { selectUser } from 'redux/selectors/user'; +import { selectFetchingRewards, selectUnclaimedRewards, selectClaimedRewards } from 'redux/selectors/rewards'; +import { doUserFetch } from 'redux/actions/user'; +import { doRewardList } from 'redux/actions/rewards'; import { selectDaemonSettings } from 'redux/selectors/settings'; import RewardsPage from './view'; @@ -23,7 +19,4 @@ const perform = dispatch => ({ fetchUser: () => dispatch(doUserFetch()), }); -export default connect( - select, - perform -)(RewardsPage); +export default connect(select, perform)(RewardsPage); diff --git a/ui/page/rewards/view.jsx b/ui/page/rewards/view.jsx index 3d97f837f0d..d6f2b1a6deb 100644 --- a/ui/page/rewards/view.jsx +++ b/ui/page/rewards/view.jsx @@ -7,7 +7,7 @@ import RewardTile from 'component/rewardTile'; import Button from 'component/button'; import Page from 'component/page'; import classnames from 'classnames'; -import { rewards as REWARD_TYPES } from 'lbryinc'; +import REWARD_TYPES from 'rewards'; import RewardAuthIntro from 'component/rewardAuthIntro'; import Card from 'component/common/card'; import I18nMessage from 'component/i18nMessage'; diff --git a/ui/page/search/index.js b/ui/page/search/index.js index 130e18b4e3c..2386e45c7bb 100644 --- a/ui/page/search/index.js +++ b/ui/page/search/index.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import * as SETTINGS from 'constants/settings'; import { doSearch, selectIsSearching, makeSelectSearchUris, makeSelectQueryWithOptions, doToast } from 'lbry-redux'; import { makeSelectClientSetting } from 'redux/selectors/settings'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import analytics from 'analytics'; import SearchPage from './view'; diff --git a/ui/page/settings/index.js b/ui/page/settings/index.js index 40c89f53509..d964749d4ee 100644 --- a/ui/page/settings/index.js +++ b/ui/page/settings/index.js @@ -25,7 +25,7 @@ import { } from 'redux/selectors/settings'; import { doWalletStatus, selectWalletIsEncrypted, selectBlockedChannelsCount, SETTINGS } from 'lbry-redux'; import SettingsPage from './view'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; const select = state => ({ daemonSettings: selectDaemonSettings(state), diff --git a/ui/page/signInVerify/index.js b/ui/page/signInVerify/index.js index c00a9b77cd9..7c8a3aae953 100644 --- a/ui/page/signInVerify/index.js +++ b/ui/page/signInVerify/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { doToast } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import SignInVerifyPage from './view'; const select = () => ({}); @@ -7,7 +7,4 @@ const perform = { doToast, }; -export default connect( - select, - perform -)(SignInVerifyPage); +export default connect(select, perform)(SignInVerifyPage); diff --git a/ui/page/tagsFollowing/index.js b/ui/page/tagsFollowing/index.js index dd3902fe03d..cc88b860677 100644 --- a/ui/page/tagsFollowing/index.js +++ b/ui/page/tagsFollowing/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { selectFollowedTags } from 'lbry-redux'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectSubscriptions } from 'redux/selectors/subscriptions'; import DiscoverPage from './view'; @@ -12,7 +12,4 @@ const select = state => ({ const perform = {}; -export default connect( - select, - perform -)(DiscoverPage); +export default connect(select, perform)(DiscoverPage); diff --git a/ui/reducers.js b/ui/reducers.js index 72320822f6d..641dbda221f 100644 --- a/ui/reducers.js +++ b/ui/reducers.js @@ -5,15 +5,12 @@ import { fileInfoReducer, searchReducer, walletReducer, - notificationsReducer, tagsReducer, commentReducer, blockedReducer, publishReducer, } from 'lbry-redux'; import { - userReducer, - rewardsReducer, costInfoReducer, blacklistReducer, filteredReducer, @@ -26,6 +23,9 @@ import appReducer from 'redux/reducers/app'; import contentReducer from 'redux/reducers/content'; import settingsReducer from 'redux/reducers/settings'; import subscriptionsReducer from 'redux/reducers/subscriptions'; +import notificationsReducer from 'redux/reducers/notifications'; +import rewardsReducer from 'redux/reducers/rewards'; +import userReducer from 'redux/reducers/user'; export default history => combineReducers({ diff --git a/ui/redux/actions/app.js b/ui/redux/actions/app.js index d603fab4830..39fef1b19db 100644 --- a/ui/redux/actions/app.js +++ b/ui/redux/actions/app.js @@ -11,18 +11,17 @@ import { Lbry, doBalanceSubscribe, doFetchFileInfos, - doError, makeSelectClaimForUri, makeSelectClaimIsMine, doPopulateSharedUserState, doFetchChannelListMine, doClearPublish, doPreferenceGet, - doToast, doClearSupport, selectFollowedTagsList, // SHARED_PREFERENCES, } from 'lbry-redux'; +import { doToast, doError } from 'redux/actions/notifications'; import Native from 'native'; import { doFetchDaemonSettings, @@ -44,7 +43,10 @@ import { selectAllowAnalytics, } from 'redux/selectors/app'; // import { selectDaemonSettings } from 'redux/selectors/settings'; -import { doAuthenticate, doGetSync, doClaimRewardType, rewards as REWARDS } from 'lbryinc'; +import { doGetSync } from 'lbryinc'; +import { doClaimRewardType } from 'redux/actions/rewards'; +import REWARDS from 'rewards'; +import { doAuthenticate } from 'redux/actions/user'; import { lbrySettings as config, version as appVersion } from 'package.json'; import analytics, { SHARE_INTERNAL } from 'analytics'; import { doSignOutCleanup, deleteSavedPassword, getSavedPassword } from 'util/saved-passwords'; diff --git a/ui/redux/actions/notifications.js b/ui/redux/actions/notifications.js new file mode 100644 index 00000000000..78d0ac90b8a --- /dev/null +++ b/ui/redux/actions/notifications.js @@ -0,0 +1,38 @@ +// @flow +import * as ACTIONS from 'constants/action_types'; +import uuid from 'uuid/v4'; + +export function doToast(params: ToastParams) { + if (!params) { + throw Error("'params' object is required to create a toast notification"); + } + + return { + type: ACTIONS.CREATE_TOAST, + data: { + id: uuid(), + params, + }, + }; +} + +export function doDismissToast() { + return { + type: ACTIONS.DISMISS_TOAST, + }; +} + +export function doError(error: string | {}) { + return { + type: ACTIONS.CREATE_ERROR, + data: { + error, + }, + }; +} + +export function doDismissError() { + return { + type: ACTIONS.DISMISS_ERROR, + }; +} diff --git a/ui/redux/actions/publish.js b/ui/redux/actions/publish.js index b5528edb55d..32158b5ccf8 100644 --- a/ui/redux/actions/publish.js +++ b/ui/redux/actions/publish.js @@ -4,13 +4,13 @@ import * as ACTIONS from 'constants/action_types'; import * as PAGES from 'constants/pages'; import { batchActions, - doError, selectMyClaims, doPublish, doCheckPendingPublishes, doCheckReflectingFiles, ACTIONS as LBRY_REDUX_ACTIONS, } from 'lbry-redux'; +import { doError } from 'redux/actions/notifications'; import { selectosNotificationsEnabled } from 'redux/selectors/settings'; import { push } from 'connected-react-router'; import analytics from 'analytics'; diff --git a/ui/redux/actions/rewards.js b/ui/redux/actions/rewards.js new file mode 100644 index 00000000000..915c8c8e102 --- /dev/null +++ b/ui/redux/actions/rewards.js @@ -0,0 +1,178 @@ +import { Lbryio } from 'lbryinc'; +import { ACTIONS, doToast, doUpdateBalance } from 'lbry-redux'; +import { selectUnclaimedRewards } from 'redux/selectors/rewards'; +import { selectUserIsRewardApproved } from 'redux/selectors/user'; +import { doFetchInviteStatus } from 'redux/actions/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 userIsRewardApproved = selectUserIsRewardApproved(state); + const unclaimedRewards = selectUnclaimedRewards(state); + const reward = + rewardType === rewards.TYPE_REWARD_CODE || rewardType === rewards.TYPE_NEW_ANDROID + ? { reward_type: rewards.TYPE_REWARD_CODE } + : unclaimedRewards.find(ur => ur.reward_type === rewardType); + + // Try to claim the email reward right away, even if we haven't called reward_list yet + if ( + rewardType !== rewards.TYPE_REWARD_CODE && + rewardType !== rewards.TYPE_CONFIRM_EMAIL && + rewardType !== rewards.TYPE_DAILY_VIEW && + rewardType !== rewards.TYPE_NEW_ANDROID && + rewardType !== rewards.TYPE_PAID_CONTENT + ) { + if (!reward || reward.transaction_id) { + // already claimed or doesn't exist, do nothing + return; + } + } + + if ( + !userIsRewardApproved && + rewardType !== rewards.TYPE_CONFIRM_EMAIL && + rewardType !== rewards.TYPE_REWARD_CODE && + rewardType !== rewards.TYPE_NEW_ANDROID + ) { + if (!options || (!options.failSilently && rewards.callbacks.rewardApprovalRequested)) { + rewards.callbacks.rewardApprovalRequested(); + } + + return; + } + + // Set `claim_code` so the api knows which reward to give if there are multiple of the same type + const params = options.params || {}; + if (!params.claim_code && reward) { + params.claim_code = reward.claim_code; + } + + dispatch({ + type: ACTIONS.CLAIM_REWARD_STARTED, + data: { reward }, + }); + + const success = successReward => { + // Temporary timeout to ensure the sdk has the correct balance after claiming a reward + setTimeout(() => { + dispatch(doUpdateBalance()).then(() => { + dispatch({ + type: ACTIONS.CLAIM_REWARD_SUCCESS, + data: { + reward: successReward, + }, + }); + if (successReward.reward_type === rewards.TYPE_NEW_USER && rewards.callbacks.claimFirstRewardSuccess) { + rewards.callbacks.claimFirstRewardSuccess(); + } else if (successReward.reward_type === rewards.TYPE_REFERRAL) { + dispatch(doFetchInviteStatus()); + } + + dispatch(doRewardList()); + + if (options.callback) { + options.callback(); + } + }); + }, 2000); + }; + + const failure = error => { + dispatch({ + type: ACTIONS.CLAIM_REWARD_FAILURE, + data: { + reward, + error: !options || !options.failSilently ? error : undefined, + }, + }); + + if (options.notifyError) { + dispatch(doToast({ message: error.message, isError: true })); + } + + if (options.callback) { + options.callback(error); + } + }; + + return rewards.claimReward(rewardType, params).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_DAILY_VIEW].forEach(type => { + dispatch(doClaimRewardType(type, { failSilently: true })); + }); + } + }; +} + +export function doClaimRewardClearError(reward) { + return dispatch => { + dispatch({ + type: ACTIONS.CLAIM_REWARD_CLEAR_ERROR, + data: { reward }, + }); + }; +} + +export function doFetchRewardedContent() { + return dispatch => { + const success = nameToClaimId => { + dispatch({ + type: ACTIONS.FETCH_REWARD_CONTENT_COMPLETED, + data: { + claimIds: Object.values(nameToClaimId), + success: true, + }, + }); + }; + + const failure = () => { + dispatch({ + type: ACTIONS.FETCH_REWARD_CONTENT_COMPLETED, + data: { + claimIds: [], + success: false, + }, + }); + }; + + Lbryio.call('reward', 'list_featured').then(success, failure); + }; +} diff --git a/ui/redux/actions/settings.js b/ui/redux/actions/settings.js index 4caca0163c5..78931d0f4d8 100644 --- a/ui/redux/actions/settings.js +++ b/ui/redux/actions/settings.js @@ -1,4 +1,5 @@ -import { Lbry, ACTIONS, doToast, SHARED_PREFERENCES, doWalletReconnect, SETTINGS } from 'lbry-redux'; +import { Lbry, ACTIONS, SHARED_PREFERENCES, doWalletReconnect, SETTINGS } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; import * as LOCAL_ACTIONS from 'constants/action_types'; import analytics from 'analytics'; import SUPPORTED_LANGUAGES from 'constants/supported_languages'; diff --git a/ui/redux/actions/subscriptions.js b/ui/redux/actions/subscriptions.js index 470db1f39b7..56cfb6404f4 100644 --- a/ui/redux/actions/subscriptions.js +++ b/ui/redux/actions/subscriptions.js @@ -1,6 +1,8 @@ // @flow import * as ACTIONS from 'constants/action_types'; -import { Lbryio, rewards, doClaimRewardType } from 'lbryinc'; +import REWARDS from 'rewards'; +import { Lbryio } from 'lbryinc'; +import { doClaimRewardType } from 'redux/actions/rewards'; import { selectUnreadByChannel } from 'redux/selectors/subscriptions'; import { parseURI } from 'lbry-redux'; @@ -144,7 +146,7 @@ export const doChannelSubscribe = (subscription: Subscription) => (dispatch: Dis claim_id: channelClaimId, }); - dispatch(doClaimRewardType(rewards.TYPE_SUBSCRIPTION, { failSilently: true })); + dispatch(doClaimRewardType(REWARDS.TYPE_SUBSCRIPTION, { failSilently: true })); } }; diff --git a/ui/redux/actions/user.js b/ui/redux/actions/user.js new file mode 100644 index 00000000000..e333b55aa4d --- /dev/null +++ b/ui/redux/actions/user.js @@ -0,0 +1,774 @@ +import { Lbry, doFetchChannelListMine, batchActions, makeSelectClaimForUri, parseURI } from 'lbry-redux'; +import * as ACTIONS from 'constants/action_types'; +import { doClaimRewardType, doRewardList } from 'redux/actions/rewards'; +import { selectEmailToVerify, selectPhoneToVerify, selectUserCountryCode, selectUser } from 'redux/selectors/user'; +import { doToast } from 'redux/actions/notifications'; +import rewards from 'rewards'; +import { Lbryio } from 'lbryinc'; + +export function doFetchInviteStatus(shouldCallRewardList = true) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_INVITE_STATUS_FETCH_STARTED, + }); + + Promise.all([Lbryio.call('user', 'invite_status'), Lbryio.call('user_referral_code', 'list')]) + .then(([status, code]) => { + if (shouldCallRewardList) { + dispatch(doRewardList()); + } + + dispatch({ + type: ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS, + data: { + invitesRemaining: status.invites_remaining ? status.invites_remaining : 0, + invitees: status.invitees, + referralLink: `${Lbryio.CONNECTION_STRING}user/refer?r=${code}`, + referralCode: code, + }, + }); + }) + .catch(error => { + dispatch({ + type: ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE, + data: { error }, + }); + }); + }; +} + +export function doInstallNew(appVersion, os = null, firebaseToken = null, callbackForUsersWhoAreSharingData, domain) { + const payload = { app_version: appVersion, domain }; + if (firebaseToken) { + payload.firebase_token = firebaseToken; + } + + Lbry.status().then(status => { + payload.app_id = + domain && domain !== 'lbry.tv' + ? (domain.replace(/[.]/gi, '') + status.installation_id).slice(0, 66) + : status.installation_id; + payload.node_id = status.lbry_id; + Lbry.version().then(version => { + payload.daemon_version = version.lbrynet_version; + payload.operating_system = os || version.os_system; + payload.platform = version.platform; + Lbryio.call('install', 'new', payload); + + if (callbackForUsersWhoAreSharingData) { + callbackForUsersWhoAreSharingData(status); + } + }); + }); +} + +export function doInstallNewWithParams( + appVersion, + installationId, + nodeId, + lbrynetVersion, + os, + platform, + firebaseToken = null +) { + return () => { + const payload = { app_version: appVersion }; + if (firebaseToken) { + payload.firebase_token = firebaseToken; + } + + payload.app_id = installationId; + payload.node_id = nodeId; + payload.daemon_version = lbrynetVersion; + payload.operating_system = os; + payload.platform = platform; + Lbryio.call('install', 'new', payload); + }; +} + +// TODO: Call doInstallNew separately so we don't have to pass appVersion and os_system params? +export function doAuthenticate( + appVersion, + os = null, + firebaseToken = null, + shareUsageData = true, + callbackForUsersWhoAreSharingData, + callInstall = true, + domain = null +) { + return dispatch => { + dispatch({ + type: ACTIONS.AUTHENTICATION_STARTED, + }); + + Lbryio.authenticate() + .then(user => { + Lbryio.getAuthToken().then(token => { + dispatch({ + type: ACTIONS.AUTHENTICATION_SUCCESS, + data: { user, accessToken: token }, + }); + + if (shareUsageData) { + dispatch(doRewardList()); + dispatch(doFetchInviteStatus(false)); + if (callInstall) { + doInstallNew(appVersion, os, firebaseToken, callbackForUsersWhoAreSharingData, domain); + } + } + }); + }) + .catch(error => { + dispatch({ + type: ACTIONS.AUTHENTICATION_FAILURE, + data: { error }, + }); + }); + }; +} + +export function doUserFetch() { + return dispatch => + new Promise((resolve, reject) => { + dispatch({ + type: ACTIONS.USER_FETCH_STARTED, + }); + + Lbryio.getCurrentUser() + .then(user => { + dispatch({ + type: ACTIONS.USER_FETCH_SUCCESS, + data: { user }, + }); + resolve(user); + }) + .catch(error => { + reject(error); + dispatch({ + type: ACTIONS.USER_FETCH_FAILURE, + data: { error }, + }); + }); + }); +} + +export function doUserCheckEmailVerified() { + // This will happen in the background so we don't need loading booleans + return dispatch => { + Lbryio.getCurrentUser().then(user => { + if (user.has_verified_email) { + dispatch(doRewardList()); + + dispatch({ + type: ACTIONS.USER_FETCH_SUCCESS, + data: { user }, + }); + } + }); + }; +} + +export function doUserPhoneReset() { + return { + type: ACTIONS.USER_PHONE_RESET, + }; +} + +export function doUserPhoneNew(phone, countryCode) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_PHONE_NEW_STARTED, + data: { phone, country_code: countryCode }, + }); + + const success = () => { + dispatch({ + type: ACTIONS.USER_PHONE_NEW_SUCCESS, + data: { phone }, + }); + }; + + const failure = error => { + dispatch({ + type: ACTIONS.USER_PHONE_NEW_FAILURE, + data: { error }, + }); + }; + + Lbryio.call('user', 'phone_number_new', { phone_number: phone, country_code: countryCode }, 'post').then( + success, + failure + ); + }; +} + +export function doUserPhoneVerifyFailure(error) { + return { + type: ACTIONS.USER_PHONE_VERIFY_FAILURE, + data: { error }, + }; +} + +export function doUserPhoneVerify(verificationCode) { + return (dispatch, getState) => { + const phoneNumber = selectPhoneToVerify(getState()); + const countryCode = selectUserCountryCode(getState()); + + dispatch({ + type: ACTIONS.USER_PHONE_VERIFY_STARTED, + code: verificationCode, + }); + + Lbryio.call( + 'user', + 'phone_number_confirm', + { + verification_code: verificationCode, + phone_number: phoneNumber, + country_code: countryCode, + }, + 'post' + ) + .then(user => { + if (user.is_identity_verified) { + dispatch({ + type: ACTIONS.USER_PHONE_VERIFY_SUCCESS, + data: { user }, + }); + dispatch(doClaimRewardType(rewards.TYPE_NEW_USER)); + } + }) + .catch(error => dispatch(doUserPhoneVerifyFailure(error))); + }; +} + +export function doUserEmailToVerify(email) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_EMAIL_VERIFY_SET, + data: { email }, + }); + }; +} + +export function doUserEmailNew(email) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_STARTED, + email, + }); + + const success = () => { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_SUCCESS, + data: { email }, + }); + dispatch(doUserFetch()); + }; + + const failure = error => { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_FAILURE, + data: { error }, + }); + }; + + Lbryio.call('user_email', 'new', { email, send_verification_email: true }, 'post') + .catch(error => { + if (error.response && error.response.status === 409) { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_EXISTS, + }); + + return Lbryio.call('user_email', 'resend_token', { email, only_if_expired: true }, 'post').then( + success, + failure + ); + } + throw error; + }) + .then(success, failure); + }; +} + +export function doUserCheckIfEmailExists(email) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_STARTED, + email, + }); + + const triggerEmailFlow = hasPassword => { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_SUCCESS, + data: { email }, + }); + + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_EXISTS, + }); + + if (hasPassword) { + dispatch({ + type: ACTIONS.USER_PASSWORD_EXISTS, + }); + } else { + // If they don't have a password, they will need to use the email verification api + Lbryio.call('user_email', 'resend_token', { email, only_if_expired: true }, 'post'); + } + }; + + const success = response => { + triggerEmailFlow(response.has_password); + }; + + const failure = error => + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_FAILURE, + data: { error }, + }); + + Lbryio.call('user', 'exists', { email }, 'post') + .catch(error => { + if (error.response && error.response.status === 404) { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_DOES_NOT_EXIST, + }); + } else if (error.response && error.response.status === 412) { + triggerEmailFlow(false); + } + + throw error; + }) + .then(success, failure); + }; +} + +export function doUserSignIn(email, password) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_STARTED, + email, + }); + + const success = () => { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_SUCCESS, + data: { email }, + }); + dispatch(doUserFetch()); + }; + + const failure = error => + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_FAILURE, + data: { error }, + }); + + Lbryio.call('user', 'signin', { email, ...(password ? { password } : {}) }, 'post') + .catch(error => { + if (error.response && error.response.status === 409) { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_EXISTS, + }); + + return Lbryio.call('user_email', 'resend_token', { email, only_if_expired: true }, 'post').then( + success, + failure + ); + } + throw error; + }) + .then(success, failure); + }; +} + +export function doUserSignUp(email, password) { + return dispatch => + new Promise((resolve, reject) => { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_STARTED, + email, + }); + + const success = () => { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_SUCCESS, + data: { email }, + }); + dispatch(doUserFetch()); + resolve(); + }; + + const failure = error => { + if (error.response && error.response.status === 409) { + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_EXISTS, + }); + } + dispatch({ + type: ACTIONS.USER_EMAIL_NEW_FAILURE, + data: { error }, + }); + + reject(error); + }; + + Lbryio.call('user', 'signup', { email, ...(password ? { password } : {}) }, 'post').then(success, failure); + }); +} + +export function doUserPasswordReset(email) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_PASSWORD_RESET_STARTED, + email, + }); + + const success = () => { + dispatch({ + type: ACTIONS.USER_PASSWORD_RESET_SUCCESS, + }); + }; + + const failure = error => + dispatch({ + type: ACTIONS.USER_PASSWORD_RESET_FAILURE, + data: { error }, + }); + + Lbryio.call('user_password', 'reset', { email }, 'post').then(success, failure); + }; +} + +export function doUserPasswordSet(newPassword, oldPassword, authToken) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_PASSWORD_SET_STARTED, + }); + + const success = () => { + dispatch({ + type: ACTIONS.USER_PASSWORD_SET_SUCCESS, + }); + dispatch(doUserFetch()); + }; + + const failure = error => + dispatch({ + type: ACTIONS.USER_PASSWORD_SET_FAILURE, + data: { error }, + }); + + Lbryio.call( + 'user_password', + 'set', + { + new_password: newPassword, + ...(oldPassword ? { old_password: oldPassword } : {}), + ...(authToken ? { auth_token: authToken } : {}), + }, + 'post' + ).then(success, failure); + }; +} + +export function doUserResendVerificationEmail(email) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_EMAIL_VERIFY_RETRY_STARTED, + }); + + const success = () => { + dispatch({ + type: ACTIONS.USER_EMAIL_VERIFY_RETRY_SUCCESS, + }); + }; + + const failure = error => { + dispatch({ + type: ACTIONS.USER_EMAIL_VERIFY_RETRY_FAILURE, + data: { error }, + }); + }; + + Lbryio.call('user_email', 'resend_token', { email }, 'post') + .catch(error => { + if (error.response && error.response.status === 409) { + throw error; + } + }) + .then(success, failure); + }; +} + +export function doClearEmailEntry() { + return { + type: ACTIONS.USER_EMAIL_NEW_CLEAR_ENTRY, + }; +} + +export function doClearPasswordEntry() { + return { + type: ACTIONS.USER_PASSWORD_SET_CLEAR, + }; +} + +export function doUserEmailVerifyFailure(error) { + return { + type: ACTIONS.USER_EMAIL_VERIFY_FAILURE, + data: { error }, + }; +} + +export function doUserEmailVerify(verificationToken, recaptcha) { + return (dispatch, getState) => { + const email = selectEmailToVerify(getState()); + + dispatch({ + type: ACTIONS.USER_EMAIL_VERIFY_STARTED, + code: verificationToken, + recaptcha, + }); + + Lbryio.call( + 'user_email', + 'confirm', + { + verification_token: verificationToken, + email, + recaptcha, + }, + 'post' + ) + .then(userEmail => { + if (userEmail.is_verified) { + dispatch({ + type: ACTIONS.USER_EMAIL_VERIFY_SUCCESS, + data: { email }, + }); + dispatch(doUserFetch()); + } else { + throw new Error('Your email is still not verified.'); // shouldn't happen + } + }) + .catch(error => dispatch(doUserEmailVerifyFailure(error))); + }; +} + +export function doFetchAccessToken() { + return dispatch => { + const success = token => + dispatch({ + type: ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS, + data: { token }, + }); + Lbryio.getAuthToken().then(success); + }; +} + +export function doUserIdentityVerify(stripeToken) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_IDENTITY_VERIFY_STARTED, + token: stripeToken, + }); + + Lbryio.call('user', 'verify_identity', { stripe_token: stripeToken }, 'post') + .then(user => { + if (user.is_identity_verified) { + dispatch({ + type: ACTIONS.USER_IDENTITY_VERIFY_SUCCESS, + data: { user }, + }); + dispatch(doClaimRewardType(rewards.TYPE_NEW_USER)); + } else { + throw new Error('Your identity is still not verified. This should not happen.'); // shouldn't happen + } + }) + .catch(error => { + dispatch({ + type: ACTIONS.USER_IDENTITY_VERIFY_FAILURE, + data: { error: error.toString() }, + }); + }); + }; +} + +export function doUserInviteNew(email) { + return dispatch => { + dispatch({ + type: ACTIONS.USER_INVITE_NEW_STARTED, + }); + + return Lbryio.call('user', 'invite', { email }, 'post') + .then(success => { + dispatch({ + type: ACTIONS.USER_INVITE_NEW_SUCCESS, + data: { email }, + }); + + dispatch( + doToast({ + message: __(`Invite sent to ${email}`), + }) + ); + + dispatch(doFetchInviteStatus()); + return success; + }) + .catch(error => { + dispatch({ + type: ACTIONS.USER_INVITE_NEW_FAILURE, + data: { error }, + }); + }); + }; +} + +export function doUserSetReferrerReset() { + return dispatch => { + dispatch({ + type: ACTIONS.USER_SET_REFERRER_RESET, + }); + }; +} +export function doUserSetReferrer(referrer, shouldClaim) { + return async (dispatch, getState) => { + dispatch({ + type: ACTIONS.USER_SET_REFERRER_STARTED, + }); + let claim; + let referrerCode; + + const { isChannel } = parseURI(referrer); + + if (isChannel) { + const uri = `lbry://${referrer}`; + claim = makeSelectClaimForUri(uri)(getState()); + if (!claim) { + try { + const response = await Lbry.resolve({ urls: [uri] }); + claim = response && response[uri]; + } catch (error) { + dispatch({ + type: ACTIONS.USER_SET_REFERRER_FAILURE, + data: { error }, + }); + } + } + referrerCode = claim && claim.permanent_url && claim.permanent_url.replace('lbry://', ''); + } + + if (!referrerCode) { + referrerCode = referrer; + } + + try { + await Lbryio.call('user', 'referral', { referrer: referrerCode }, 'post'); + dispatch({ + type: ACTIONS.USER_SET_REFERRER_SUCCESS, + }); + if (shouldClaim) { + dispatch(doClaimRewardType(rewards.TYPE_REFEREE)); + dispatch(doUserFetch()); + } else { + dispatch(doUserFetch()); + } + } catch (error) { + dispatch({ + type: ACTIONS.USER_SET_REFERRER_FAILURE, + data: { error }, + }); + } + }; +} + +export function doUserSetCountry(country) { + return (dispatch, getState) => { + const state = getState(); + const user = selectUser(state); + + Lbryio.call('user_country', 'set', { country }).then(() => { + const newUser = { ...user, country }; + dispatch({ + type: ACTIONS.USER_FETCH_SUCCESS, + data: { user: newUser }, + }); + }); + }; +} + +export function doClaimYoutubeChannels() { + return dispatch => { + dispatch({ + type: ACTIONS.USER_YOUTUBE_IMPORT_STARTED, + }); + + let transferResponse; + return Lbry.address_list({ page: 1, page_size: 99999 }) + .then(addressList => addressList.items[0]) + .then(address => + Lbryio.call('yt', 'transfer', { + address: address.address, + public_key: address.pubkey, + }).then(response => { + if (response && response.length) { + transferResponse = response; + return Promise.all( + response.map(channelMeta => { + if (channelMeta && channelMeta.channel && channelMeta.channel.channel_certificate) { + return Lbry.channel_import({ + channel_data: channelMeta.channel.channel_certificate, + }); + } + return null; + }) + ).then(() => { + const actions = [ + { + type: ACTIONS.USER_YOUTUBE_IMPORT_SUCCESS, + data: transferResponse, + }, + ]; + actions.push(doUserFetch()); + actions.push(doFetchChannelListMine()); + dispatch(batchActions(...actions)); + }); + } + }) + ) + .catch(error => { + dispatch({ + type: ACTIONS.USER_YOUTUBE_IMPORT_FAILURE, + data: String(error), + }); + }); + }; +} + +export function doCheckYoutubeTransfer() { + return dispatch => { + dispatch({ + type: ACTIONS.USER_YOUTUBE_IMPORT_STARTED, + }); + + return Lbryio.call('yt', 'transfer') + .then(response => { + if (response && response.length) { + dispatch({ + type: ACTIONS.USER_YOUTUBE_IMPORT_SUCCESS, + data: response, + }); + } else { + throw new Error(); + } + }) + .catch(error => { + dispatch({ + type: ACTIONS.USER_YOUTUBE_IMPORT_FAILURE, + data: String(error), + }); + }); + }; +} diff --git a/ui/redux/reducers/notifications.js b/ui/redux/reducers/notifications.js new file mode 100644 index 00000000000..d0404b0e6e3 --- /dev/null +++ b/ui/redux/reducers/notifications.js @@ -0,0 +1,92 @@ +// @flow +import * as ACTIONS from 'constants/action_types'; +import { handleActions } from 'util/redux-utils'; + +const defaultState: NotificationState = { + notifications: [], + toasts: [], + errors: [], +}; + +export default handleActions( + { + // Toasts + [ACTIONS.CREATE_TOAST]: (state: NotificationState, action: DoToast) => { + const toast: Toast = action.data; + const newToasts: Array = state.toasts.slice(); + newToasts.push(toast); + + return { + ...state, + toasts: newToasts, + }; + }, + [ACTIONS.DISMISS_TOAST]: (state: NotificationState) => { + const newToasts: Array = state.toasts.slice(); + newToasts.shift(); + + return { + ...state, + toasts: newToasts, + }; + }, + + // Notifications + [ACTIONS.CREATE_NOTIFICATION]: (state: NotificationState, action: DoNotification) => { + const notification: Notification = action.data; + const newNotifications: Array = state.notifications.slice(); + newNotifications.push(notification); + + return { + ...state, + notifications: newNotifications, + }; + }, + // Used to mark notifications as read/dismissed + [ACTIONS.EDIT_NOTIFICATION]: (state: NotificationState, action: DoEditNotification) => { + const { notification } = action.data; + let notifications: Array = state.notifications.slice(); + + notifications = notifications.map(pastNotification => + pastNotification.id === notification.id ? notification : pastNotification + ); + + return { + ...state, + notifications, + }; + }, + [ACTIONS.DELETE_NOTIFICATION]: (state: NotificationState, action: DoDeleteNotification) => { + const { id } = action.data; + let newNotifications: Array = state.notifications.slice(); + newNotifications = newNotifications.filter(notification => notification.id !== id); + + return { + ...state, + notifications: newNotifications, + }; + }, + + // Errors + [ACTIONS.CREATE_ERROR]: (state: NotificationState, action: DoError) => { + const error: ErrorNotification = action.data; + const newErrors: Array = state.errors.slice(); + newErrors.push(error); + + return { + ...state, + errors: newErrors, + }; + }, + [ACTIONS.DISMISS_ERROR]: (state: NotificationState) => { + const newErrors: Array = state.errors.slice(); + newErrors.shift(); + + return { + ...state, + errors: newErrors, + }; + }, + }, + defaultState +); diff --git a/ui/redux/reducers/rewards.js b/ui/redux/reducers/rewards.js new file mode 100644 index 00000000000..4f1b7bfddf4 --- /dev/null +++ b/ui/redux/reducers/rewards.js @@ -0,0 +1,112 @@ +import { ACTIONS } from 'lbry-redux'; + +const reducers = {}; +const defaultState = { + fetching: false, + claimedRewardsById: {}, // id => reward + unclaimedRewards: [], + claimPendingByType: {}, + claimErrorsByType: {}, + rewardedContentClaimIds: [], +}; + +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); + + // Currently, for multiple rewards of the same type, they will both show "claiming" when one is beacuse we track this by `reward_type` + // To fix this we will need to use `claim_code` instead, and change all selectors to match + if (reward) { + 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.claim_code === reward.claim_code); + 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], ''); +}; + +reducers[ACTIONS.FETCH_REWARD_CONTENT_COMPLETED] = (state, action) => { + const { claimIds } = action.data; + + return Object.assign({}, state, { + rewardedContentClaimIds: claimIds, + }); +}; + +export default function rewardsReducer(state = defaultState, action) { + const handler = reducers[action.type]; + if (handler) return handler(state, action); + return state; +} diff --git a/ui/redux/reducers/user.js b/ui/redux/reducers/user.js new file mode 100644 index 00000000000..c9356079663 --- /dev/null +++ b/ui/redux/reducers/user.js @@ -0,0 +1,379 @@ +import * as ACTIONS from 'constants/action_types'; + +const reducers = {}; + +const defaultState = { + authenticationIsPending: false, + userIsPending: false, + emailNewIsPending: false, + emailNewErrorMessage: '', + emailToVerify: '', + emailAlreadyExists: false, + emailDoesNotExist: false, + resendingVerificationEmail: false, + passwordResetPending: false, + passwordResetSuccess: false, + passwordResetError: undefined, + passwordSetPending: false, + passwordSetSuccess: false, + passwordSetError: undefined, + inviteNewErrorMessage: '', + inviteNewIsPending: false, + inviteStatusIsPending: false, + invitesRemaining: undefined, + invitees: undefined, + referralLink: undefined, + referralCode: undefined, + user: undefined, + accessToken: undefined, + youtubeChannelImportPending: false, + youtubeChannelImportErrorMessage: '', + referrerSetIsPending: false, + referrerSetError: '', +}; + +reducers[ACTIONS.AUTHENTICATION_STARTED] = state => + Object.assign({}, state, { + authenticationIsPending: true, + userIsPending: true, + accessToken: defaultState.accessToken, + }); + +reducers[ACTIONS.AUTHENTICATION_SUCCESS] = (state, action) => + Object.assign({}, state, { + authenticationIsPending: false, + userIsPending: false, + accessToken: action.data.accessToken, + 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, + }); + +reducers[ACTIONS.USER_FETCH_SUCCESS] = (state, action) => + Object.assign({}, state, { + userIsPending: false, + user: action.data.user, + emailToVerify: action.data.user.has_verified_email ? null : state.emailToVerify, + }); + +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: '', + emailAlreadyExists: false, + emailDoesNotExist: false, + }); + +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 => + Object.assign({}, state, { + emailAlreadyExists: true, + }); + +reducers[ACTIONS.USER_EMAIL_NEW_DOES_NOT_EXIST] = state => + Object.assign({}, state, { + emailDoesNotExist: true, + }); + +reducers[ACTIONS.USER_EMAIL_NEW_FAILURE] = (state, action) => + Object.assign({}, state, { + emailNewIsPending: false, + emailNewErrorMessage: action.data.error, + }); + +reducers[ACTIONS.USER_EMAIL_NEW_CLEAR_ENTRY] = state => { + const newUser = { ...state.user }; + delete newUser.primary_email; + + return Object.assign({}, state, { + emailNewErrorMessage: null, + emailAlreadyExists: false, + emailDoesNotExist: false, + passwordExistsForUser: false, + emailToVerify: null, + user: newUser, + }); +}; + +reducers[ACTIONS.USER_PASSWORD_SET_CLEAR] = state => + Object.assign({}, state, { + passwordResetSuccess: false, + passwordResetPending: false, + passwordResetError: null, + }); + +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_EMAIL_VERIFY_SET] = (state, action) => + Object.assign({}, state, { + emailToVerify: action.data.email, + }); + +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, + referralLink: action.data.referralLink, + referralCode: action.data.referralCode, + }); + +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, + }); + +reducers[ACTIONS.USER_YOUTUBE_IMPORT_STARTED] = state => + Object.assign({}, state, { + youtubeChannelImportPending: true, + youtubeChannelImportErrorMessage: '', + }); + +reducers[ACTIONS.USER_YOUTUBE_IMPORT_SUCCESS] = (state, action) => { + const total = action.data.reduce((acc, value) => acc + value.total_published_videos, 0); + + const complete = action.data.reduce((acc, value) => acc + value.total_transferred, 0); + + return Object.assign({}, state, { + youtubeChannelImportPending: false, + youtubeChannelImportErrorMessage: '', + youtubeChannelImportTotal: total, + youtubeChannelImportComplete: complete, + }); +}; + +reducers[ACTIONS.USER_YOUTUBE_IMPORT_FAILURE] = (state, action) => + Object.assign({}, state, { + youtubeChannelImportPending: false, + youtubeChannelImportErrorMessage: action.data, + }); + +reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_STARTED] = state => + Object.assign({}, state, { + resendingVerificationEmail: true, + }); + +reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_SUCCESS] = state => + Object.assign({}, state, { + resendingVerificationEmail: false, + }); + +reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_FAILURE] = state => + Object.assign({}, state, { + resendingVerificationEmail: false, + }); + +reducers[ACTIONS.USER_SET_REFERRER_STARTED] = state => + Object.assign({}, state, { + referrerSetIsPending: true, + referrerSetError: defaultState.referrerSetError, + }); + +reducers[ACTIONS.USER_SET_REFERRER_SUCCESS] = state => + Object.assign({}, state, { + referrerSetIsPending: false, + referrerSetError: defaultState.referrerSetError, + }); + +reducers[ACTIONS.USER_SET_REFERRER_FAILURE] = (state, action) => + Object.assign({}, state, { + referrerSetIsPending: false, + referrerSetError: action.data.error.message, + }); + +reducers[ACTIONS.USER_SET_REFERRER_RESET] = state => + Object.assign({}, state, { + referrerSetIsPending: false, + referrerSetError: defaultState.referrerSetError, + }); + +reducers[ACTIONS.USER_PASSWORD_EXISTS] = state => + Object.assign({}, state, { + passwordExistsForUser: true, + }); + +reducers[ACTIONS.USER_PASSWORD_RESET_STARTED] = state => + Object.assign({}, state, { + passwordResetPending: true, + passwordResetSuccess: defaultState.passwordResetSuccess, + passwordResetError: null, + }); + +reducers[ACTIONS.USER_PASSWORD_RESET_SUCCESS] = state => + Object.assign({}, state, { + passwordResetPending: false, + passwordResetSuccess: true, + }); + +reducers[ACTIONS.USER_PASSWORD_RESET_FAILURE] = (state, action) => + Object.assign({}, state, { + passwordResetPending: false, + passwordResetError: action.data.error, + }); + +reducers[ACTIONS.USER_PASSWORD_SET_STARTED] = state => + Object.assign({}, state, { + passwordSetPending: true, + passwordSetSuccess: defaultState.passwordSetSuccess, + }); + +reducers[ACTIONS.USER_PASSWORD_SET_SUCCESS] = state => + Object.assign({}, state, { + passwordSetPending: false, + passwordSetSuccess: true, + }); + +reducers[ACTIONS.USER_PASSWORD_SET_FAILURE] = (state, action) => + Object.assign({}, state, { + passwordSetPending: false, + passwordSetError: action.data.error, + }); + +export default function userReducer(state = defaultState, action) { + const handler = reducers[action.type]; + if (handler) return handler(state, action); + return state; +} diff --git a/ui/redux/selectors/notifications.js b/ui/redux/selectors/notifications.js new file mode 100644 index 00000000000..8de3a1488f5 --- /dev/null +++ b/ui/redux/selectors/notifications.js @@ -0,0 +1,26 @@ +import { createSelector } from 'reselect'; + +export const selectState = state => state.notifications || {}; + +export const selectToast = createSelector(selectState, state => { + if (state.toasts.length) { + const { id, params } = state.toasts[0]; + return { + id, + ...params, + }; + } + + return null; +}); + +export const selectError = createSelector(selectState, state => { + if (state.errors.length) { + const { error } = state.errors[0]; + return { + error, + }; + } + + return null; +}); diff --git a/ui/redux/selectors/rewards.js b/ui/redux/selectors/rewards.js new file mode 100644 index 00000000000..9d3893ca164 --- /dev/null +++ b/ui/redux/selectors/rewards.js @@ -0,0 +1,59 @@ +import { createSelector } from 'reselect'; +import REWARDS from 'rewards'; + +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); + +const selectRewardByClaimCode = (state, claimCode) => + selectUnclaimedRewards(state).find(reward => reward.claim_code === claimCode); + +export const makeSelectRewardByClaimCode = () => createSelector(selectRewardByClaimCode, reward => reward); + +export const makeSelectRewardAmountByType = () => + createSelector(selectRewardByType, reward => (reward ? reward.reward_amount : 0)); + +export const selectRewardContentClaimIds = createSelector(selectState, state => state.rewardedContentClaimIds); + +export const selectReferralReward = createSelector( + selectUnclaimedRewards, + unclaimedRewards => unclaimedRewards.filter(reward => reward.reward_type === REWARDS.TYPE_REFERRAL)[0] +); diff --git a/ui/redux/selectors/user.js b/ui/redux/selectors/user.js new file mode 100644 index 00000000000..5d8ed3b584e --- /dev/null +++ b/ui/redux/selectors/user.js @@ -0,0 +1,129 @@ +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 selectEmailAlreadyExists = createSelector(selectState, state => state.emailAlreadyExists); + +export const selectEmailDoesNotExist = createSelector(selectState, state => state.emailDoesNotExist); + +export const selectResendingVerificationEmail = createSelector(selectState, state => state.resendingVerificationEmail); + +export const selectUserEmail = createSelector(selectUser, user => + user ? user.primary_email || user.latest_claimed_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 selectYoutubeChannels = createSelector(selectUser, user => (user ? user.youtube_channels : null)); + +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 => { + const error = state.emailNewErrorMessage; + return typeof error === 'object' && error !== null ? error.message : error; +}); + +export const selectPasswordExists = createSelector(selectState, state => state.passwordExistsForUser); + +export const selectPasswordResetIsPending = createSelector(selectState, state => state.passwordResetPending); + +export const selectPasswordResetSuccess = createSelector(selectState, state => state.passwordResetSuccess); + +export const selectPasswordResetError = createSelector(selectState, state => { + const error = state.passwordResetError; + return typeof error === 'object' && error !== null ? error.message : error; +}); + +export const selectPasswordSetIsPending = createSelector(selectState, state => state.passwordSetPending); + +export const selectPasswordSetSuccess = createSelector(selectState, state => state.passwordSetSuccess); + +export const selectPasswordSetError = createSelector(selectState, state => { + const error = state.passwordSetError; + return typeof error === 'object' && error !== null ? error.message : error; +}); + +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 selectPhoneNewIsPending = createSelector(selectState, state => state.phoneNewIsPending); + +export const selectPhoneVerifyIsPending = createSelector(selectState, state => state.phoneVerifyIsPending); + +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 selectUserVerifiedEmail = createSelector(selectUser, user => user && user.has_verified_email); + +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); + +export const selectUserInviteReferralLink = createSelector(selectState, state => state.referralLink); + +export const selectUserInviteReferralCode = createSelector(selectState, state => + state.referralCode ? state.referralCode[0] : '' +); + +export const selectYouTubeImportPending = createSelector(selectState, state => state.youtubeChannelImportPending); + +export const selectYouTubeImportError = createSelector(selectState, state => state.youtubeChannelImportErrorMessage); + +export const selectSetReferrerPending = createSelector(selectState, state => state.referrerSetIsPending); + +export const selectSetReferrerError = createSelector(selectState, state => state.referrerSetError); + +export const selectYouTubeImportVideosComplete = createSelector(selectState, state => { + const total = state.youtubeChannelImportTotal; + const complete = state.youtubeChannelImportComplete || 0; + + if (total) { + return [complete, total]; + } +}); diff --git a/ui/rewards.js b/ui/rewards.js new file mode 100644 index 00000000000..e5d7ee71563 --- /dev/null +++ b/ui/rewards.js @@ -0,0 +1,127 @@ +import { Lbry } from 'lbry-redux'; +import { doToast } from 'redux/actions/notifications'; +import { Lbryio } from 'lbryinc'; + +const rewards = {}; + +rewards.TYPE_NEW_DEVELOPER = 'new_developer'; +rewards.TYPE_NEW_USER = 'new_user'; +rewards.TYPE_CONFIRM_EMAIL = 'email_provided'; +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_REFERRAL = 'referrer'; +rewards.TYPE_REFEREE = 'referee'; +rewards.TYPE_REWARD_CODE = 'reward_code'; +rewards.TYPE_SUBSCRIPTION = 'subscription'; +rewards.YOUTUBE_CREATOR = 'youtube_creator'; +rewards.TYPE_DAILY_VIEW = 'daily_view'; +rewards.TYPE_NEW_ANDROID = 'new_android'; +rewards.TYPE_PAID_CONTENT = 'paid_content'; + +rewards.claimReward = (type, rewardParams) => { + function requestReward(resolve, reject, params) { + if (!Lbryio.enabled) { + reject(new Error(__('Rewards are not enabled.'))); + return; + } + + Lbryio.call('reward', 'claim', params, 'post').then(reward => { + const message = reward.reward_notification || `You have claimed a ${reward.reward_amount} LBC reward.`; + + // Display global notice + const action = doToast({ + message, + linkText: __('Show All'), + linkTarget: '/rewards', + }); + window.store.dispatch(action); + + if (rewards.callbacks.claimRewardSuccess) { + rewards.callbacks.claimRewardSuccess(); + } + + resolve(reward); + }, reject); + } + + return new Promise((resolve, reject) => { + Lbry.address_unused().then(address => { + const params = { + reward_type: type, + wallet_address: address, + ...rewardParams, + }; + + switch (type) { + case rewards.TYPE_FIRST_CHANNEL: + Lbry.channel_list({ page: 1, page_size: 10 }) + .then(claims => { + const claim = + claims.items && + claims.items.find( + foundClaim => + foundClaim.name.length && + foundClaim.name[0] === '@' && + foundClaim.txid.length && + foundClaim.type === '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.stream_list({ page: 1, page_size: 10 }) + .then(claims => { + const claim = + claims.items && + claims.items.find( + foundClaim => + foundClaim.name.length && + foundClaim.name[0] !== '@' && + foundClaim.txid.length && + foundClaim.type === '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); + } + }); + }); +}; +rewards.callbacks = { + // Set any callbacks that require code not found in this project + claimRewardSuccess: null, + claimFirstRewardSuccess: null, + rewardApprovalRequired: null, +}; + +rewards.setCallback = (name, method) => { + rewards.callbacks[name] = method; +}; + +export default rewards; diff --git a/ui/scss/component/_form-field.scss b/ui/scss/component/_form-field.scss index 899e9368da7..3d48d6ed801 100644 --- a/ui/scss/component/_form-field.scss +++ b/ui/scss/component/_form-field.scss @@ -12,7 +12,7 @@ input-submit { } input[type='number'] { - width: 5rem; + width: 8rem; } fieldset-group { diff --git a/ui/store.js b/ui/store.js index 2ec932c27e8..927f8bbe2bf 100644 --- a/ui/store.js +++ b/ui/store.js @@ -10,7 +10,8 @@ import { createMemoryHistory, createBrowserHistory } from 'history'; import { routerMiddleware } from 'connected-react-router'; import createRootReducer from './reducers'; import { Lbry, buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS, SETTINGS } from 'lbry-redux'; -import { LBRYINC_ACTIONS, doGetSync, selectUserVerifiedEmail } from 'lbryinc'; +import { doGetSync } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { getSavedPassword, getAuthToken } from 'util/saved-passwords'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import { generateInitialUrl } from 'util/url'; @@ -163,7 +164,7 @@ const sharedStateCb = ({ dispatch, getState }) => { const populateAuthTokenHeader = () => { return next => action => { if ( - (action.type === LBRYINC_ACTIONS.USER_FETCH_SUCCESS || action.type === LBRYINC_ACTIONS.AUTHENTICATION_SUCCESS) && + (action.type === ACTIONS.USER_FETCH_SUCCESS || action.type === ACTIONS.AUTHENTICATION_SUCCESS) && action.data.user.has_verified_email === true ) { const authToken = getAuthToken(); diff --git a/web/component/fileViewerEmbeddedEnded/index.js b/web/component/fileViewerEmbeddedEnded/index.js index 1598053811b..b80fcc9f43c 100644 --- a/web/component/fileViewerEmbeddedEnded/index.js +++ b/web/component/fileViewerEmbeddedEnded/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import fileViewerEmbeddedEnded from './view'; -import { selectUserVerifiedEmail } from 'lbryinc'; +import { selectUserVerifiedEmail } from 'redux/selectors/user'; export default connect(state => ({ isAuthenticated: selectUserVerifiedEmail(state), diff --git a/web/component/openInAppLink/index.js b/web/component/openInAppLink/index.js index 81290681c87..15674aa8e32 100644 --- a/web/component/openInAppLink/index.js +++ b/web/component/openInAppLink/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectUser } from 'lbryinc'; +import { selectUser } from 'redux/selectors/user'; import OpenInAppLink from './view'; const select = state => ({ diff --git a/web/component/youtubeReferralWelcome/index.js b/web/component/youtubeReferralWelcome/index.js index 9d807e2dd9e..d2cb2c7db05 100644 --- a/web/component/youtubeReferralWelcome/index.js +++ b/web/component/youtubeReferralWelcome/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { doOpenModal } from 'redux/actions/app'; -import { selectUser } from 'lbryinc'; +import { selectUser } from 'redux/selectors/user'; import YoutubeWelcome from './view'; const select = state => ({ diff --git a/yarn.lock b/yarn.lock index 7a03b273496..cea80fd1556 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6215,9 +6215,9 @@ lbry-redux@lbryio/lbry-redux#f8ac5359d9d05fba2c3a536003a9d4c64b86c9f0: reselect "^3.0.0" uuid "^3.3.2" -lbryinc@lbryio/lbryinc#3ceb09549cb5ec22927ce3bea44ae8dbe2e4a006: +lbryinc@lbryio/lbryinc#72eee35f5181940eb4a468a27ddb2a2a4e362fb0: version "0.0.1" - resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/3ceb09549cb5ec22927ce3bea44ae8dbe2e4a006" + resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/72eee35f5181940eb4a468a27ddb2a2a4e362fb0" dependencies: reselect "^3.0.0"