Changes needed for additional youtube transfer UI #62

Merged
neb-b merged 4 commits from onboarding into master 2019-10-07 04:40:02 +02:00
13 changed files with 1269 additions and 761 deletions

View file

@ -22,6 +22,7 @@
"__": true
},
"rules": {
"consistent-return": 0,
"import/no-commonjs": "warn",
"import/no-amd": "warn",
"import/prefer-default-export": "ignore",

516
dist/bundle.es.js vendored

File diff suppressed because it is too large Load diff

1177
dist/bundle.js vendored

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,43 @@
// Auth Token
// User
export const GENERATE_AUTH_TOKEN_FAILURE = 'GENERATE_AUTH_TOKEN_FAILURE';
export const GENERATE_AUTH_TOKEN_STARTED = 'GENERATE_AUTH_TOKEN_STARTED';
export const GENERATE_AUTH_TOKEN_SUCCESS = 'GENERATE_AUTH_TOKEN_SUCCESS';
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
export const AUTHENTICATION_SUCCESS = 'AUTHENTICATION_SUCCESS';
neb-b commented 2019-10-01 05:19:42 +02:00 (Migrated from github.com)
Review

We were importing all of these from lbry-redux. Not sure why

We were importing all of these from `lbry-redux`. Not sure why
export const AUTHENTICATION_FAILURE = 'AUTHENTICATION_FAILURE';
export const USER_EMAIL_DECLINE = 'USER_EMAIL_DECLINE';
export const USER_EMAIL_NEW_STARTED = 'USER_EMAIL_NEW_STARTED';
export const USER_EMAIL_NEW_SUCCESS = 'USER_EMAIL_NEW_SUCCESS';
export const USER_EMAIL_NEW_EXISTS = 'USER_EMAIL_NEW_EXISTS';
export const USER_EMAIL_NEW_FAILURE = 'USER_EMAIL_NEW_FAILURE';
export const USER_EMAIL_VERIFY_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_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';
export const USER_PHONE_NEW_FAILURE = 'USER_PHONE_NEW_FAILURE';
export const USER_PHONE_VERIFY_STARTED = 'USER_PHONE_VERIFY_STARTED';
export const USER_PHONE_VERIFY_SUCCESS = 'USER_PHONE_VERIFY_SUCCESS';
export const USER_PHONE_VERIFY_FAILURE = 'USER_PHONE_VERIFY_FAILURE';
export const USER_IDENTITY_VERIFY_STARTED = 'USER_IDENTITY_VERIFY_STARTED';
export const USER_IDENTITY_VERIFY_SUCCESS = 'USER_IDENTITY_VERIFY_SUCCESS';
export const USER_IDENTITY_VERIFY_FAILURE = 'USER_IDENTITY_VERIFY_FAILURE';
export const USER_FETCH_STARTED = 'USER_FETCH_STARTED';
export const USER_FETCH_SUCCESS = 'USER_FETCH_SUCCESS';
export const USER_FETCH_FAILURE = 'USER_FETCH_FAILURE';
export const USER_INVITE_STATUS_FETCH_STARTED = 'USER_INVITE_STATUS_FETCH_STARTED';
export const USER_INVITE_STATUS_FETCH_SUCCESS = 'USER_INVITE_STATUS_FETCH_SUCCESS';
export const USER_INVITE_STATUS_FETCH_FAILURE = 'USER_INVITE_STATUS_FETCH_FAILURE';
export const USER_INVITE_NEW_STARTED = 'USER_INVITE_NEW_STARTED';
export const USER_INVITE_NEW_SUCCESS = 'USER_INVITE_NEW_SUCCESS';
export const USER_INVITE_NEW_FAILURE = 'USER_INVITE_NEW_FAILURE';
export const FETCH_ACCESS_TOKEN_SUCCESS = 'FETCH_ACCESS_TOKEN_SUCCESS';
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';
// Claims
export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';
@ -82,6 +118,7 @@ export const FETCH_SUB_COUNT_COMPLETED = 'FETCH_SUB_COUNT_COMPLETED';
// Cross-device Sync
export const GET_SYNC_STARTED = 'GET_SYNC_STARTED';
export const GET_SYNC_COMPLETED = 'GET_SYNC_COMPLETED';
export const GET_SYNC_FAILED = 'GET_SYNC_FAILED';
export const SET_SYNC_STARTED = 'SET_SYNC_STARTED';
export const SET_SYNC_FAILED = 'SET_SYNC_FAILED';
export const SET_SYNC_COMPLETED = 'SET_SYNC_COMPLETED';
@ -89,3 +126,4 @@ export const SET_DEFAULT_ACCOUNT = 'SET_DEFAULT_ACCOUNT';
export const SYNC_APPLY_STARTED = 'SYNC_APPLY_STARTED';
export const SYNC_APPLY_COMPLETED = 'SYNC_APPLY_COMPLETED';
export const SYNC_APPLY_FAILED = 'SYNC_APPLY_FAILED';
export const SYNC_RESET = 'SYNC_RESET';

3
src/constants/youtube.js Normal file
View file

@ -0,0 +1,3 @@
export const NOT_TRANSFERRED = 'not_transferred';
export const PENDING_TRANSFER = 'pending_transfer';
export const COMPLETED_TRANSFER = 'completed_transfer';

View file

@ -1,4 +1,5 @@
import * as LBRYINC_ACTIONS from 'constants/action_types';
import * as YOUTUBE_STATUSES from 'constants/youtube';
import Lbryio from 'lbryio';
import rewards from 'rewards';
import subscriptionsReducer from 'redux/reducers/subscriptions';
@ -7,7 +8,7 @@ import subscriptionsReducer from 'redux/reducers/subscriptions';
export { userStateSyncMiddleware } from 'redux/middleware/sync';
// constants
export { LBRYINC_ACTIONS };
export { LBRYINC_ACTIONS, YOUTUBE_STATUSES };
// Lbryio and rewards
export { Lbryio, rewards };
@ -58,6 +59,7 @@ export {
doUserIdentityVerify,
doUserInviteNew,
doClaimYoutubeChannels,
doCheckYoutubeTransfer,
} from 'redux/actions/user';
export { doFetchCostInfoForUri } from 'redux/actions/cost_info';
export { doBlackListedOutpointsSubscribe } from 'redux/actions/blacklist';
@ -70,6 +72,7 @@ export {
doSetSync,
doSetDefaultAccount,
doSyncApply,
doResetSync,
} from 'redux/actions/sync';
// reducers
@ -153,8 +156,9 @@ export {
selectUserInviteReferralLink,
selectUserVerifiedEmail,
selectYoutubeChannels,
selectYTImportPending,
selectYTImportError,
selectYouTubeImportPending,
selectYouTubeImportError,
selectYouTubeImportVideosComplete,
} from 'redux/selectors/user';
export {
makeSelectFetchingCostInfoForUri,
@ -176,6 +180,7 @@ export {
selectSyncData,
selectSyncHash,
selectSetSyncErrorMessage,
selectGetSyncErrorMessage,
selectGetSyncIsPending,
selectSetSyncIsPending,
selectSyncApplyIsPending,

View file

@ -1,5 +1,5 @@
import Lbryio from 'lbryio';
import { ACTIONS, doToast } from 'lbry-redux';
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';
@ -71,26 +71,31 @@ export function doClaimRewardType(rewardType, options = {}) {
});
const success = successReward => {
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());
}
// 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());
dispatch(doRewardList());
if (options.callback) {
options.callback();
}
if (options.callback) {
options.callback();
}
});
}, 1000);
};
const failure = error => {
@ -111,7 +116,7 @@ export function doClaimRewardType(rewardType, options = {}) {
}
};
rewards.claimReward(rewardType, params).then(success, failure);
return rewards.claimReward(rewardType, params).then(success, failure);
};
}

