a73694deb4
## Ticket 1572 Always check for availability before use. ## Changes - Consolidated the keys into one place for easier tracking. - It'll also be easier to code using constant autocomplete. - Cleaned up the wrapper to be as close as the original. - Updated existing code to use the wrapper (even if they already handled the availability) to encourage future code to just use the wrapper.
969 lines
26 KiB
JavaScript
969 lines
26 KiB
JavaScript
import Lbry from 'lbry';
|
|
import { selectClaimForUri } from 'redux/selectors/claims';
|
|
import { doFetchChannelListMine } from 'redux/actions/claims';
|
|
import { isURIValid, normalizeURI } from 'util/lbryURI';
|
|
import { batchActions } from 'util/batch-actions';
|
|
import { getStripeEnvironment } from 'util/stripe';
|
|
import { ODYSEE_CHANNEL } from 'constants/channels';
|
|
import * as ACTIONS from 'constants/action_types';
|
|
import { doFetchGeoBlockedList } from 'redux/actions/blocked';
|
|
import { doClaimRewardType, doRewardList } from 'redux/actions/rewards';
|
|
import { selectEmailToVerify, selectPhoneToVerify, selectUserCountryCode, selectUser } from 'redux/selectors/user';
|
|
import { selectIsRewardApproved } from 'redux/selectors/rewards';
|
|
import { doToast } from 'redux/actions/notifications';
|
|
import rewards from 'rewards';
|
|
import { Lbryio } from 'lbryinc';
|
|
import { DOMAIN, LOCALE_API } from 'config';
|
|
import { getDefaultLanguage } from 'util/default-languages';
|
|
import { LocalStorage, LS } from 'util/storage';
|
|
|
|
export let sessionStorageAvailable = false;
|
|
const CHECK_INTERVAL = 200;
|
|
const AUTH_WAIT_TIMEOUT = 10000;
|
|
const stripeEnvironment = getStripeEnvironment();
|
|
|
|
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, callbackForUsersWhoAreSharingData, domain) {
|
|
const payload = { app_version: appVersion, domain };
|
|
|
|
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 = version.os_system;
|
|
payload.platform = version.platform;
|
|
Lbryio.call('install', 'new', payload);
|
|
|
|
if (callbackForUsersWhoAreSharingData) {
|
|
callbackForUsersWhoAreSharingData(status);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function checkAuthBusy() {
|
|
let time = Date.now();
|
|
return new Promise(function (resolve, reject) {
|
|
(function waitForAuth() {
|
|
try {
|
|
sessionStorage.setItem('test', 'available');
|
|
sessionStorage.removeItem('test');
|
|
sessionStorageAvailable = true;
|
|
} catch (e) {
|
|
if (e) {
|
|
// no session storage
|
|
}
|
|
}
|
|
if (!IS_WEB || !sessionStorageAvailable) {
|
|
return resolve();
|
|
}
|
|
const inProgress = LocalStorage.getItem(LS.AUTH_IN_PROGRESS);
|
|
if (!inProgress) {
|
|
LocalStorage.setItem(LS.AUTH_IN_PROGRESS, 'true');
|
|
return resolve();
|
|
} else {
|
|
if (Date.now() - time < AUTH_WAIT_TIMEOUT) {
|
|
setTimeout(waitForAuth, CHECK_INTERVAL);
|
|
} else {
|
|
return resolve();
|
|
}
|
|
}
|
|
})();
|
|
});
|
|
}
|
|
|
|
/***
|
|
* Given a user, return their highest ranking Odysee membership (Premium or Premium Plus)
|
|
* @param dispatch
|
|
* @param user
|
|
* @returns {Promise<void>}
|
|
*/
|
|
export function doCheckUserOdyseeMemberships(user) {
|
|
return async (dispatch) => {
|
|
// get memberships for a given user
|
|
// TODO: in the future, can we specify this just to @odysee?
|
|
|
|
const response = await Lbryio.call(
|
|
'membership',
|
|
'mine',
|
|
{
|
|
environment: stripeEnvironment,
|
|
},
|
|
'post'
|
|
);
|
|
|
|
let savedMemberships = [];
|
|
let highestMembershipRanking;
|
|
|
|
// TODO: this will work for now, but it should be adjusted
|
|
// TODO: to check if it's active, or if it's cancelled if it's still valid past current date
|
|
// loop through all memberships and save the @odysee ones
|
|
// maybe in the future we can only hit @odysee in the API call
|
|
for (const membership of response) {
|
|
if (membership.MembershipDetails && membership.MembershipDetails.channel_name === '@odysee') {
|
|
savedMemberships.push(membership.MembershipDetails.name);
|
|
}
|
|
}
|
|
|
|
// determine highest ranking membership based on returned data
|
|
// note: this is from an odd state in the API where a user can be both premium/Premium + at the same time
|
|
// I expect this can change once upgrade/downgrade is implemented
|
|
if (savedMemberships.length > 0) {
|
|
// if premium plus is a membership, return that, otherwise it's only premium
|
|
const premiumPlusExists = savedMemberships.includes('Premium+');
|
|
if (premiumPlusExists) {
|
|
highestMembershipRanking = 'Premium+';
|
|
} else {
|
|
highestMembershipRanking = 'Premium';
|
|
}
|
|
}
|
|
|
|
dispatch({
|
|
type: ACTIONS.ADD_ODYSEE_MEMBERSHIP_DATA,
|
|
data: { user, odyseeMembershipName: highestMembershipRanking || '' }, // '' = none; `undefined` = not fetched
|
|
});
|
|
};
|
|
}
|
|
|
|
// TODO: Call doInstallNew separately so we don't have to pass appVersion and os_system params?
|
|
export function doAuthenticate(
|
|
appVersion,
|
|
shareUsageData = true,
|
|
callbackForUsersWhoAreSharingData,
|
|
callInstall = true
|
|
) {
|
|
return (dispatch) => {
|
|
dispatch({
|
|
type: ACTIONS.AUTHENTICATION_STARTED,
|
|
});
|
|
checkAuthBusy()
|
|
.then(() => {
|
|
return Lbryio.authenticate(DOMAIN, getDefaultLanguage());
|
|
})
|
|
.then((user) => {
|
|
LocalStorage.removeItem(LS.AUTH_IN_PROGRESS);
|
|
Lbryio.getAuthToken().then((token) => {
|
|
dispatch({
|
|
type: ACTIONS.AUTHENTICATION_SUCCESS,
|
|
data: { user, accessToken: token },
|
|
});
|
|
|
|
// if user is an Odysee member, get the membership details
|
|
if (user.odysee_member) {
|
|
dispatch(doCheckUserOdyseeMemberships(user));
|
|
}
|
|
|
|
if (shareUsageData) {
|
|
dispatch(doRewardList());
|
|
|
|
if (callInstall && !user?.device_types?.includes('web')) {
|
|
doInstallNew(appVersion, callbackForUsersWhoAreSharingData, DOMAIN);
|
|
}
|
|
}
|
|
|
|
dispatch(doFetchGeoBlockedList());
|
|
});
|
|
})
|
|
.catch((error) => {
|
|
LocalStorage.removeItem(LS.AUTH_IN_PROGRESS);
|
|
|
|
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) => {
|
|
// get user membership status
|
|
if (user.odysee_member) {
|
|
dispatch(doCheckUserOdyseeMemberships(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) {
|
|
// check premium membership
|
|
if (user.odysee_member) {
|
|
dispatch(doCheckUserOdyseeMemberships(user));
|
|
}
|
|
|
|
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) => {
|
|
// no email
|
|
if (error.response && error.response.status === 404) {
|
|
dispatch({
|
|
type: ACTIONS.USER_EMAIL_NEW_DOES_NOT_EXIST,
|
|
});
|
|
// sign in by email
|
|
} else if (error.response && error.response.status === 412) {
|
|
triggerEmailFlow(false);
|
|
}
|
|
|
|
throw error;
|
|
})
|
|
// sign the user in
|
|
.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, only_if_expired: true }, '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 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_address%', { email_address: 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 doUserSetReferrerWithUri(uri) {
|
|
return async (dispatch, getState) => {
|
|
const state = getState();
|
|
let claim = selectClaimForUri(state, uri);
|
|
|
|
let referrerCode;
|
|
if (!claim) {
|
|
try {
|
|
const response = await Lbry.resolve({ urls: [uri] });
|
|
if (response && response[uri] && !response[uri].error) claim = response && response[uri];
|
|
if (claim) {
|
|
if (claim.signing_channel) {
|
|
referrerCode = claim.signing_channel.permanent_url.replace('lbry://', '');
|
|
} else {
|
|
referrerCode = claim.permanent_url.replace('lbry://', '');
|
|
}
|
|
const isRewardApproved = selectIsRewardApproved(state);
|
|
dispatch(doUserSetReferrer(referrerCode, isRewardApproved));
|
|
}
|
|
} catch (error) {
|
|
dispatch({
|
|
type: ACTIONS.USER_SET_REFERRER_FAILURE,
|
|
data: { error },
|
|
});
|
|
}
|
|
} else {
|
|
referrerCode = claim.permanent_url.replace('lbry://', '');
|
|
const isRewardApproved = selectIsRewardApproved(state);
|
|
dispatch(doUserSetReferrer(referrerCode, isRewardApproved));
|
|
}
|
|
};
|
|
}
|
|
|
|
export function doUserSetReferrer(referrer, shouldClaim) {
|
|
return async (dispatch, getState) => {
|
|
dispatch({
|
|
type: ACTIONS.USER_SET_REFERRER_STARTED,
|
|
});
|
|
|
|
let claim, referrerCode;
|
|
const isValid = isURIValid(referrer);
|
|
|
|
if (isValid) {
|
|
const state = getState();
|
|
const uri = normalizeURI(referrer);
|
|
claim = selectClaimForUri(state, uri);
|
|
|
|
if (!claim) {
|
|
try {
|
|
const response = await Lbry.resolve({ urls: [uri] });
|
|
if (response && response[uri] && !response[uri].error) claim = response && response[uri];
|
|
if (claim) {
|
|
if (claim.signing_channel) {
|
|
referrerCode = claim.signing_channel.permanent_url.replace('lbry://', '');
|
|
} else {
|
|
referrerCode = claim.permanent_url.replace('lbry://', '');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
dispatch({
|
|
type: ACTIONS.USER_SET_REFERRER_FAILURE,
|
|
data: { error },
|
|
});
|
|
}
|
|
} else {
|
|
referrerCode = 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),
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
/***
|
|
* Receives a csv of channel claim ids, hits the backend and returns nicely formatted object with relevant info
|
|
* @param claimIdCsv
|
|
* @returns {(function(*): Promise<void>)|*}
|
|
*/
|
|
export function doFetchUserMemberships(claimIdCsv) {
|
|
return async (dispatch) => {
|
|
if (!claimIdCsv || (claimIdCsv.length && claimIdCsv.length < 1)) return;
|
|
|
|
// check if users have odysee memberships (premium/premium+)
|
|
const response = await Lbryio.call('membership', 'check', {
|
|
channel_id: ODYSEE_CHANNEL.ID,
|
|
claim_ids: claimIdCsv,
|
|
environment: stripeEnvironment,
|
|
});
|
|
|
|
let updatedResponse = {};
|
|
|
|
// loop through returned users
|
|
for (const user in response) {
|
|
// if array was returned for a user (indicating a membership exists), otherwise is null
|
|
if (response[user] && response[user].length) {
|
|
// get membership for user
|
|
// note: a for loop is kind of odd, indicates there may be multiple memberships?
|
|
// probably not needed depending on what we do with the frontend, should revisit
|
|
for (const membership of response[user]) {
|
|
if (membership.channel_name) {
|
|
updatedResponse[user] = membership.name;
|
|
window.checkedMemberships[user] = membership.name;
|
|
}
|
|
}
|
|
} else {
|
|
// note the user has been fetched but is null
|
|
updatedResponse[user] = null;
|
|
window.checkedMemberships[user] = null;
|
|
}
|
|
}
|
|
|
|
dispatch({ type: ACTIONS.ADD_CLAIMIDS_MEMBERSHIP_DATA, data: { response: updatedResponse } });
|
|
};
|
|
}
|
|
|
|
export function doFetchUserLocale(isRetry = false) {
|
|
return (dispatch) => {
|
|
fetch(LOCALE_API)
|
|
.then((res) => res.json())
|
|
.then((json) => {
|
|
const locale = json.data; // [flow] local: LocaleInfo
|
|
if (locale) {
|
|
dispatch({
|
|
type: ACTIONS.USER_FETCH_LOCALE_DONE,
|
|
data: locale,
|
|
});
|
|
}
|
|
})
|
|
.catch(() => {
|
|
if (!isRetry) {
|
|
// If failed, retry one more time after N seconds. This removes the
|
|
// need to fetch at each component level. If it failed twice, probably
|
|
// don't need to fetch anymore.
|
|
setTimeout(() => {
|
|
dispatch(doFetchUserLocale(true));
|
|
}, 10000);
|
|
}
|
|
});
|
|
};
|
|
}
|