sync wip: ux refactor, wallet encryption tests, wallet import/export, sync loop begin rewrite
This commit is contained in:
parent
bdfb8d29f5
commit
d8be27aa50
13 changed files with 542 additions and 268 deletions
|
@ -33,10 +33,10 @@ export function deriveSecrets(rootPassword, email, saltSeed, callback) {
|
|||
const DEFAULT_MAXMEM = MAXMEM_MULTIPLIER * SCRYPT_N * SCRYPT_R;
|
||||
|
||||
function getKeyParts(key) {
|
||||
const lbryIdPassword = key.slice(0, KEY_LENGTH).toString('base64');
|
||||
const providerKey = key.slice(0, KEY_LENGTH).toString('base64');
|
||||
const hmacKey = key.slice(KEY_LENGTH, KEY_LENGTH * 2).toString('base64');
|
||||
const dataKey = key.slice(KEY_LENGTH * 2).toString('base64');
|
||||
return { lbryIdPassword, hmacKey, dataKey }; // Buffer aa bb cc 6c
|
||||
return { providerKey, hmacKey, dataKey }; // Buffer aa bb cc 6c
|
||||
}
|
||||
|
||||
const salt = generateSalt(encodedEmail, saltSeed);
|
||||
|
@ -52,8 +52,36 @@ export function deriveSecrets(rootPassword, email, saltSeed, callback) {
|
|||
crypto.scrypt(encodedPassword, salt, KEY_LENGTH * NUM_KEYS, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P, maxmem: DEFAULT_MAXMEM }, scryptCallback);
|
||||
}
|
||||
|
||||
export function walletHmac(inputString) {
|
||||
const hmac = crypto.createHmac('sha256', inputString.toString('utf8'));
|
||||
const res = hmac.digest('hex');
|
||||
export function walletHmac(inputString, hmacKey) {
|
||||
const res = crypto.createHmac('sha256', hmacKey)
|
||||
.update(inputString.toString('utf8'))
|
||||
.digest('hex');
|
||||
console.log('hmac res', res)
|
||||
return res;
|
||||
}
|
||||
|
||||
export function aesEncrypt(text, key) {
|
||||
try {
|
||||
const iv = crypto.randomBytes(16);
|
||||
let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
|
||||
let encrypted = cipher.update(text);
|
||||
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
||||
return { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex') };
|
||||
} catch (e) {
|
||||
return { error: `Wallet decrypt failed error: ${e.message}` };
|
||||
}
|
||||
}
|
||||
|
||||
export function aesDecrypt(cipher, key) {
|
||||
try {
|
||||
let iv = Buffer.from(cipher.iv, 'hex');
|
||||
let encryptedText = Buffer.from(cipher.encryptedData, 'hex');
|
||||
let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), iv);
|
||||
let decrypted = decipher.update(encryptedText);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
// handle errors here
|
||||
return { result: decrypted.toString() };
|
||||
} catch (e) {
|
||||
return { error: `Wallet decrypt failed error: ${e.message}`};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import test from 'tape';
|
||||
// import sync from '../sync.js';
|
||||
import { generateSalt, generateSaltSeed, deriveSecrets, walletHmac } from './sync.js';
|
||||
import { generateSalt, generateSaltSeed, deriveSecrets, walletHmac, aesEncrypt, aesDecrypt } from './sync.js';
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
export default function doTest() {
|
||||
|
||||
test('Generate sync seed', (assert) => {
|
||||
const seed = generateSaltSeed();
|
||||
console.log('seed', seed);
|
||||
|
@ -28,12 +29,13 @@ export default function doTest() {
|
|||
const email = 'example@example.com';
|
||||
|
||||
const expectedHmacKey = 'bCxUIryLK0Lf9nKg9yiZDlGleMuGJkadLzTje1PAI+8='; //base64
|
||||
const expectedLbryIdPassword = 'HKo/J+x4Hsy2NkMvj2JB9RI0yrvEiB4QSA/NHPaT/cA=';
|
||||
const expectedProviderKey = 'HKo/J+x4Hsy2NkMvj2JB9RI0yrvEiB4QSA/NHPaT/cA=';
|
||||
// add expectedDataKey to test
|
||||
|
||||
function cb(e, r) {
|
||||
console.log('result', r);
|
||||
console.log('derive keys result:', r);
|
||||
assert.equal(r.hmacKey, expectedHmacKey, 'hmac is expected value');
|
||||
assert.equal(r.lbryIdPassword, expectedLbryIdPassword, 'lbryid password is expected value');
|
||||
assert.equal(r.providerKey, expectedProviderKey, 'lbryid password is expected value');
|
||||
assert.end();
|
||||
}
|
||||
|
||||
|
@ -44,13 +46,25 @@ export default function doTest() {
|
|||
const hmacKey = 'bCxUIryLK0Lf9nKg9yiZDlGleMuGJkadLzTje1PAI+8=';
|
||||
const sequence = 1;
|
||||
const walletState = `zo4MTkyOjE2OjE68QlIU76+W91/v/F1tu8h+kGB0Ee`;
|
||||
const expectedHmacHex = '52edbad5b0f9d8cf6189795702790cc2cb92060be24672913ab3e4b69c03698b';
|
||||
const expectedHmacHex = '9fe70ebdeaf85b3afe5ae42e52f946acc54ded0350acacdded821845217839d4';
|
||||
|
||||
const input_str = `${sequence}:${walletState}`;
|
||||
const hmacHex = walletHmac(input_str);
|
||||
const hmacHex = walletHmac(input_str, hmacKey);
|
||||
assert.equal(hmacHex, expectedHmacHex);
|
||||
assert.end();
|
||||
});
|
||||
|
||||
test('Encrypt/Decrypt', (assert) => {
|
||||
const key = crypto.randomBytes(32);
|
||||
// const wrongKey = crypto.randomBytes(32); // todo: what tests should fail; how much error handling needed?
|
||||
// test a handy json file
|
||||
const input = fs.readFileSync('../../package.json', 'utf8');
|
||||
const cipher = aesEncrypt(input, key);
|
||||
console.log('cipher', cipher);
|
||||
const output = aesDecrypt(cipher, key);
|
||||
assert.equal(input, output.result);
|
||||
assert.end();
|
||||
});
|
||||
}
|
||||
|
||||
doTest();
|
||||
|
|
|
@ -2334,5 +2334,13 @@
|
|||
"Doing Math": "Doing Math",
|
||||
"Hold on, doing some math.": "Hold on, doing some math.",
|
||||
"You are signed in as %email%.": "You are signed in as %email%.",
|
||||
"Sync Provider Url": "Sync Provider Url",
|
||||
"Enter Password": "Enter Password",
|
||||
"Enter a password.": "Enter a password.",
|
||||
"Some sign-in relevant message.": "Some sign-in relevant message.",
|
||||
"Hold on, doing some math. (Registering you)": "Hold on, doing some math. (Registering you)",
|
||||
"Failed to view lbry://@rossmanngroup#a/Day-39-Gotham-City-Solutions#e, please try again. If this problem persists, visit https://lbry.com/faq/support for support.": "Failed to view lbry://@rossmanngroup#a/Day-39-Gotham-City-Solutions#e, please try again. If this problem persists, visit https://lbry.com/faq/support for support.",
|
||||
"Anon --[used in <%anonymous% Reposted>]--": "Anon",
|
||||
"%anonymous%": "%anonymous%",
|
||||
"--end--": "--end--"
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ export default function HeaderMenuButtons(props: HeaderMenuButtonProps) {
|
|||
<MenuList className="menu__list--header">
|
||||
<HeaderMenuLink page={PAGES.UPLOAD} icon={ICONS.PUBLISH} name={__('Upload')} />
|
||||
<HeaderMenuLink page={PAGES.CHANNEL_NEW} icon={ICONS.CHANNEL} name={__('New Channel')} />
|
||||
<HeaderMenuLink page={PAGES.SETTINGS_SYNC} icon={ICONS.GAMING} name={__('Sign In')} />
|
||||
</MenuList>
|
||||
</Menu>
|
||||
|
||||
|
|
|
@ -123,6 +123,12 @@ export const DO_UTXO_CONSOLIDATE_STARTED = 'DO_UTXO_CONSOLIDATE_STARTED';
|
|||
export const DO_UTXO_CONSOLIDATE_COMPLETED = 'DO_UTXO_CONSOLIDATE_COMPLETED';
|
||||
export const DO_UTXO_CONSOLIDATE_FAILED = 'DO_UTXO_CONSOLIDATE_FAILED';
|
||||
export const PENDING_CONSOLIDATED_TXOS_UPDATED = 'PENDING_CONSOLIDATED_TXOS_UPDATED';
|
||||
export const WALLET_EXPORT_STARTED = 'WALLET_EXPORT_STARTED';
|
||||
export const WALLET_EXPORT_COMPLETED = 'WALLET_EXPORT_COMPLETED';
|
||||
export const WALLET_EXPORT_FAILED = 'WALLET_EXPORT_FAILED';
|
||||
export const WALLET_IMPORT_STARTED = 'WALLET_IMPORT_STARTED';
|
||||
export const WALLET_IMPORT_COMPLETED = 'WALLET_IMPORT_COMPLETED';
|
||||
export const WALLET_IMPORT_FAILED = 'WALLET_IMPORT_FAILED';
|
||||
|
||||
// Claims
|
||||
export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';
|
||||
|
@ -511,3 +517,9 @@ export const LSYNC_DERIVE_FAILED = 'LSYNC_DERIVE_FAILED';
|
|||
export const LSYNC_GET_SALT_STARTED = 'LSYNC_GET_SALT_STARTED';
|
||||
export const LSYNC_GET_SALT_COMPLETED = 'LSYNC_GET_SALT_COMPLETED'; // got salt
|
||||
export const LSYNC_GET_SALT_FAILED = 'LSYNC_GET_SALT_FAILED';
|
||||
export const LSYNC_CHECK_EMAIL_STARTED = 'LSYNC_CHECK_EMAIL_STARTED';
|
||||
export const LSYNC_CHECK_EMAIL_COMPLETED = 'LSYNC_CHECK_EMAIL_COMPLETED'; // got salt
|
||||
export const LSYNC_CHECK_EMAIL_FAILED = 'LSYNC_CHECK_EMAIL_FAILED';
|
||||
export const LSYNC_SYNC_STARTED = 'LSYNC_SYNC_STARTED';
|
||||
export const LSYNC_SYNC_COMPLETED = 'LSYNC_SYNC_COMPLETED'; // got salt
|
||||
export const LSYNC_SYNC_FAILED = 'LSYNC_SYNC_FAILED';
|
||||
|
|
|
@ -36,7 +36,7 @@ const Lbry = {
|
|||
|
||||
// Returns a human readable media type based on the content type or extension of a file that is returned by the sdk
|
||||
getMediaType: (contentType: ?string, fileName: ?string) => {
|
||||
if (fileName) {
|
||||
if (fileName && fileName.split('.').length > 1) {
|
||||
const formats = [
|
||||
[/\.(mp4|m4v|webm|flv|f4v|ogv)$/i, 'video'],
|
||||
[/\.(mp3|m4a|aac|wav|flac|ogg|opus)$/i, 'audio'],
|
||||
|
@ -111,6 +111,8 @@ const Lbry = {
|
|||
wallet_list: (params = {}) => daemonCallWithResult('wallet_list', params),
|
||||
wallet_send: (params = {}) => daemonCallWithResult('wallet_send', params),
|
||||
wallet_status: (params = {}) => daemonCallWithResult('wallet_status', params),
|
||||
wallet_export: (params = {}) => daemonCallWithResult('wallet_export', params),
|
||||
wallet_import: (params = {}) => daemonCallWithResult('wallet_import', params),
|
||||
address_is_mine: (params = {}) => daemonCallWithResult('address_is_mine', params),
|
||||
address_unused: (params = {}) => daemonCallWithResult('address_unused', params),
|
||||
address_list: (params = {}) => daemonCallWithResult('address_list', params),
|
||||
|
|
|
@ -4,7 +4,7 @@ const SYNC_API_DOWN = 'sync_api_down';
|
|||
const DUPLICATE_EMAIL = 'duplicate_email';
|
||||
const UNKNOWN_ERROR = 'unknown_api_error';
|
||||
const NOT_FOUND = 'not_found';
|
||||
console.log('process.env.', process.env.LBRYSYNC_BASE_URL);
|
||||
// console.log('process.env.', process.env.LBRYSYNC_BASE_URL);
|
||||
|
||||
const API_VERSION = 3;
|
||||
const POST = 'POST';
|
||||
|
@ -41,43 +41,47 @@ export async function getAuthToken(email, password, deviceId) {
|
|||
|
||||
export async function register(email, password, saltSeed) {
|
||||
try {
|
||||
await callWithResult(POST, REGISTER_ENDPOINT, { email, password, clientSaltSeed: saltSeed });
|
||||
return;
|
||||
const result = await callWithResult(POST, REGISTER_ENDPOINT, { email, password, clientSaltSeed: saltSeed });
|
||||
return result;
|
||||
} catch (e) {
|
||||
return { error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
export async function pushWallet(walletState, hmac, token) {
|
||||
// token?
|
||||
const body = {
|
||||
token: token,
|
||||
encryptedWallet: walletState.encryptedWallet,
|
||||
sequence: walletState.sequence,
|
||||
hmac: hmac,
|
||||
};
|
||||
await callWithResult(POST, WALLET_ENDPOINT, { token, hmac, sequence });
|
||||
}
|
||||
|
||||
export async function pullWallet(token) {
|
||||
try {
|
||||
await callWithResult(GET, REGISTER_ENDPOINT, { token });
|
||||
return;
|
||||
const result = await callWithResult(GET, WALLET_ENDPOINT, { token });
|
||||
return result;
|
||||
} catch (e) {
|
||||
return { error: e.message };
|
||||
}
|
||||
} // token
|
||||
}
|
||||
|
||||
// export async function pushWallet(walletState, hmac, token) {
|
||||
// // token?
|
||||
// const body = {
|
||||
// token: token,
|
||||
// encryptedWallet: walletState.encryptedWallet,
|
||||
// sequence: walletState.sequence,
|
||||
// hmac: hmac,
|
||||
// };
|
||||
// await callWithResult(POST, WALLET_ENDPOINT, { token, hmac, sequence });
|
||||
// }
|
||||
|
||||
function callWithResult(method, endpoint, params = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
apiCall(
|
||||
return apiCall(
|
||||
method,
|
||||
endpoint,
|
||||
params,
|
||||
(result) => {
|
||||
console.log('cwr result', result);
|
||||
resolve(result);
|
||||
},
|
||||
reject
|
||||
(er) => {
|
||||
console.log('er', er);
|
||||
reject(er);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -96,13 +100,18 @@ function apiCall(method, endpoint, params, resolve, reject) {
|
|||
return fetch(`${Lbrysync.apiUrl}${endpoint}${searchString}`, options)
|
||||
.then(handleResponse)
|
||||
.then((response) => {
|
||||
return response;
|
||||
console.log('response 200', response);
|
||||
return resolve(response);
|
||||
})
|
||||
.catch(reject);
|
||||
.catch((r) => {
|
||||
console.log('r', r);
|
||||
return reject(r);
|
||||
});
|
||||
}
|
||||
|
||||
function handleResponse(response) {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
console.log('200+');
|
||||
return response.json();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,25 +17,18 @@ import {
|
|||
selectLbrySyncEncryptedHmacKey,
|
||||
selectLbrySyncEncryptedRoot,
|
||||
selectLbrySyncEncryptedProviderPass,
|
||||
selectLbrySyncCheckingEmail,
|
||||
selectLbrySyncEmailError,
|
||||
selectLbrySyncEmailCandidate,
|
||||
} from 'redux/selectors/sync';
|
||||
|
||||
import {
|
||||
doLbrysyncGetSalt,
|
||||
doLbrysyncRegister,
|
||||
doGenerateSaltSeed,
|
||||
doDeriveSecrets,
|
||||
doLbrysyncAuthenticate,
|
||||
} from 'redux/actions/sync';
|
||||
import { doHandleEmail, doLbrysyncRegister, doLbrysyncAuthenticate, doEmailVerifySubscribe } from 'redux/actions/sync';
|
||||
|
||||
const select = (state) => ({
|
||||
isWalletEncrypted: selectWalletIsEncrypted(state),
|
||||
registering: selectLbrySyncRegistering(state),
|
||||
registeredEmail: selectLbrySyncEmail(state),
|
||||
registerError: selectLbrySyncRegisterError(state),
|
||||
|
||||
gettingSalt: selectLbrySyncGettingSalt(state),
|
||||
saltError: selectLbrySyncSaltError(state),
|
||||
saltSeed: selectLbrySyncSaltSeed(state),
|
||||
token: selectLbrySyncToken(state),
|
||||
|
||||
authenticating: selectLbrySyncIsAuthenticating(state),
|
||||
|
@ -46,16 +39,25 @@ const select = (state) => ({
|
|||
encHmacKey: selectLbrySyncEncryptedHmacKey(state), // ?
|
||||
encRootPass: selectLbrySyncEncryptedRoot(state),
|
||||
encProviderPass: selectLbrySyncEncryptedProviderPass(state),
|
||||
// begin
|
||||
// --email
|
||||
isCheckingEmail: selectLbrySyncCheckingEmail(state),
|
||||
candidateEmail: selectLbrySyncEmailCandidate(state),
|
||||
emailError: selectLbrySyncEmailError(state),
|
||||
registeredEmail: selectLbrySyncEmail(state),
|
||||
saltSeed: selectLbrySyncSaltSeed(state),
|
||||
// --password
|
||||
// registerError
|
||||
});
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
encryptWallet: () => dispatch(doNotifyEncryptWallet()),
|
||||
decryptWallet: () => dispatch(doNotifyDecryptWallet()),
|
||||
getSalt: (email) => dispatch(doLbrysyncGetSalt(email)),
|
||||
generateSaltSeed: () => dispatch(doGenerateSaltSeed()),
|
||||
handleEmail: (email, signUp) => dispatch(doHandleEmail(email, signUp)),
|
||||
authenticate: () => dispatch(doLbrysyncAuthenticate()),
|
||||
deriveSecrets: (p, e, s) => dispatch(doDeriveSecrets(p, e, s)),
|
||||
register: (email, secrets, saltseed) => dispatch(doLbrysyncRegister(email, secrets, saltseed)),
|
||||
waitForVerify: (stop) => dispatch(doEmailVerifySubscribe(stop)),
|
||||
// deriveSecrets: (p, e, s) => dispatch(doDeriveSecrets(p, e, s)),
|
||||
register: (password) => dispatch(doLbrysyncRegister(password)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(SettingsSync);
|
||||
|
|
|
@ -12,7 +12,6 @@ type Props = {
|
|||
encryptWallet: (string) => void,
|
||||
decryptWallet: (string) => void,
|
||||
registering: boolean,
|
||||
email: string,
|
||||
registerError: string,
|
||||
token: string,
|
||||
authenticating: boolean,
|
||||
|
@ -22,108 +21,117 @@ type Props = {
|
|||
encRootPass: string,
|
||||
encProviderPass: string,
|
||||
getSalt: (string) => void,
|
||||
gettingSalt: boolean,
|
||||
saltError: string,
|
||||
saltSeed: string,
|
||||
deriveSecrets: (string, string, string) => void, // something
|
||||
|
||||
// begin
|
||||
// email
|
||||
handleEmail: (string, string) => void, // return something?
|
||||
checkingEmail: boolean,
|
||||
candidateEmail?: string,
|
||||
registeredEmail?: string,
|
||||
saltSeed: string,
|
||||
emailError: string,
|
||||
// password/register
|
||||
register: (string) => void,
|
||||
waitForVerify: () => void,
|
||||
};
|
||||
|
||||
export default function NotificationSettingsPage(props: Props) {
|
||||
export default function SettingsSyncPage(props: Props) {
|
||||
// const { } = props;
|
||||
const {
|
||||
walletEncrypted,
|
||||
encryptWallet,
|
||||
decryptWallet,
|
||||
registering,
|
||||
registeredEmail,
|
||||
registerError,
|
||||
token,
|
||||
authenticating,
|
||||
authError,
|
||||
authenticate,
|
||||
derivingKeys,
|
||||
encHmacKey, // ?
|
||||
encRootPass,
|
||||
encProviderPass,
|
||||
getSalt,
|
||||
generateSaltSeed,
|
||||
deriveSecrets,
|
||||
gettingSalt,
|
||||
saltError,
|
||||
saltSeed,
|
||||
register,
|
||||
// begin
|
||||
// .. email
|
||||
registeredEmail,
|
||||
handleEmail,
|
||||
checkingEmail,
|
||||
candidateEmail,
|
||||
saltSeed,
|
||||
emailError,
|
||||
// password
|
||||
// verify
|
||||
waitForVerify,
|
||||
} = props;
|
||||
|
||||
/*
|
||||
Register / auth
|
||||
*/
|
||||
const SIGN_IN_MODE = 'sign_in';
|
||||
const SIGN_UP_MODE = 'sign_up';
|
||||
const VERIFY_MODE = 'verify';
|
||||
const MATH_MODE = 'math';
|
||||
const DONE_MODE = 'done';
|
||||
|
||||
const [mode, setMode] = React.useState(registeredEmail ? VERIFY_MODE : SIGN_IN_MODE);
|
||||
// modes
|
||||
const SIGN_IN_MODE = 'sign_in_mode';
|
||||
const SIGN_UP_MODE = 'sign_up_mode';
|
||||
const [mode, setMode] = React.useState(SIGN_IN_MODE);
|
||||
// steps
|
||||
const EMAIL_SCREEN = 'sign_in'; // show email input
|
||||
// checking email
|
||||
const PASSWORD_SCREEN = 'password'; // show password input
|
||||
// registering
|
||||
const REGISTERING_SCREEN = 'register'; // show working page for deriving passwords, registering
|
||||
const VERIFY_SCREEN = 'verify'; // show waiting for email verification
|
||||
// waiting for email verification
|
||||
const SYNC_SCREEN = 'sync';
|
||||
// syncing wallet with server
|
||||
const DONE_SCREEN = 'done';
|
||||
|
||||
const [email, setEmail] = React.useState();
|
||||
const [pass, setPass] = React.useState();
|
||||
const [showPass, setShowPass] = React.useState(false);
|
||||
const [error, setError] = React.useState('');
|
||||
|
||||
let STEP;
|
||||
if (!candidateEmail) {
|
||||
STEP = EMAIL_SCREEN; // present email form, on submit check email salt
|
||||
} else if (!encRootPass && !registering) {
|
||||
// make this "hasPasswords"
|
||||
STEP = PASSWORD_SCREEN; // present password form, on submit derive keys and register
|
||||
} else if (registering) {
|
||||
STEP = REGISTERING_SCREEN;
|
||||
} else if (encRootPass && !token) {
|
||||
STEP = VERIFY_SCREEN; // until token
|
||||
} else if (token) {
|
||||
STEP = SYNC_SCREEN;
|
||||
}
|
||||
|
||||
// error comes from store
|
||||
// const [error, setError] = React.useState('');
|
||||
|
||||
React.useEffect(() => {
|
||||
let interval;
|
||||
if (!token && registeredEmail) {
|
||||
interval = setInterval(() => {
|
||||
console.log('doauthint');
|
||||
authenticate();
|
||||
}, 5000);
|
||||
if (registeredEmail && !token) {
|
||||
waitForVerify();
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
waitForVerify(true);
|
||||
};
|
||||
}, [registeredEmail, token, authenticate]);
|
||||
}, [registeredEmail, token]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (token && registeredEmail) {
|
||||
setMode(DONE_MODE);
|
||||
if (token) {
|
||||
// sign up
|
||||
// what
|
||||
// pushAndStart();
|
||||
// what
|
||||
//
|
||||
}
|
||||
}, [registeredEmail, token, setMode]);
|
||||
}, [token]);
|
||||
|
||||
const handleSignUp = async () => {
|
||||
// get salt for email to make sure
|
||||
const saltSeedOrError = await getSalt(email);
|
||||
if (saltSeedOrError.seed) {
|
||||
setError('Email already registered');
|
||||
return;
|
||||
}
|
||||
// -- if found, report email already exists - sign in?
|
||||
const saltSeed = await generateSaltSeed();
|
||||
// saltSeed = generateSaltSeed()
|
||||
setMode(MATH_MODE);
|
||||
const secrets = await deriveSecrets(pass, email, saltSeed);
|
||||
setMode(VERIFY_MODE);
|
||||
// passwords = driveKeys(root, email, saltSeed);
|
||||
try {
|
||||
const registerSuccess = await register(email, secrets, saltSeed);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
// registerSuccess = register(email, servicePassword, saltSeed)
|
||||
|
||||
// poll auth until success
|
||||
// store [token, rootPassword, providerPass, HmacKey, saltSeed, salt, registeredEmail]
|
||||
const handleRegister = (e) => {
|
||||
register(pass);
|
||||
};
|
||||
|
||||
const handleSignIn = async () => {
|
||||
// get saltseed for email
|
||||
// saltSeed = getSaltSeed()
|
||||
// -- if error, report email not registered - sign up?
|
||||
// salt = generateSalt(seed)
|
||||
// passwords = deriveKeys(root, email, saltSeed);
|
||||
// token = authenticate(email, servicePassword, deviceId)
|
||||
// store [token, rootPassword, servicePassword, HmacKey, saltSeed, salt, registeredEmail]
|
||||
// kick off sync pull
|
||||
// -- possibly merge conflicts
|
||||
const handleSignUpEmail = async () => {
|
||||
// get salt for email to make sure email doesn't exist
|
||||
handleEmail(email, true);
|
||||
};
|
||||
|
||||
const handleSignInEmail = () => {
|
||||
handleEmail(email, false);
|
||||
};
|
||||
|
||||
const doneCard = (
|
||||
|
@ -150,11 +158,19 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
/>
|
||||
);
|
||||
|
||||
const deriveCard = (
|
||||
<Card title={__('Doing Math')} subtitle={__('Hold on, doing some math.')} actions={<div>Math...</div>} />
|
||||
const registerCard = (
|
||||
<Card title={__('Registering')} subtitle={__('Hold on a moment, signing you up.')} actions={<div>Math...</div>} />
|
||||
);
|
||||
|
||||
const signInCard = (
|
||||
const syncCard = (
|
||||
<Card
|
||||
title={__('Syncing With Server')}
|
||||
subtitle={__(`Great. Now we're syncing your wallet.`)}
|
||||
actions={<div>Math...</div>}
|
||||
/>
|
||||
);
|
||||
|
||||
const signInEmailCard = (
|
||||
<Card
|
||||
title={__('Sign In')}
|
||||
subtitle={
|
||||
|
@ -172,7 +188,7 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
}
|
||||
actions={
|
||||
<div>
|
||||
<Form onSubmit={handleSignIn} className="section">
|
||||
<Form onSubmit={handleSignInEmail} className="section">
|
||||
<FormField
|
||||
autoFocus
|
||||
placeholder={__('yourstruly@example.com')}
|
||||
|
@ -181,25 +197,18 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
label={__('Email')}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
helper={emailError && emailError}
|
||||
/>
|
||||
<FormField
|
||||
type={showPass ? 'text' : 'password'}
|
||||
name="root_password"
|
||||
inputButton={
|
||||
<>
|
||||
<Button
|
||||
icon={showPass ? ICONS.EYE : ICONS.EYE_OFF}
|
||||
onClick={() => setShowPass(!showPass)}
|
||||
className={'editable-text__input-button'}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
label={__('Password')}
|
||||
value={pass}
|
||||
type={'text'}
|
||||
name="sync_provider"
|
||||
disabled
|
||||
label={__('Sync Provider Url')}
|
||||
value={'https://dev.lbry.id'}
|
||||
onChange={(e) => setPass(e.target.value)}
|
||||
/>
|
||||
<div className="section__actions">
|
||||
<Button button="primary" type="submit" label={__('Submit')} disabled={!email} />
|
||||
<Button button="primary" type="submit" label={__('Submit')} disabled={checkingEmail} />
|
||||
</div>
|
||||
<p className="help--card-actions">
|
||||
<I18nMessage
|
||||
|
@ -207,7 +216,7 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
terms: <Button button="link" href="https://www.lbry.com/termsofservice" label={__('terms')} />,
|
||||
}}
|
||||
>
|
||||
By creating an account, you agree to our %terms% and confirm you're over the age of 13.
|
||||
Some sign-in relevant message.
|
||||
</I18nMessage>
|
||||
</p>
|
||||
</Form>
|
||||
|
@ -216,7 +225,7 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
/>
|
||||
);
|
||||
|
||||
const signUpCard = (
|
||||
const signUpEmailCard = (
|
||||
<Card
|
||||
title={__('Sign Up')}
|
||||
subtitle={
|
||||
|
@ -234,7 +243,7 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
}
|
||||
actions={
|
||||
<div>
|
||||
<Form onSubmit={handleSignUp} className="section">
|
||||
<Form onSubmit={handleSignUpEmail} className="section">
|
||||
<FormField
|
||||
autoFocus
|
||||
placeholder={__('yourstruly@example.com')}
|
||||
|
@ -243,9 +252,46 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
label={__('Email')}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
helper={emailError && emailError}
|
||||
/>
|
||||
<FormField
|
||||
type="password"
|
||||
type={'text'}
|
||||
name="sync_provider"
|
||||
disabled
|
||||
label={__('Sync Provider Url')}
|
||||
value={'https://dev.lbry.id'}
|
||||
onChange={(e) => setPass(e.target.value)}
|
||||
/>
|
||||
<div className="section__actions">
|
||||
<Button button="primary" type="submit" label={__('Submit')} disabled={checkingEmail} />
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const passwordCard = (
|
||||
<Card
|
||||
title={__('Enter Password')}
|
||||
subtitle={
|
||||
<>
|
||||
<p>
|
||||
<I18nMessage
|
||||
tokens={{
|
||||
sign_in: <Button button="link" onClick={() => setMode(EMAIL_SCREEN)} label={__('Sign in')} />,
|
||||
}}
|
||||
>
|
||||
Enter a password.
|
||||
</I18nMessage>
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
actions={
|
||||
<div>
|
||||
<fieldset-section className="section">
|
||||
<FormField
|
||||
type={showPass ? 'text' : 'password'}
|
||||
name="root_password"
|
||||
inputButton={
|
||||
<>
|
||||
|
@ -261,7 +307,8 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
onChange={(e) => setPass(e.target.value)}
|
||||
/>
|
||||
<div className="section__actions">
|
||||
<Button button="primary" type="submit" label={__('Submit')} disabled={!email} />
|
||||
<Button button="primary" label={__('Submit')} onClick={handleRegister} />{' '}
|
||||
{/* password input validation */}
|
||||
</div>
|
||||
<p className="help--card-actions">
|
||||
<I18nMessage
|
||||
|
@ -272,7 +319,7 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
By creating an account, you agree to our %terms% and confirm you're over the age of 13.
|
||||
</I18nMessage>
|
||||
</p>
|
||||
</Form>
|
||||
</fieldset-section>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
@ -287,11 +334,12 @@ export default function NotificationSettingsPage(props: Props) {
|
|||
backout={{ title: __('Wallet Sync'), backLabel: __('Back') }}
|
||||
>
|
||||
<div className="card-stack">
|
||||
{mode === DONE_MODE && <>{doneCard}</>}
|
||||
{mode === SIGN_IN_MODE && <>{signInCard}</>}
|
||||
{mode === SIGN_UP_MODE && <>{signUpCard}</>}
|
||||
{mode === MATH_MODE && <>{deriveCard}</>}
|
||||
{mode === VERIFY_MODE && <>{verifyCard}</>}
|
||||
{STEP === EMAIL_SCREEN && mode === SIGN_IN_MODE && <>{signInEmailCard}</>}
|
||||
{STEP === EMAIL_SCREEN && mode === SIGN_UP_MODE && <>{signUpEmailCard}</>}
|
||||
{STEP === PASSWORD_SCREEN && <>{passwordCard}</>}
|
||||
{STEP === REGISTERING_SCREEN && <>{registerCard}</>}
|
||||
{STEP === VERIFY_SCREEN && <>{verifyCard}</>}
|
||||
{STEP === SYNC_SCREEN && <>{syncCard}</>}
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
|
|
|
@ -6,9 +6,9 @@ import { Lbryio } from 'lbryinc';
|
|||
import Lbry from 'lbry';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import * as Lbrysync from 'lbrysync';
|
||||
import { safeStoreEncrypt, safeStoreDecrypt, getSavedPassword } from 'util/saved-passwords';
|
||||
import { safeStoreEncrypt, safeStoreDecrypt, getSavedPassword } from 'util/saved-passwords';
|
||||
|
||||
import { doWalletEncrypt, doWalletDecrypt } from 'redux/actions/wallet';
|
||||
import { doWalletEncrypt, doWalletDecrypt, doUpdateBalance } from 'redux/actions/wallet';
|
||||
import {
|
||||
selectSyncHash,
|
||||
selectGetSyncIsPending,
|
||||
|
@ -21,6 +21,7 @@ import Comments from 'comments';
|
|||
import { getSubsetFromKeysArray } from 'util/sync-settings';
|
||||
|
||||
let syncTimer = null;
|
||||
let verifyInterval = null;
|
||||
const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes
|
||||
const NO_WALLET_ERROR = 'no wallet found for this user';
|
||||
const BAD_PASSWORD_ERROR_NAME = 'InvalidPasswordError';
|
||||
|
@ -74,124 +75,188 @@ export function doSetDefaultAccount(success: () => void, failure: (string) => vo
|
|||
};
|
||||
}
|
||||
|
||||
export const doLbrysyncGetSalt = (email: string) => async (dispatch: Dispatch) => {
|
||||
const { fetchSaltSeed } = Lbrysync;
|
||||
// "signupCheck=true" - if signupCheck and salt, return fail. else if salt return salt.
|
||||
// export const doLbrysyncGetSalt = (email: string) => async (dispatch: Dispatch) => {
|
||||
// const { fetchSaltSeed } = Lbrysync;
|
||||
// dispatch({
|
||||
// type: ACTIONS.LSYNC_GET_SALT_STARTED,
|
||||
// });
|
||||
// try {
|
||||
// const saltOrError = await fetchSaltSeed(email);
|
||||
// console.log('REDUX Salt', saltOrError)
|
||||
// dispatch({
|
||||
// type: ACTIONS.LSYNC_GET_SALT_COMPLETED,
|
||||
// data: { email: email, saltSeed: saltOrError},
|
||||
// });
|
||||
// return saltOrError;
|
||||
// } catch (e) {
|
||||
// dispatch({
|
||||
// type: ACTIONS.LSYNC_GET_SALT_FAILED,
|
||||
// data: { email: email, saltError: 'Not Found'},
|
||||
// });
|
||||
// return 'not found';
|
||||
// }
|
||||
// };
|
||||
|
||||
export const doHandleEmail = (email, isSignUp) => async (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_GET_SALT_STARTED,
|
||||
type: ACTIONS.LSYNC_CHECK_EMAIL_STARTED,
|
||||
// CLEAR STUFF
|
||||
});
|
||||
try {
|
||||
const saltOrError = await fetchSaltSeed(email);
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_GET_SALT_COMPLETED,
|
||||
data: { email: email, saltSeed: saltOrError},
|
||||
});
|
||||
return saltOrError;
|
||||
} catch (e) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_GET_SALT_FAILED,
|
||||
data: { email: email, saltError: 'Not Found'},
|
||||
});
|
||||
return 'not found';
|
||||
const { fetchSaltSeed } = Lbrysync;
|
||||
if (isSignUp) {
|
||||
try {
|
||||
await fetchSaltSeed(email);
|
||||
// !!got salt seed
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_CHECK_EMAIL_FAILED,
|
||||
data: { emailError: 'Email Already Found' },
|
||||
});
|
||||
} catch (e) {
|
||||
// no salt, we're good.
|
||||
const seed = await ipcRenderer.invoke('invoke-get-salt-seed');
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_CHECK_EMAIL_COMPLETED,
|
||||
data: { candidateEmail: email, saltSeed: seed },
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Sign In
|
||||
try {
|
||||
const saltResponse = await fetchSaltSeed(email);
|
||||
// !!got salt seed
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_CHECK_EMAIL_COMPLETED,
|
||||
data: { candidateEmail: email, saltSeed: saltResponse.seed },
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('e', e.message);
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_CHECK_EMAIL_FAILED,
|
||||
data: { emailError: 'Email Not Found' },
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// register an email (eventually username)
|
||||
export const doLbrysyncRegister = (email: string, secrets: any, saltSeed: string) => async (dispatch: Dispatch) => {
|
||||
const { register } = Lbrysync;
|
||||
// started
|
||||
export const doLbrysyncSync = () => async (dispatch: Dispatch, getState: GetState) => {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_SYNC_STARTED,
|
||||
});
|
||||
// Wallet status
|
||||
const status = await Lbry.wallet_status();
|
||||
// if (status.isLocked) {
|
||||
// // error
|
||||
// }
|
||||
// return Lbry.wallet_unlock({ password });
|
||||
// .then((status) => {
|
||||
//
|
||||
//
|
||||
// }
|
||||
|
||||
// See if we should pull
|
||||
const { pullWallet } = Lbrysync;
|
||||
|
||||
// Pull from sync
|
||||
const walletInfo = await pullWallet();
|
||||
if (walletInfo) {
|
||||
console.log('walletInfo', walletInfo);
|
||||
/*
|
||||
wallet_state = WalletState(
|
||||
encrypted_wallet=response.json()['encryptedWallet'],
|
||||
sequence=response.json()['sequence'],
|
||||
)
|
||||
hmac = response.json()['hmac']
|
||||
return wallet_state, hmac
|
||||
*/
|
||||
// Lbry.wallet_import();
|
||||
// update sequence, others
|
||||
}
|
||||
const exported = await Lbry.wallet_export();
|
||||
// const encWallet = encrypt(exported, password)
|
||||
};
|
||||
|
||||
//register an email (eventually username)
|
||||
export const doLbrysyncRegister = (password: string) => async (dispatch: Dispatch, getState: GetState) => {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_REGISTER_STARTED,
|
||||
});
|
||||
const resultIfError = await register(email, secrets.providerPass, saltSeed);
|
||||
const encProviderPass = safeStoreEncrypt(secrets.providerPass);
|
||||
const encHmacKey = safeStoreEncrypt(secrets.hmacKey);
|
||||
const enctyptedRoot = safeStoreEncrypt(secrets.rootPassword);
|
||||
const registerData = {
|
||||
email,
|
||||
saltSeed,
|
||||
providerPass: encProviderPass,
|
||||
hmacKey: encHmacKey,
|
||||
rootPass: enctyptedRoot,
|
||||
};
|
||||
|
||||
if (!resultIfError) {
|
||||
const state = getState();
|
||||
const { sync } = state;
|
||||
const { candidateEmail: email, saltSeed } = sync;
|
||||
// started
|
||||
try {
|
||||
const secrets = await ipcRenderer.invoke('invoke-get-secrets', password, email, saltSeed);
|
||||
const encProviderKey = safeStoreEncrypt(secrets.providerKey);
|
||||
const encHmacKey = safeStoreEncrypt(secrets.hmacKey);
|
||||
const encDataKey = safeStoreEncrypt(secrets.dataKey);
|
||||
const enctyptedRoot = safeStoreEncrypt(password);
|
||||
const registerData = {
|
||||
email, // email
|
||||
saltSeed,
|
||||
providerKey: encProviderKey,
|
||||
hmacKey: encHmacKey,
|
||||
dataKey: encDataKey,
|
||||
rootPass: enctyptedRoot,
|
||||
};
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_REGISTER_COMPLETED,
|
||||
data: registerData,
|
||||
});
|
||||
} else {
|
||||
|
||||
return registerData;
|
||||
} catch (e) {
|
||||
console.log('e', e.message);
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_REGISTER_FAILED,
|
||||
data: resultIfError,
|
||||
data: 'ohno',
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// get token given username/password
|
||||
export const doLbrysyncAuthenticate =
|
||||
() => async (dispatch: Dispatch, getState: GetState) => {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_AUTH_STARTED,
|
||||
});
|
||||
const state = getState();
|
||||
const { lbrysync } = state;
|
||||
const { registeredEmail: email, encryptedProviderPass } = lbrysync;
|
||||
const status = await Lbry.status();
|
||||
const { installation_id: deviceId } = status;
|
||||
const password = safeStoreDecrypt(encryptedProviderPass);
|
||||
|
||||
const { getAuthToken } = Lbrysync;
|
||||
|
||||
const result: { token?: string, error?: string } = await getAuthToken(email, password, deviceId);
|
||||
|
||||
if (result.token) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_AUTH_COMPLETED,
|
||||
data: result.token,
|
||||
});
|
||||
export function doEmailVerifySubscribe(stop) {
|
||||
return (dispatch) => {
|
||||
if (stop) {
|
||||
clearInterval(verifyInterval);
|
||||
} else {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_AUTH_FAILED,
|
||||
data: result.error,
|
||||
});
|
||||
dispatch(doLbrysyncAuthenticate());
|
||||
verifyInterval = setInterval(() => dispatch(doLbrysyncAuthenticate()), 5000);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const doGenerateSaltSeed = () => async (dispatch: Dispatch) => {
|
||||
const result = await ipcRenderer.invoke('invoke-get-salt-seed');
|
||||
return result;
|
||||
};
|
||||
|
||||
export const doDeriveSecrets = (rootPassword: string, email: string, saltSeed: string) => async (dispatch: Dispatch) =>
|
||||
{
|
||||
// get token given username/password
|
||||
export const doLbrysyncAuthenticate = () => async (dispatch: Dispatch, getState: GetState) => {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_DERIVE_STARTED,
|
||||
type: ACTIONS.LSYNC_AUTH_STARTED,
|
||||
});
|
||||
try {
|
||||
const result = await ipcRenderer.invoke('invoke-get-secrets', rootPassword, email, saltSeed);
|
||||
const state = getState();
|
||||
const { sync } = state;
|
||||
const { registeredEmail: email, encryptedProviderKey } = sync;
|
||||
const status = await Lbry.status();
|
||||
const { installation_id: deviceId } = status;
|
||||
const password = safeStoreDecrypt(encryptedProviderKey);
|
||||
|
||||
const data = {
|
||||
hmacKey: result.hmacKey,
|
||||
rootPassword,
|
||||
providerPass: result.lbryIdPassword,
|
||||
};
|
||||
const { getAuthToken } = Lbrysync;
|
||||
|
||||
const result: { token?: string, error?: string } = await getAuthToken(email, password, deviceId);
|
||||
|
||||
if (result.token) {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_DERIVE_COMPLETED,
|
||||
data,
|
||||
type: ACTIONS.LSYNC_AUTH_COMPLETED,
|
||||
data: result.token,
|
||||
});
|
||||
return data;
|
||||
} catch (e) {
|
||||
clearInterval(verifyInterval);
|
||||
} else {
|
||||
dispatch({
|
||||
type: ACTIONS.LSYNC_DERIVE_FAILED,
|
||||
data: {
|
||||
error: e,
|
||||
},
|
||||
type: ACTIONS.LSYNC_AUTH_FAILED,
|
||||
data: result.error,
|
||||
});
|
||||
return { error: e.message };
|
||||
}
|
||||
};
|
||||
|
||||
// replaced with
|
||||
export function doSetSync(oldHash: string, newHash: string, data: any) {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
|
@ -441,6 +506,54 @@ export function doSyncApply(syncHash: string, syncData: any, password: string) {
|
|||
};
|
||||
}
|
||||
|
||||
export function doWalletExport(password?: string) {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.WALLET_EXPORT_STARTED,
|
||||
});
|
||||
Lbry.wallet_export({ password })
|
||||
.then((walletData) => {
|
||||
// if password, etc etc
|
||||
// return data
|
||||
dispatch({
|
||||
type: ACTIONS.WALLET_EXPORT_COMPLETED,
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
dispatch({
|
||||
type: ACTIONS.WALLET_EXPORT_FAILED,
|
||||
data: {
|
||||
error: 'Wallet Export Failed',
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doWalletImport(data: string, password?: string) {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
type: ACTIONS.WALLET_IMPORT_STARTED,
|
||||
});
|
||||
Lbry.wallet_import({ data, password })
|
||||
.then((walletData) => {
|
||||
// if password, etc etc
|
||||
// return data
|
||||
dispatch({
|
||||
type: ACTIONS.WALLET_IMPORT_COMPLETED,
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
dispatch({
|
||||
type: ACTIONS.WALLET_IMPORT_FAILED,
|
||||
data: {
|
||||
error: 'Wallet Import Failed',
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doCheckSync() {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch({
|
||||
|
|
|
@ -19,7 +19,7 @@ export const buildSharedStateMiddleware =
|
|||
const actionResult = next(action);
|
||||
// Call `getState` after calling `next` to ensure the state has updated in response to the action
|
||||
function runPreferences() {
|
||||
const nextState: { settings: any } = getState(); // bring in lbrysync
|
||||
const nextState: { settings: any } = getState(); // bring in lbrysync?
|
||||
const syncEnabled =
|
||||
nextState.settings && nextState.settings.clientSettings && nextState.settings.clientSettings.enable_sync;
|
||||
const signedIn = false; // get from new sync system newsync
|
||||
|
|
|
@ -17,7 +17,14 @@ const defaultState = {
|
|||
hashChanged: false,
|
||||
fatalError: false,
|
||||
// lbrysync
|
||||
syncProvider: null,
|
||||
// --email
|
||||
checkingEmail: false,
|
||||
candidateEmail: null,
|
||||
emailError: null,
|
||||
//syncProvider: null,
|
||||
gettingSalt: false,
|
||||
saltSeed: null,
|
||||
saltError: null,
|
||||
// reg
|
||||
registering: false,
|
||||
registeredEmail: null,
|
||||
|
@ -31,10 +38,7 @@ const defaultState = {
|
|||
encryptedHmacKey: null,
|
||||
encryptedRoot: null,
|
||||
encryptedProviderPass: null,
|
||||
// salt
|
||||
gettingSalt: false,
|
||||
saltSeed: null,
|
||||
saltError: null,
|
||||
encryptedDataKey: null,
|
||||
};
|
||||
|
||||
reducers[ACTIONS.SYNC_STATE_POPULATE] = (state) => {
|
||||
|
@ -136,17 +140,21 @@ reducers[ACTIONS.LSYNC_REGISTER_COMPLETED] = (state, action) => ({
|
|||
...state,
|
||||
registeredEmail: action.data.email,
|
||||
encryptedHmacKey: action.data.hmacKey,
|
||||
encryptedProviderPass: action.data.providerPass,
|
||||
encryptedDataKey: action.data.dataKey,
|
||||
encryptedProviderKey: action.data.providerKey,
|
||||
encryptedRoot: action.data.rootPass,
|
||||
saltSeed: action.data.saltSeed,
|
||||
syncProvider: action.data.syncProvider,
|
||||
registering: false,
|
||||
});
|
||||
// clear attempt
|
||||
reducers[ACTIONS.LSYNC_REGISTER_FAILED] = (state, action) => ({
|
||||
...state,
|
||||
registeredEmail: null,
|
||||
registering: false,
|
||||
registerError: action.data.error,
|
||||
});
|
||||
// Auth
|
||||
// Auth
|
||||
reducers[ACTIONS.LSYNC_AUTH_STARTED] = (state) => ({
|
||||
...state,
|
||||
isAuthenticating: true,
|
||||
|
@ -161,12 +169,13 @@ reducers[ACTIONS.LSYNC_AUTH_FAILED] = (state, action) => ({
|
|||
authError: action.data,
|
||||
isAuthenticating: false,
|
||||
});
|
||||
// derive
|
||||
// derive
|
||||
reducers[ACTIONS.LSYNC_DERIVE_STARTED] = (state) => ({
|
||||
...state,
|
||||
derivingKeys: true,
|
||||
deriveError: null,
|
||||
});
|
||||
// add more keys
|
||||
reducers[ACTIONS.LSYNC_DERIVE_COMPLETED] = (state, action) => ({
|
||||
...state,
|
||||
derivingKeys: false,
|
||||
|
@ -176,7 +185,7 @@ reducers[ACTIONS.LSYNC_DERIVE_FAILED] = (state, action) => ({
|
|||
deriveError: action.data.error,
|
||||
derivingKeys: false,
|
||||
});
|
||||
// salt
|
||||
// salt util
|
||||
reducers[ACTIONS.LSYNC_GET_SALT_STARTED] = (state) => ({
|
||||
...state,
|
||||
gettingSalt: true,
|
||||
|
@ -191,6 +200,27 @@ reducers[ACTIONS.LSYNC_GET_SALT_FAILED] = (state, action) => ({
|
|||
saltError: action.data.error,
|
||||
gettingSalt: false,
|
||||
});
|
||||
// email
|
||||
reducers[ACTIONS.LSYNC_CHECK_EMAIL_STARTED] = (state) => ({
|
||||
...state,
|
||||
checkingEmail: true,
|
||||
emailError: null,
|
||||
candidateEmail: null,
|
||||
saltSeed: null,
|
||||
});
|
||||
reducers[ACTIONS.LSYNC_CHECK_EMAIL_COMPLETED] = (state, action) => ({
|
||||
...state,
|
||||
checkingEmail: false,
|
||||
candidateEmail: action.data.candidateEmail,
|
||||
saltSeed: action.data.saltSeed,
|
||||
});
|
||||
reducers[ACTIONS.LSYNC_CHECK_EMAIL_FAILED] = (state, action) => ({
|
||||
...state,
|
||||
checkingEmail: false,
|
||||
emailError: action.data.emailError,
|
||||
candidateEmail: null,
|
||||
saltSeed: null,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SYNC_RESET] = () => defaultState;
|
||||
|
||||
|
|
|
@ -1,49 +1,56 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
const selectState = state => state.sync || {};
|
||||
const selectState = (state) => state.sync || {};
|
||||
|
||||
export const selectHasSyncedWallet = createSelector(selectState, state => state.hasSyncedWallet);
|
||||
export const selectHasSyncedWallet = createSelector(selectState, (state) => state.hasSyncedWallet);
|
||||
|
||||
export const selectSyncHash = createSelector(selectState, state => state.syncHash);
|
||||
export const selectSyncHash = createSelector(selectState, (state) => state.syncHash);
|
||||
|
||||
export const selectSyncData = createSelector(selectState, state => state.syncData);
|
||||
export const selectSyncData = createSelector(selectState, (state) => state.syncData);
|
||||
|
||||
export const selectSetSyncErrorMessage = createSelector(selectState, state => state.setSyncErrorMessage);
|
||||
export const selectSetSyncErrorMessage = createSelector(selectState, (state) => state.setSyncErrorMessage);
|
||||
|
||||
export const selectGetSyncErrorMessage = createSelector(selectState, state => state.getSyncErrorMessage);
|
||||
export const selectGetSyncErrorMessage = createSelector(selectState, (state) => state.getSyncErrorMessage);
|
||||
|
||||
export const selectGetSyncIsPending = createSelector(selectState, state => state.getSyncIsPending);
|
||||
export const selectGetSyncIsPending = createSelector(selectState, (state) => state.getSyncIsPending);
|
||||
|
||||
export const selectSetSyncIsPending = createSelector(selectState, state => state.setSyncIsPending);
|
||||
export const selectSetSyncIsPending = createSelector(selectState, (state) => state.setSyncIsPending);
|
||||
|
||||
export const selectHashChanged = createSelector(selectState, state => state.hashChanged);
|
||||
export const selectHashChanged = createSelector(selectState, (state) => state.hashChanged);
|
||||
|
||||
export const selectSyncApplyIsPending = createSelector(selectState, state => state.syncApplyIsPending);
|
||||
export const selectSyncApplyIsPending = createSelector(selectState, (state) => state.syncApplyIsPending);
|
||||
|
||||
export const selectSyncApplyErrorMessage = createSelector(selectState, state => state.syncApplyErrorMessage);
|
||||
export const selectSyncApplyErrorMessage = createSelector(selectState, (state) => state.syncApplyErrorMessage);
|
||||
|
||||
export const selectSyncApplyPasswordError = createSelector(selectState, state => state.syncApplyPasswordError);
|
||||
export const selectSyncApplyPasswordError = createSelector(selectState, (state) => state.syncApplyPasswordError);
|
||||
|
||||
export const selectSyncIsLocked = createSelector(selectState, state => state.syncLocked);
|
||||
export const selectSyncIsLocked = createSelector(selectState, (state) => state.syncLocked);
|
||||
|
||||
export const selectPrefsReady = createSelector(selectState, state => state.prefsReady);
|
||||
export const selectPrefsReady = createSelector(selectState, (state) => state.prefsReady);
|
||||
|
||||
export const selectSyncFatalError = createSelector(selectState, state => state.fatalError);
|
||||
export const selectSyncFatalError = createSelector(selectState, (state) => state.fatalError);
|
||||
// lbrysync
|
||||
export const selectLbrySyncRegistering = createSelector(selectState, (state) => state.registering);
|
||||
export const selectLbrySyncEmail = createSelector(selectState, (state) => state.registeredEmail);
|
||||
export const selectLbrySyncRegisterError = createSelector(selectState, (state) => state.registerError);
|
||||
|
||||
// begin
|
||||
export const selectLbrySyncCheckingEmail = createSelector(selectState, (state) => state.checkingEmail);
|
||||
export const selectLbrySyncEmailError = createSelector(selectState, (state) => state.emailError);
|
||||
export const selectLbrySyncEmail = createSelector(selectState, (state) => state.registeredEmail);
|
||||
export const selectLbrySyncEmailCandidate = createSelector(selectState, (state) => state.candidateEmail);
|
||||
export const selectLbrySyncGettingSalt = createSelector(selectState, (state) => state.gettingSalt);
|
||||
export const selectLbrySyncSaltError = createSelector(selectState, (state) => state.saltError);
|
||||
export const selectLbrySyncSaltSeed = createSelector(selectState, (state) => state.saltSeed);
|
||||
|
||||
export const selectLbrySyncIsAuthenticating = createSelector(selectState, (state) => state.isAuthenticating);
|
||||
|
||||
export const selectLbrySyncAuthError = createSelector(selectState, (state) => state.authError);
|
||||
export const selectLbrySyncToken = createSelector(selectState, (state) => state.authToken);
|
||||
|
||||
// password
|
||||
export const selectLbrySyncDerivingKeys = createSelector(selectState, (state) => state.derivingKeys);
|
||||
export const selectLbrySyncEncryptedHmacKey = createSelector(selectState, (state) => state.encryptedHmacKey);
|
||||
export const selectLbrySyncEncryptedRoot = createSelector(selectState, (state) => state.encryptedRoot);
|
||||
export const selectLbrySyncEncryptedProviderPass = createSelector(selectState, (state) => state.encryptedProviderPass);
|
||||
// register
|
||||
export const selectLbrySyncRegistering = createSelector(selectState, (state) => state.registering);
|
||||
export const selectLbrySyncRegisterError = createSelector(selectState, (state) => state.registerError);
|
||||
// auth
|
||||
export const selectLbrySyncIsAuthenticating = createSelector(selectState, (state) => state.isAuthenticating);
|
||||
|
||||
export const selectLbrySyncAuthError = createSelector(selectState, (state) => state.authError);
|
||||
|
||||
export const selectLbrySyncToken = createSelector(selectState, (state) => state.authToken);
|
||||
// push/pull?
|
||||
|
|
Loading…
Reference in a new issue