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;
|
const DEFAULT_MAXMEM = MAXMEM_MULTIPLIER * SCRYPT_N * SCRYPT_R;
|
||||||
|
|
||||||
function getKeyParts(key) {
|
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 hmacKey = key.slice(KEY_LENGTH, KEY_LENGTH * 2).toString('base64');
|
||||||
const dataKey = key.slice(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);
|
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);
|
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) {
|
export function walletHmac(inputString, hmacKey) {
|
||||||
const hmac = crypto.createHmac('sha256', inputString.toString('utf8'));
|
const res = crypto.createHmac('sha256', hmacKey)
|
||||||
const res = hmac.digest('hex');
|
.update(inputString.toString('utf8'))
|
||||||
|
.digest('hex');
|
||||||
|
console.log('hmac res', res)
|
||||||
return 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 test from 'tape';
|
||||||
// import sync from '../sync.js';
|
// 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() {
|
export default function doTest() {
|
||||||
|
|
||||||
test('Generate sync seed', (assert) => {
|
test('Generate sync seed', (assert) => {
|
||||||
const seed = generateSaltSeed();
|
const seed = generateSaltSeed();
|
||||||
console.log('seed', seed);
|
console.log('seed', seed);
|
||||||
|
@ -28,12 +29,13 @@ export default function doTest() {
|
||||||
const email = 'example@example.com';
|
const email = 'example@example.com';
|
||||||
|
|
||||||
const expectedHmacKey = 'bCxUIryLK0Lf9nKg9yiZDlGleMuGJkadLzTje1PAI+8='; //base64
|
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) {
|
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.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();
|
assert.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,13 +46,25 @@ export default function doTest() {
|
||||||
const hmacKey = 'bCxUIryLK0Lf9nKg9yiZDlGleMuGJkadLzTje1PAI+8=';
|
const hmacKey = 'bCxUIryLK0Lf9nKg9yiZDlGleMuGJkadLzTje1PAI+8=';
|
||||||
const sequence = 1;
|
const sequence = 1;
|
||||||
const walletState = `zo4MTkyOjE2OjE68QlIU76+W91/v/F1tu8h+kGB0Ee`;
|
const walletState = `zo4MTkyOjE2OjE68QlIU76+W91/v/F1tu8h+kGB0Ee`;
|
||||||
const expectedHmacHex = '52edbad5b0f9d8cf6189795702790cc2cb92060be24672913ab3e4b69c03698b';
|
const expectedHmacHex = '9fe70ebdeaf85b3afe5ae42e52f946acc54ded0350acacdded821845217839d4';
|
||||||
|
|
||||||
const input_str = `${sequence}:${walletState}`;
|
const input_str = `${sequence}:${walletState}`;
|
||||||
const hmacHex = walletHmac(input_str);
|
const hmacHex = walletHmac(input_str, hmacKey);
|
||||||
assert.equal(hmacHex, expectedHmacHex);
|
assert.equal(hmacHex, expectedHmacHex);
|
||||||
assert.end();
|
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();
|
doTest();
|
||||||
|
|
|
@ -2334,5 +2334,13 @@
|
||||||
"Doing Math": "Doing Math",
|
"Doing Math": "Doing Math",
|
||||||
"Hold on, doing some math.": "Hold on, doing some math.",
|
"Hold on, doing some math.": "Hold on, doing some math.",
|
||||||
"You are signed in as %email%.": "You are signed in as %email%.",
|
"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--"
|
"--end--": "--end--"
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ export default function HeaderMenuButtons(props: HeaderMenuButtonProps) {
|
||||||
<MenuList className="menu__list--header">
|
<MenuList className="menu__list--header">
|
||||||
<HeaderMenuLink page={PAGES.UPLOAD} icon={ICONS.PUBLISH} name={__('Upload')} />
|
<HeaderMenuLink page={PAGES.UPLOAD} icon={ICONS.PUBLISH} name={__('Upload')} />
|
||||||
<HeaderMenuLink page={PAGES.CHANNEL_NEW} icon={ICONS.CHANNEL} name={__('New Channel')} />
|
<HeaderMenuLink page={PAGES.CHANNEL_NEW} icon={ICONS.CHANNEL} name={__('New Channel')} />
|
||||||
|
<HeaderMenuLink page={PAGES.SETTINGS_SYNC} icon={ICONS.GAMING} name={__('Sign In')} />
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</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_COMPLETED = 'DO_UTXO_CONSOLIDATE_COMPLETED';
|
||||||
export const DO_UTXO_CONSOLIDATE_FAILED = 'DO_UTXO_CONSOLIDATE_FAILED';
|
export const DO_UTXO_CONSOLIDATE_FAILED = 'DO_UTXO_CONSOLIDATE_FAILED';
|
||||||
export const PENDING_CONSOLIDATED_TXOS_UPDATED = 'PENDING_CONSOLIDATED_TXOS_UPDATED';
|
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
|
// Claims
|
||||||
export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';
|
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_STARTED = 'LSYNC_GET_SALT_STARTED';
|
||||||
export const LSYNC_GET_SALT_COMPLETED = 'LSYNC_GET_SALT_COMPLETED'; // got salt
|
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_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
|
// 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) => {
|
getMediaType: (contentType: ?string, fileName: ?string) => {
|
||||||
if (fileName) {
|
if (fileName && fileName.split('.').length > 1) {
|
||||||
const formats = [
|
const formats = [
|
||||||
[/\.(mp4|m4v|webm|flv|f4v|ogv)$/i, 'video'],
|
[/\.(mp4|m4v|webm|flv|f4v|ogv)$/i, 'video'],
|
||||||
[/\.(mp3|m4a|aac|wav|flac|ogg|opus)$/i, 'audio'],
|
[/\.(mp3|m4a|aac|wav|flac|ogg|opus)$/i, 'audio'],
|
||||||
|
@ -111,6 +111,8 @@ const Lbry = {
|
||||||
wallet_list: (params = {}) => daemonCallWithResult('wallet_list', params),
|
wallet_list: (params = {}) => daemonCallWithResult('wallet_list', params),
|
||||||
wallet_send: (params = {}) => daemonCallWithResult('wallet_send', params),
|
wallet_send: (params = {}) => daemonCallWithResult('wallet_send', params),
|
||||||
wallet_status: (params = {}) => daemonCallWithResult('wallet_status', 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_is_mine: (params = {}) => daemonCallWithResult('address_is_mine', params),
|
||||||
address_unused: (params = {}) => daemonCallWithResult('address_unused', params),
|
address_unused: (params = {}) => daemonCallWithResult('address_unused', params),
|
||||||
address_list: (params = {}) => daemonCallWithResult('address_list', 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 DUPLICATE_EMAIL = 'duplicate_email';
|
||||||
const UNKNOWN_ERROR = 'unknown_api_error';
|
const UNKNOWN_ERROR = 'unknown_api_error';
|
||||||
const NOT_FOUND = 'not_found';
|
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 API_VERSION = 3;
|
||||||
const POST = 'POST';
|
const POST = 'POST';
|
||||||
|
@ -41,43 +41,47 @@ export async function getAuthToken(email, password, deviceId) {
|
||||||
|
|
||||||
export async function register(email, password, saltSeed) {
|
export async function register(email, password, saltSeed) {
|
||||||
try {
|
try {
|
||||||
await callWithResult(POST, REGISTER_ENDPOINT, { email, password, clientSaltSeed: saltSeed });
|
const result = await callWithResult(POST, REGISTER_ENDPOINT, { email, password, clientSaltSeed: saltSeed });
|
||||||
return;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { error: e.message };
|
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) {
|
export async function pullWallet(token) {
|
||||||
try {
|
try {
|
||||||
await callWithResult(GET, REGISTER_ENDPOINT, { token });
|
const result = await callWithResult(GET, WALLET_ENDPOINT, { token });
|
||||||
return;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { error: e.message };
|
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 = {}) {
|
function callWithResult(method, endpoint, params = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
apiCall(
|
return apiCall(
|
||||||
method,
|
method,
|
||||||
endpoint,
|
endpoint,
|
||||||
params,
|
params,
|
||||||
(result) => {
|
(result) => {
|
||||||
|
console.log('cwr result', result);
|
||||||
resolve(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)
|
return fetch(`${Lbrysync.apiUrl}${endpoint}${searchString}`, options)
|
||||||
.then(handleResponse)
|
.then(handleResponse)
|
||||||
.then((response) => {
|
.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) {
|
function handleResponse(response) {
|
||||||
if (response.status >= 200 && response.status < 300) {
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
console.log('200+');
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,25 +17,18 @@ import {
|
||||||
selectLbrySyncEncryptedHmacKey,
|
selectLbrySyncEncryptedHmacKey,
|
||||||
selectLbrySyncEncryptedRoot,
|
selectLbrySyncEncryptedRoot,
|
||||||
selectLbrySyncEncryptedProviderPass,
|
selectLbrySyncEncryptedProviderPass,
|
||||||
|
selectLbrySyncCheckingEmail,
|
||||||
|
selectLbrySyncEmailError,
|
||||||
|
selectLbrySyncEmailCandidate,
|
||||||
} from 'redux/selectors/sync';
|
} from 'redux/selectors/sync';
|
||||||
|
|
||||||
import {
|
import { doHandleEmail, doLbrysyncRegister, doLbrysyncAuthenticate, doEmailVerifySubscribe } from 'redux/actions/sync';
|
||||||
doLbrysyncGetSalt,
|
|
||||||
doLbrysyncRegister,
|
|
||||||
doGenerateSaltSeed,
|
|
||||||
doDeriveSecrets,
|
|
||||||
doLbrysyncAuthenticate,
|
|
||||||
} from 'redux/actions/sync';
|
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
isWalletEncrypted: selectWalletIsEncrypted(state),
|
isWalletEncrypted: selectWalletIsEncrypted(state),
|
||||||
registering: selectLbrySyncRegistering(state),
|
registering: selectLbrySyncRegistering(state),
|
||||||
registeredEmail: selectLbrySyncEmail(state),
|
|
||||||
registerError: selectLbrySyncRegisterError(state),
|
registerError: selectLbrySyncRegisterError(state),
|
||||||
|
|
||||||
gettingSalt: selectLbrySyncGettingSalt(state),
|
|
||||||
saltError: selectLbrySyncSaltError(state),
|
|
||||||
saltSeed: selectLbrySyncSaltSeed(state),
|
|
||||||
token: selectLbrySyncToken(state),
|
token: selectLbrySyncToken(state),
|
||||||
|
|
||||||
authenticating: selectLbrySyncIsAuthenticating(state),
|
authenticating: selectLbrySyncIsAuthenticating(state),
|
||||||
|
@ -46,16 +39,25 @@ const select = (state) => ({
|
||||||
encHmacKey: selectLbrySyncEncryptedHmacKey(state), // ?
|
encHmacKey: selectLbrySyncEncryptedHmacKey(state), // ?
|
||||||
encRootPass: selectLbrySyncEncryptedRoot(state),
|
encRootPass: selectLbrySyncEncryptedRoot(state),
|
||||||
encProviderPass: selectLbrySyncEncryptedProviderPass(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) => ({
|
const perform = (dispatch) => ({
|
||||||
encryptWallet: () => dispatch(doNotifyEncryptWallet()),
|
encryptWallet: () => dispatch(doNotifyEncryptWallet()),
|
||||||
decryptWallet: () => dispatch(doNotifyDecryptWallet()),
|
decryptWallet: () => dispatch(doNotifyDecryptWallet()),
|
||||||
getSalt: (email) => dispatch(doLbrysyncGetSalt(email)),
|
handleEmail: (email, signUp) => dispatch(doHandleEmail(email, signUp)),
|
||||||
generateSaltSeed: () => dispatch(doGenerateSaltSeed()),
|
|
||||||
authenticate: () => dispatch(doLbrysyncAuthenticate()),
|
authenticate: () => dispatch(doLbrysyncAuthenticate()),
|
||||||
deriveSecrets: (p, e, s) => dispatch(doDeriveSecrets(p, e, s)),
|
waitForVerify: (stop) => dispatch(doEmailVerifySubscribe(stop)),
|
||||||
register: (email, secrets, saltseed) => dispatch(doLbrysyncRegister(email, secrets, saltseed)),
|
// deriveSecrets: (p, e, s) => dispatch(doDeriveSecrets(p, e, s)),
|
||||||
|
register: (password) => dispatch(doLbrysyncRegister(password)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(SettingsSync);
|
export default connect(select, perform)(SettingsSync);
|
||||||
|
|
|
@ -12,7 +12,6 @@ type Props = {
|
||||||
encryptWallet: (string) => void,
|
encryptWallet: (string) => void,
|
||||||
decryptWallet: (string) => void,
|
decryptWallet: (string) => void,
|
||||||
registering: boolean,
|
registering: boolean,
|
||||||
email: string,
|
|
||||||
registerError: string,
|
registerError: string,
|
||||||
token: string,
|
token: string,
|
||||||
authenticating: boolean,
|
authenticating: boolean,
|
||||||
|
@ -22,108 +21,117 @@ type Props = {
|
||||||
encRootPass: string,
|
encRootPass: string,
|
||||||
encProviderPass: string,
|
encProviderPass: string,
|
||||||
getSalt: (string) => void,
|
getSalt: (string) => void,
|
||||||
gettingSalt: boolean,
|
|
||||||
saltError: string,
|
|
||||||
saltSeed: string,
|
|
||||||
deriveSecrets: (string, string, string) => void, // something
|
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 { } = props;
|
||||||
const {
|
const {
|
||||||
walletEncrypted,
|
|
||||||
encryptWallet,
|
|
||||||
decryptWallet,
|
|
||||||
registering,
|
registering,
|
||||||
registeredEmail,
|
|
||||||
registerError,
|
registerError,
|
||||||
token,
|
token,
|
||||||
authenticating,
|
authenticating,
|
||||||
authError,
|
authError,
|
||||||
authenticate,
|
authenticate,
|
||||||
derivingKeys,
|
|
||||||
encHmacKey, // ?
|
|
||||||
encRootPass,
|
encRootPass,
|
||||||
encProviderPass,
|
|
||||||
getSalt,
|
|
||||||
generateSaltSeed,
|
|
||||||
deriveSecrets,
|
|
||||||
gettingSalt,
|
|
||||||
saltError,
|
|
||||||
saltSeed,
|
|
||||||
register,
|
register,
|
||||||
|
// begin
|
||||||
|
// .. email
|
||||||
|
registeredEmail,
|
||||||
|
handleEmail,
|
||||||
|
checkingEmail,
|
||||||
|
candidateEmail,
|
||||||
|
saltSeed,
|
||||||
|
emailError,
|
||||||
|
// password
|
||||||
|
// verify
|
||||||
|
waitForVerify,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Register / auth
|
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 [email, setEmail] = React.useState();
|
||||||
const [pass, setPass] = React.useState();
|
const [pass, setPass] = React.useState();
|
||||||
const [showPass, setShowPass] = React.useState(false);
|
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(() => {
|
React.useEffect(() => {
|
||||||
let interval;
|
if (registeredEmail && !token) {
|
||||||
if (!token && registeredEmail) {
|
waitForVerify();
|
||||||
interval = setInterval(() => {
|
|
||||||
console.log('doauthint');
|
|
||||||
authenticate();
|
|
||||||
}, 5000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval);
|
waitForVerify(true);
|
||||||
};
|
};
|
||||||
}, [registeredEmail, token, authenticate]);
|
}, [registeredEmail, token]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (token && registeredEmail) {
|
if (token) {
|
||||||
setMode(DONE_MODE);
|
// sign up
|
||||||
|
// what
|
||||||
|
// pushAndStart();
|
||||||
|
// what
|
||||||
|
//
|
||||||
}
|
}
|
||||||
}, [registeredEmail, token, setMode]);
|
}, [token]);
|
||||||
|
|
||||||
const handleSignUp = async () => {
|
const handleRegister = (e) => {
|
||||||
// get salt for email to make sure
|
register(pass);
|
||||||
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 handleSignIn = async () => {
|
const handleSignUpEmail = async () => {
|
||||||
// get saltseed for email
|
// get salt for email to make sure email doesn't exist
|
||||||
// saltSeed = getSaltSeed()
|
handleEmail(email, true);
|
||||||
// -- if error, report email not registered - sign up?
|
};
|
||||||
// salt = generateSalt(seed)
|
|
||||||
// passwords = deriveKeys(root, email, saltSeed);
|
const handleSignInEmail = () => {
|
||||||
// token = authenticate(email, servicePassword, deviceId)
|
handleEmail(email, false);
|
||||||
// store [token, rootPassword, servicePassword, HmacKey, saltSeed, salt, registeredEmail]
|
|
||||||
// kick off sync pull
|
|
||||||
// -- possibly merge conflicts
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const doneCard = (
|
const doneCard = (
|
||||||
|
@ -150,11 +158,19 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const deriveCard = (
|
const registerCard = (
|
||||||
<Card title={__('Doing Math')} subtitle={__('Hold on, doing some math.')} actions={<div>Math...</div>} />
|
<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
|
<Card
|
||||||
title={__('Sign In')}
|
title={__('Sign In')}
|
||||||
subtitle={
|
subtitle={
|
||||||
|
@ -172,7 +188,7 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
<div>
|
<div>
|
||||||
<Form onSubmit={handleSignIn} className="section">
|
<Form onSubmit={handleSignInEmail} className="section">
|
||||||
<FormField
|
<FormField
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder={__('yourstruly@example.com')}
|
placeholder={__('yourstruly@example.com')}
|
||||||
|
@ -181,25 +197,18 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
label={__('Email')}
|
label={__('Email')}
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
helper={emailError && emailError}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
type={showPass ? 'text' : 'password'}
|
type={'text'}
|
||||||
name="root_password"
|
name="sync_provider"
|
||||||
inputButton={
|
disabled
|
||||||
<>
|
label={__('Sync Provider Url')}
|
||||||
<Button
|
value={'https://dev.lbry.id'}
|
||||||
icon={showPass ? ICONS.EYE : ICONS.EYE_OFF}
|
|
||||||
onClick={() => setShowPass(!showPass)}
|
|
||||||
className={'editable-text__input-button'}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
label={__('Password')}
|
|
||||||
value={pass}
|
|
||||||
onChange={(e) => setPass(e.target.value)}
|
onChange={(e) => setPass(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className="section__actions">
|
<div className="section__actions">
|
||||||
<Button button="primary" type="submit" label={__('Submit')} disabled={!email} />
|
<Button button="primary" type="submit" label={__('Submit')} disabled={checkingEmail} />
|
||||||
</div>
|
</div>
|
||||||
<p className="help--card-actions">
|
<p className="help--card-actions">
|
||||||
<I18nMessage
|
<I18nMessage
|
||||||
|
@ -207,7 +216,7 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
terms: <Button button="link" href="https://www.lbry.com/termsofservice" label={__('terms')} />,
|
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>
|
</I18nMessage>
|
||||||
</p>
|
</p>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -216,7 +225,7 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const signUpCard = (
|
const signUpEmailCard = (
|
||||||
<Card
|
<Card
|
||||||
title={__('Sign Up')}
|
title={__('Sign Up')}
|
||||||
subtitle={
|
subtitle={
|
||||||
|
@ -234,7 +243,7 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
<div>
|
<div>
|
||||||
<Form onSubmit={handleSignUp} className="section">
|
<Form onSubmit={handleSignUpEmail} className="section">
|
||||||
<FormField
|
<FormField
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder={__('yourstruly@example.com')}
|
placeholder={__('yourstruly@example.com')}
|
||||||
|
@ -243,9 +252,46 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
label={__('Email')}
|
label={__('Email')}
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
helper={emailError && emailError}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<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"
|
name="root_password"
|
||||||
inputButton={
|
inputButton={
|
||||||
<>
|
<>
|
||||||
|
@ -261,7 +307,8 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
onChange={(e) => setPass(e.target.value)}
|
onChange={(e) => setPass(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className="section__actions">
|
<div className="section__actions">
|
||||||
<Button button="primary" type="submit" label={__('Submit')} disabled={!email} />
|
<Button button="primary" label={__('Submit')} onClick={handleRegister} />{' '}
|
||||||
|
{/* password input validation */}
|
||||||
</div>
|
</div>
|
||||||
<p className="help--card-actions">
|
<p className="help--card-actions">
|
||||||
<I18nMessage
|
<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.
|
By creating an account, you agree to our %terms% and confirm you're over the age of 13.
|
||||||
</I18nMessage>
|
</I18nMessage>
|
||||||
</p>
|
</p>
|
||||||
</Form>
|
</fieldset-section>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -287,11 +334,12 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
backout={{ title: __('Wallet Sync'), backLabel: __('Back') }}
|
backout={{ title: __('Wallet Sync'), backLabel: __('Back') }}
|
||||||
>
|
>
|
||||||
<div className="card-stack">
|
<div className="card-stack">
|
||||||
{mode === DONE_MODE && <>{doneCard}</>}
|
{STEP === EMAIL_SCREEN && mode === SIGN_IN_MODE && <>{signInEmailCard}</>}
|
||||||
{mode === SIGN_IN_MODE && <>{signInCard}</>}
|
{STEP === EMAIL_SCREEN && mode === SIGN_UP_MODE && <>{signUpEmailCard}</>}
|
||||||
{mode === SIGN_UP_MODE && <>{signUpCard}</>}
|
{STEP === PASSWORD_SCREEN && <>{passwordCard}</>}
|
||||||
{mode === MATH_MODE && <>{deriveCard}</>}
|
{STEP === REGISTERING_SCREEN && <>{registerCard}</>}
|
||||||
{mode === VERIFY_MODE && <>{verifyCard}</>}
|
{STEP === VERIFY_SCREEN && <>{verifyCard}</>}
|
||||||
|
{STEP === SYNC_SCREEN && <>{syncCard}</>}
|
||||||
</div>
|
</div>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { ipcRenderer } from 'electron';
|
||||||
import * as Lbrysync from 'lbrysync';
|
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 {
|
import {
|
||||||
selectSyncHash,
|
selectSyncHash,
|
||||||
selectGetSyncIsPending,
|
selectGetSyncIsPending,
|
||||||
|
@ -21,6 +21,7 @@ import Comments from 'comments';
|
||||||
import { getSubsetFromKeysArray } from 'util/sync-settings';
|
import { getSubsetFromKeysArray } from 'util/sync-settings';
|
||||||
|
|
||||||
let syncTimer = null;
|
let syncTimer = null;
|
||||||
|
let verifyInterval = null;
|
||||||
const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes
|
const SYNC_INTERVAL = 1000 * 60 * 5; // 5 minutes
|
||||||
const NO_WALLET_ERROR = 'no wallet found for this user';
|
const NO_WALLET_ERROR = 'no wallet found for this user';
|
||||||
const BAD_PASSWORD_ERROR_NAME = 'InvalidPasswordError';
|
const BAD_PASSWORD_ERROR_NAME = 'InvalidPasswordError';
|
||||||
|
@ -74,71 +75,168 @@ export function doSetDefaultAccount(success: () => void, failure: (string) => vo
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const doLbrysyncGetSalt = (email: string) => async (dispatch: Dispatch) => {
|
// "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_CHECK_EMAIL_STARTED,
|
||||||
|
// CLEAR STUFF
|
||||||
|
});
|
||||||
const { fetchSaltSeed } = Lbrysync;
|
const { fetchSaltSeed } = Lbrysync;
|
||||||
dispatch({
|
if (isSignUp) {
|
||||||
type: ACTIONS.LSYNC_GET_SALT_STARTED,
|
|
||||||
});
|
|
||||||
try {
|
try {
|
||||||
const saltOrError = await fetchSaltSeed(email);
|
await fetchSaltSeed(email);
|
||||||
|
// !!got salt seed
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.LSYNC_GET_SALT_COMPLETED,
|
type: ACTIONS.LSYNC_CHECK_EMAIL_FAILED,
|
||||||
data: { email: email, saltSeed: saltOrError},
|
data: { emailError: 'Email Already Found' },
|
||||||
});
|
});
|
||||||
return saltOrError;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
// no salt, we're good.
|
||||||
|
const seed = await ipcRenderer.invoke('invoke-get-salt-seed');
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.LSYNC_GET_SALT_FAILED,
|
type: ACTIONS.LSYNC_CHECK_EMAIL_COMPLETED,
|
||||||
data: { email: email, saltError: 'Not Found'},
|
data: { candidateEmail: email, saltSeed: seed },
|
||||||
});
|
});
|
||||||
return 'not found';
|
}
|
||||||
|
} 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' },
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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)
|
//register an email (eventually username)
|
||||||
export const doLbrysyncRegister = (email: string, secrets: any, saltSeed: string) => async (dispatch: Dispatch) => {
|
export const doLbrysyncRegister = (password: string) => async (dispatch: Dispatch, getState: GetState) => {
|
||||||
const { register } = Lbrysync;
|
|
||||||
// started
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.LSYNC_REGISTER_STARTED,
|
type: ACTIONS.LSYNC_REGISTER_STARTED,
|
||||||
});
|
});
|
||||||
const resultIfError = await register(email, secrets.providerPass, saltSeed);
|
const state = getState();
|
||||||
const encProviderPass = safeStoreEncrypt(secrets.providerPass);
|
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 encHmacKey = safeStoreEncrypt(secrets.hmacKey);
|
||||||
const enctyptedRoot = safeStoreEncrypt(secrets.rootPassword);
|
const encDataKey = safeStoreEncrypt(secrets.dataKey);
|
||||||
|
const enctyptedRoot = safeStoreEncrypt(password);
|
||||||
const registerData = {
|
const registerData = {
|
||||||
email,
|
email, // email
|
||||||
saltSeed,
|
saltSeed,
|
||||||
providerPass: encProviderPass,
|
providerKey: encProviderKey,
|
||||||
hmacKey: encHmacKey,
|
hmacKey: encHmacKey,
|
||||||
|
dataKey: encDataKey,
|
||||||
rootPass: enctyptedRoot,
|
rootPass: enctyptedRoot,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!resultIfError) {
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.LSYNC_REGISTER_COMPLETED,
|
type: ACTIONS.LSYNC_REGISTER_COMPLETED,
|
||||||
data: registerData,
|
data: registerData,
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
|
return registerData;
|
||||||
|
} catch (e) {
|
||||||
|
console.log('e', e.message);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.LSYNC_REGISTER_FAILED,
|
type: ACTIONS.LSYNC_REGISTER_FAILED,
|
||||||
data: resultIfError,
|
data: 'ohno',
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function doEmailVerifySubscribe(stop) {
|
||||||
|
return (dispatch) => {
|
||||||
|
if (stop) {
|
||||||
|
clearInterval(verifyInterval);
|
||||||
|
} else {
|
||||||
|
dispatch(doLbrysyncAuthenticate());
|
||||||
|
verifyInterval = setInterval(() => dispatch(doLbrysyncAuthenticate()), 5000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// get token given username/password
|
// get token given username/password
|
||||||
export const doLbrysyncAuthenticate =
|
export const doLbrysyncAuthenticate = () => async (dispatch: Dispatch, getState: GetState) => {
|
||||||
() => async (dispatch: Dispatch, getState: GetState) => {
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.LSYNC_AUTH_STARTED,
|
type: ACTIONS.LSYNC_AUTH_STARTED,
|
||||||
});
|
});
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { lbrysync } = state;
|
const { sync } = state;
|
||||||
const { registeredEmail: email, encryptedProviderPass } = lbrysync;
|
const { registeredEmail: email, encryptedProviderKey } = sync;
|
||||||
const status = await Lbry.status();
|
const status = await Lbry.status();
|
||||||
const { installation_id: deviceId } = status;
|
const { installation_id: deviceId } = status;
|
||||||
const password = safeStoreDecrypt(encryptedProviderPass);
|
const password = safeStoreDecrypt(encryptedProviderKey);
|
||||||
|
|
||||||
const { getAuthToken } = Lbrysync;
|
const { getAuthToken } = Lbrysync;
|
||||||
|
|
||||||
|
@ -149,6 +247,7 @@ export const doLbrysyncAuthenticate =
|
||||||
type: ACTIONS.LSYNC_AUTH_COMPLETED,
|
type: ACTIONS.LSYNC_AUTH_COMPLETED,
|
||||||
data: result.token,
|
data: result.token,
|
||||||
});
|
});
|
||||||
|
clearInterval(verifyInterval);
|
||||||
} else {
|
} else {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.LSYNC_AUTH_FAILED,
|
type: ACTIONS.LSYNC_AUTH_FAILED,
|
||||||
|
@ -157,41 +256,7 @@ export const doLbrysyncAuthenticate =
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const doGenerateSaltSeed = () => async (dispatch: Dispatch) => {
|
// replaced with
|
||||||
const result = await ipcRenderer.invoke('invoke-get-salt-seed');
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const doDeriveSecrets = (rootPassword: string, email: string, saltSeed: string) => async (dispatch: Dispatch) =>
|
|
||||||
{
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_DERIVE_STARTED,
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
const result = await ipcRenderer.invoke('invoke-get-secrets', rootPassword, email, saltSeed);
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
hmacKey: result.hmacKey,
|
|
||||||
rootPassword,
|
|
||||||
providerPass: result.lbryIdPassword,
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_DERIVE_COMPLETED,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
} catch (e) {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_DERIVE_FAILED,
|
|
||||||
data: {
|
|
||||||
error: e,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return { error: e.message };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export function doSetSync(oldHash: string, newHash: string, data: any) {
|
export function doSetSync(oldHash: string, newHash: string, data: any) {
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: 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() {
|
export function doCheckSync() {
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const buildSharedStateMiddleware =
|
||||||
const actionResult = next(action);
|
const actionResult = next(action);
|
||||||
// Call `getState` after calling `next` to ensure the state has updated in response to the action
|
// Call `getState` after calling `next` to ensure the state has updated in response to the action
|
||||||
function runPreferences() {
|
function runPreferences() {
|
||||||
const nextState: { settings: any } = getState(); // bring in lbrysync
|
const nextState: { settings: any } = getState(); // bring in lbrysync?
|
||||||
const syncEnabled =
|
const syncEnabled =
|
||||||
nextState.settings && nextState.settings.clientSettings && nextState.settings.clientSettings.enable_sync;
|
nextState.settings && nextState.settings.clientSettings && nextState.settings.clientSettings.enable_sync;
|
||||||
const signedIn = false; // get from new sync system newsync
|
const signedIn = false; // get from new sync system newsync
|
||||||
|
|
|
@ -17,7 +17,14 @@ const defaultState = {
|
||||||
hashChanged: false,
|
hashChanged: false,
|
||||||
fatalError: false,
|
fatalError: false,
|
||||||
// lbrysync
|
// lbrysync
|
||||||
syncProvider: null,
|
// --email
|
||||||
|
checkingEmail: false,
|
||||||
|
candidateEmail: null,
|
||||||
|
emailError: null,
|
||||||
|
//syncProvider: null,
|
||||||
|
gettingSalt: false,
|
||||||
|
saltSeed: null,
|
||||||
|
saltError: null,
|
||||||
// reg
|
// reg
|
||||||
registering: false,
|
registering: false,
|
||||||
registeredEmail: null,
|
registeredEmail: null,
|
||||||
|
@ -31,10 +38,7 @@ const defaultState = {
|
||||||
encryptedHmacKey: null,
|
encryptedHmacKey: null,
|
||||||
encryptedRoot: null,
|
encryptedRoot: null,
|
||||||
encryptedProviderPass: null,
|
encryptedProviderPass: null,
|
||||||
// salt
|
encryptedDataKey: null,
|
||||||
gettingSalt: false,
|
|
||||||
saltSeed: null,
|
|
||||||
saltError: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.SYNC_STATE_POPULATE] = (state) => {
|
reducers[ACTIONS.SYNC_STATE_POPULATE] = (state) => {
|
||||||
|
@ -136,10 +140,14 @@ reducers[ACTIONS.LSYNC_REGISTER_COMPLETED] = (state, action) => ({
|
||||||
...state,
|
...state,
|
||||||
registeredEmail: action.data.email,
|
registeredEmail: action.data.email,
|
||||||
encryptedHmacKey: action.data.hmacKey,
|
encryptedHmacKey: action.data.hmacKey,
|
||||||
encryptedProviderPass: action.data.providerPass,
|
encryptedDataKey: action.data.dataKey,
|
||||||
|
encryptedProviderKey: action.data.providerKey,
|
||||||
encryptedRoot: action.data.rootPass,
|
encryptedRoot: action.data.rootPass,
|
||||||
saltSeed: action.data.saltSeed,
|
saltSeed: action.data.saltSeed,
|
||||||
|
syncProvider: action.data.syncProvider,
|
||||||
|
registering: false,
|
||||||
});
|
});
|
||||||
|
// clear attempt
|
||||||
reducers[ACTIONS.LSYNC_REGISTER_FAILED] = (state, action) => ({
|
reducers[ACTIONS.LSYNC_REGISTER_FAILED] = (state, action) => ({
|
||||||
...state,
|
...state,
|
||||||
registeredEmail: null,
|
registeredEmail: null,
|
||||||
|
@ -167,6 +175,7 @@ reducers[ACTIONS.LSYNC_DERIVE_STARTED] = (state) => ({
|
||||||
derivingKeys: true,
|
derivingKeys: true,
|
||||||
deriveError: null,
|
deriveError: null,
|
||||||
});
|
});
|
||||||
|
// add more keys
|
||||||
reducers[ACTIONS.LSYNC_DERIVE_COMPLETED] = (state, action) => ({
|
reducers[ACTIONS.LSYNC_DERIVE_COMPLETED] = (state, action) => ({
|
||||||
...state,
|
...state,
|
||||||
derivingKeys: false,
|
derivingKeys: false,
|
||||||
|
@ -176,7 +185,7 @@ reducers[ACTIONS.LSYNC_DERIVE_FAILED] = (state, action) => ({
|
||||||
deriveError: action.data.error,
|
deriveError: action.data.error,
|
||||||
derivingKeys: false,
|
derivingKeys: false,
|
||||||
});
|
});
|
||||||
// salt
|
// salt util
|
||||||
reducers[ACTIONS.LSYNC_GET_SALT_STARTED] = (state) => ({
|
reducers[ACTIONS.LSYNC_GET_SALT_STARTED] = (state) => ({
|
||||||
...state,
|
...state,
|
||||||
gettingSalt: true,
|
gettingSalt: true,
|
||||||
|
@ -191,6 +200,27 @@ reducers[ACTIONS.LSYNC_GET_SALT_FAILED] = (state, action) => ({
|
||||||
saltError: action.data.error,
|
saltError: action.data.error,
|
||||||
gettingSalt: false,
|
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;
|
reducers[ACTIONS.SYNC_RESET] = () => defaultState;
|
||||||
|
|
||||||
|
|
|
@ -1,49 +1,56 @@
|
||||||
import { createSelector } from 'reselect';
|
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
|
// 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 selectLbrySyncGettingSalt = createSelector(selectState, (state) => state.gettingSalt);
|
||||||
export const selectLbrySyncSaltError = createSelector(selectState, (state) => state.saltError);
|
export const selectLbrySyncSaltError = createSelector(selectState, (state) => state.saltError);
|
||||||
export const selectLbrySyncSaltSeed = createSelector(selectState, (state) => state.saltSeed);
|
export const selectLbrySyncSaltSeed = createSelector(selectState, (state) => state.saltSeed);
|
||||||
|
// password
|
||||||
export const selectLbrySyncIsAuthenticating = createSelector(selectState, (state) => state.isAuthenticating);
|
|
||||||
|
|
||||||
export const selectLbrySyncAuthError = createSelector(selectState, (state) => state.authError);
|
|
||||||
export const selectLbrySyncToken = createSelector(selectState, (state) => state.authToken);
|
|
||||||
|
|
||||||
export const selectLbrySyncDerivingKeys = createSelector(selectState, (state) => state.derivingKeys);
|
export const selectLbrySyncDerivingKeys = createSelector(selectState, (state) => state.derivingKeys);
|
||||||
export const selectLbrySyncEncryptedHmacKey = createSelector(selectState, (state) => state.encryptedHmacKey);
|
export const selectLbrySyncEncryptedHmacKey = createSelector(selectState, (state) => state.encryptedHmacKey);
|
||||||
export const selectLbrySyncEncryptedRoot = createSelector(selectState, (state) => state.encryptedRoot);
|
export const selectLbrySyncEncryptedRoot = createSelector(selectState, (state) => state.encryptedRoot);
|
||||||
export const selectLbrySyncEncryptedProviderPass = createSelector(selectState, (state) => state.encryptedProviderPass);
|
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