View file

@ -1,35 +1,6 @@
import * as ACTIONS from 'constants/action_types';
import Lbryio from 'lbryio';
import { Lbry } from 'lbry-redux';
export function doSetSync(oldHash, newHash, data) {
return dispatch => {
dispatch({
type: ACTIONS.SET_SYNC_STARTED,
});
Lbryio.call('sync', 'set', { old_hash: oldHash, new_hash: newHash, data }, 'post')
.then(response => {
if (!response.hash) {
return dispatch({
type: ACTIONS.SET_SYNC_FAILED,
data: { error: 'No hash returned for sync/set.' },
});
}
return dispatch({
type: ACTIONS.SET_SYNC_COMPLETED,
data: { syncHash: response.hash },
});
})
.catch(error => {
dispatch({
type: ACTIONS.SET_SYNC_FAILED,
data: { error },
});
});
};
}
import { Lbry, doFetchChannelListMine } from 'lbry-redux';
export function doSetDefaultAccount(success, failure) {
return dispatch => {
@ -67,11 +38,9 @@ export function doSetDefaultAccount(success, failure) {
failure(err);
}
});
} else {
} else if (failure) {
// no default account to set
if (failure) {
failure('Could not set a default account'); // fail
}
failure('Could not set a default account'); // fail
}
})
.catch(err => {
@ -82,26 +51,67 @@ export function doSetDefaultAccount(success, failure) {
};
}
export function doGetSync(password) {
export function doSetSync(oldHash, newHash, data) {
return dispatch => {
dispatch({
type: ACTIONS.SET_SYNC_STARTED,
});
return Lbryio.call('sync', 'set', { old_hash: oldHash, new_hash: newHash, data }, 'post')
.then(response => {
if (!response.hash) {
throw Error('No hash returned for sync/set.');
}
return dispatch({
type: ACTIONS.SET_SYNC_COMPLETED,
data: { syncHash: response.hash },
});
})
.catch(error => {
dispatch({
type: ACTIONS.SET_SYNC_FAILED,
data: { error },
});
});
};
}
export function doGetSync(password = '', shouldSetDefaultAccount) {
return dispatch => {
dispatch({
type: ACTIONS.GET_SYNC_STARTED,
});
const data = {};
Lbry.sync_hash().then(hash => {
Lbryio.call('sync', 'get', { hash }, 'post')
.then(response => {
const data = { hasSyncedWallet: true };
if (response.changed) {
const syncHash = response.hash;
data.syncHash = syncHash;
data.syncData = response.data;
Lbry.sync_apply({ password, data: response.data }).then(
const syncHash = response.hash;
data.syncHash = syncHash;
data.syncData = response.data;
data.hasSyncedWallet = true;
if (response.changed || shouldSetDefaultAccount) {
return Lbry.sync_apply({ password, data: response.data }).then(
({ hash: walletHash, data: walletData }) => {
if (walletHash !== syncHash) {
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
if (walletHash !== syncHash || shouldSetDefaultAccount) {
// different local hash, need to synchronise
dispatch(doSetSync(syncHash, walletHash, walletData));
if (shouldSetDefaultAccount) {
dispatch(
doSetDefaultAccount(() => {
Lbry.status().then(status => {
if (status.wallet.is_locked) {
Lbry.account_unlock({ password });
}
dispatch(doFetchChannelListMine());
});
})
);
}
}
}
);
@ -110,17 +120,26 @@ export function doGetSync(password) {
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
})
.catch(() => {
// user doesn't have a synced wallet
dispatch({
type: ACTIONS.GET_SYNC_COMPLETED,
data: { hasSyncedWallet: false, syncHash: null },
});
if (data.hasSyncedWallet) {
dispatch({
type: ACTIONS.GET_SYNC_FAILED,
data: {
error: 'Error getting synced wallet',
},
});
} else {
// user doesn't have a synced wallet
dispatch({
type: ACTIONS.GET_SYNC_COMPLETED,
data: { hasSyncedWallet: false, syncHash: null },
});
// call sync_apply to get data to sync
// first time sync. use any string for old hash
Lbry.sync_apply({ password }).then(({ hash: walletHash, data }) =>
dispatch(doSetSync('', walletHash, data))
);
// call sync_apply to get data to sync
// first time sync. use any string for old hash
Lbry.sync_apply({ password }).then(({ hash: walletHash, data: syncApplyData }) =>
dispatch(doSetSync('', walletHash, syncApplyData, password))
);
}
});
});
};
@ -182,3 +201,11 @@ export function doCheckSync() {
});
};
}
export function doResetSync() {
return dispatch =>
new Promise(resolve => {
dispatch({ type: ACTIONS.SYNC_RESET });
resolve();
});
}

View file

@ -1,4 +1,5 @@
import { ACTIONS, Lbry, doToast, doFetchChannelListMine, batchActions } from 'lbry-redux';
import { Lbry, doToast, doFetchChannelListMine, batchActions } from 'lbry-redux';
import * as ACTIONS from 'constants/action_types';
import { doClaimRewardType, doRewardList } from 'redux/actions/rewards';
import {
selectEmailToVerify,
@ -84,9 +85,7 @@ export function doUserFetch() {
});
Lbryio.getCurrentUser()
.then(user => {
// analytics.setUser(user);
dispatch(doRewardList());
dispatch({
type: ACTIONS.USER_FETCH_SUCCESS,
data: { user },
@ -388,15 +387,18 @@ export function doClaimYoutubeChannels() {
dispatch({
type: ACTIONS.USER_YOUTUBE_IMPORT_STARTED,
});
Lbry.address_list()
let transferResponse;
return Lbry.address_list()
.then(addressList => addressList.sort((a, b) => a.used_times - b.used_times)[0])
.then(address =>
Lbryio.call('yt', 'transfer', {
address: address.address,
public_key: address.pubkey,
}).then(response => {
if (response && response.success) {
Promise.all(
if (response && response.length) {
transferResponse = response;
return Promise.all(
response.map(channelMeta => {
if (channelMeta && channelMeta.channel && channelMeta.channel.channel_certificate) {
return Lbry.channel_import({
@ -408,7 +410,8 @@ export function doClaimYoutubeChannels() {
).then(() => {
const actions = [
{
type: ACTIONS.USER_YOUTUBE_IMPORT_COMPLETED,
type: ACTIONS.USER_YOUTUBE_IMPORT_SUCCESS,
data: transferResponse,
},
];
actions.push(doUserFetch());
@ -426,3 +429,29 @@ export function doClaimYoutubeChannels() {
});
};
}
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),
});
});
};
}

View file

@ -6,6 +6,7 @@ const defaultState = {
syncHash: null,
syncData: null,
setSyncErrorMessage: null,
getSyncErrorMessage: null,
syncApplyErrorMessage: '',
syncApplyIsPending: false,
getSyncIsPending: false,
@ -16,6 +17,7 @@ const defaultState = {
reducers[ACTIONS.GET_SYNC_STARTED] = state =>
Object.assign({}, state, {
getSyncIsPending: true,
getSyncErrorMessage: null,
});
reducers[ACTIONS.GET_SYNC_COMPLETED] = (state, action) =>
@ -27,6 +29,12 @@ reducers[ACTIONS.GET_SYNC_COMPLETED] = (state, action) =>
hashChanged: action.data.hashChanged,
});
reducers[ACTIONS.GET_SYNC_FAILED] = (state, action) =>
Object.assign({}, state, {
getSyncIsPending: false,
getSyncErrorMessage: action.data.error,
});
reducers[ACTIONS.SET_SYNC_STARTED] = state =>
Object.assign({}, state, {
setSyncIsPending: true,
@ -65,6 +73,8 @@ reducers[ACTIONS.SYNC_APPLY_FAILED] = (state, action) =>
syncApplyErrorMessage: action.data.error,
});
reducers[ACTIONS.SYNC_RESET] = () => defaultState;
export function syncReducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);

View file

@ -1,4 +1,4 @@
import { ACTIONS } from 'lbry-redux';
import * as ACTIONS from 'constants/action_types';
const reducers = {};
@ -14,8 +14,8 @@ const defaultState = {
invitesRemaining: undefined,
invitees: undefined,
user: undefined,
ytChannelImportPending: false,
ytChannelImportErrorMessage: '',
youtubeChannelImportPending: false,
youtubeChannelImportErrorMessage: '',
};
reducers[ACTIONS.AUTHENTICATION_STARTED] = state =>
@ -225,20 +225,27 @@ reducers[ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE] = state =>
reducers[ACTIONS.USER_YOUTUBE_IMPORT_STARTED] = state =>
Object.assign({}, state, {
ytChannelImportPending: true,
ytChannelImportErrorMessage: '',
youtubeChannelImportPending: true,
youtubeChannelImportErrorMessage: '',
});
reducers[ACTIONS.USER_YOUTUBE_IMPORT_COMPLETED] = state =>
Object.assign({}, state, {
ytChannelImportPending: false,
ytChannelImportErrorMessage: '',
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, {
ytChannelImportPending: false,
ytChannelImportErrorMessage: action.data,
youtubeChannelImportPending: false,
youtubeChannelImportErrorMessage: action.data,
});
export function userReducer(state = defaultState, action) {

View file

@ -13,6 +13,11 @@ export const selectSetSyncErrorMessage = createSelector(
state => state.setSyncErrorMessage
);
export const selectGetSyncErrorMessage = createSelector(
selectState,
state => state.getSyncErrorMessage
);
export const selectGetSyncIsPending = createSelector(selectState, state => state.getSyncIsPending);
export const selectSetSyncIsPending = createSelector(selectState, state => state.setSyncIsPending);

View file

@ -142,12 +142,21 @@ export const selectUserInviteReferralLink = createSelector(
state => state.referralLink
);
export const selectYTImportPending = createSelector(
export const selectYouTubeImportPending = createSelector(
selectState,
state => state.ytChannelImportPending
state => state.youtubeChannelImportPending
);
export const selectYTImportError = createSelector(
export const selectYouTubeImportError = createSelector(
selectState,
state => state.ytChannelImportErrorMessage
state => state.youtubeChannelImportErrorMessage
);
export const selectYouTubeImportVideosComplete = createSelector(selectState, state => {
const total = state.youtubeChannelImportTotal;
const complete = state.youtubeChannelImportComplete || 0;
if (total) {
return [complete, total];
}
});