Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

base load error handling revision #3443

Closed
wants to merge 10 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export default class AdaAddressesStore extends Store<StoresMap, ActionsMap> {
apiKey: 'czsajliz-wxgu6tujd1zqq7hey_pclfqhdjsqolsxjfsurgh',
},
},
cslFactory: RustModule.CrossCsl.init,
cslFactory: (ctx) => RustModule.CrossCsl.init(ctx),
});
}

Expand Down
64 changes: 38 additions & 26 deletions packages/yoroi-extension/app/stores/base/BaseLoadingStore.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// @flow
import type { lf$Database, lf$lovefieldExport, } from 'lovefield';
import { schema, } from 'lovefield';
import { observable, computed, when, runInAction } from 'mobx';
import { computed, observable, runInAction } from 'mobx';
import Store from './Store';
import environment from '../../environment';
import LocalizableError from '../../i18n/LocalizableError';
import { UnableToLoadError, StorageLoadError } from '../../i18n/errors';
import { StorageLoadError, UnableToLoadError } from '../../i18n/errors';
import Request from '../lib/LocalizedRequest';
import type { MigrationRequest } from '../../api/common/migration';
import { migrateAndRefresh } from '../../api/common/migration';
import { Logger, stringifyError } from '../../utils/logging';
import { closeOtherInstances } from '../../utils/tabManager';
import { loadLovefieldDB, importOldDb, } from '../../api/ada/lib/storage/database/index';
import { importOldDb, loadLovefieldDB, } from '../../api/ada/lib/storage/database/index';
import { RustModule } from '../../api/ada/lib/cardanoCrypto/rustLoader';

/** Load dependencies before launching the app */
Expand All @@ -29,35 +29,38 @@ export default class BaseLoadingStore<TStores, TActions> extends Store<TStores,
// note: never get anything but result except for the .error inside this file
@observable loadPersistentDbRequest: Request<void => Promise<lf$Database>>
= new Request<void => Promise<lf$Database>>(
async () => await loadLovefieldDB(schema.DataStoreType.INDEXED_DB)
() => loadLovefieldDB(schema.DataStoreType.INDEXED_DB)
);

blockingLoadingRequests: Array<Promise<void>> = [];
__blockingLoadingRequests: Array<[Request<() => Promise<void>>, string]> = [];

setup(): void {
}

registerBlockingLoadingRequest(promise: Promise<void>): void {
this.blockingLoadingRequests.push(promise);
registerBlockingLoadingRequest(promise: Promise<void>, name: string): void {
this.__blockingLoadingRequests.push([new Request(() => promise), name]);
}

load(env: 'connector' | 'extension'): void {
when(() => this.isLoading, this.postLoadingScreenEnd.bind(this));
const rustLoadingParams = (env === 'extension') ? ['dontLoadMessagesSigning'] : [];
Promise
.all([
// $FlowFixMe[invalid-tuple-arity]: this is correct, flow is confused
this.loadRustRequest.execute(
(env === 'extension') ? [ 'dontLoadMessagesSigning' ] : []
).promise,
this.loadPersistentDbRequest.execute().promise,
...this.blockingLoadingRequests,
// $FlowIgnore[invalid-tuple-arity]
this.loadRustRequest.execute(rustLoadingParams),
this.loadPersistentDbRequest.execute(),
...(this.__blockingLoadingRequests.map(([r]) => r.execute())),
])
.then(async () => {
Logger.debug(`[yoroi] closing other instances`);
await closeOtherInstances(this.getTabIdKey.bind(this)());
Logger.debug(`[yoroi] loading persistent db`);
const persistentDb = this.loadPersistentDbRequest.result;
if (persistentDb == null) throw new Error(`${nameof(BaseLoadingStore)}::${nameof(this.load)} load db was not loaded. Should never happen`);
if (persistentDb == null) {
throw new Error(
`${nameof(BaseLoadingStore)}::${nameof(this.load)}
DB was not loaded. Should never happen`
);
}
Logger.debug(`[yoroi] check migrations`);
await this.migrationRequest.execute({
localStorageApi: this.api.localStorage,
Expand All @@ -69,20 +72,29 @@ export default class BaseLoadingStore<TStores, TActions> extends Store<TStores,
runInAction(() => {
this.error = null;
this._loading = false;
this.postLoadingScreenEnd();
Logger.debug(`[yoroi] loading ended`);
});
return undefined;
}).catch((error) => {
Logger.error(`${nameof(BaseLoadingStore)}::${nameof(this.load)} Unable to load libraries ` + stringifyError(error));
if (this.loadPersistentDbRequest.error != null) {
runInAction(() => {
this.error = new StorageLoadError();
});
} else {
runInAction(() => {
this.error = new UnableToLoadError();
});
}
})
.catch((error) => {
const isRustLoadError = this.loadRustRequest.error != null;
const isDbLoadError = this.loadPersistentDbRequest.error != null;
const failedBlockingLoadingRequestName =
this.__blockingLoadingRequests.find(([r]) => r.error != null)?.[1];
const errorType =
(isRustLoadError && 'rust')
|| (isDbLoadError && 'db')
|| failedBlockingLoadingRequestName
|| 'unclear';
Logger.error(
`${nameof(BaseLoadingStore)}::${nameof(this.load)}
Unable to load libraries (error type: ${errorType}) `
+ stringifyError(error)
);
runInAction(() => {
this.error = isDbLoadError ? new StorageLoadError() : new UnableToLoadError();
});
});
}

Expand Down
76 changes: 41 additions & 35 deletions packages/yoroi-extension/app/stores/base/BaseProfileStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface CoinPriceStore {
}

interface LoadingStore {
+registerBlockingLoadingRequest: (promise: Promise<void>) => void
+registerBlockingLoadingRequest: (promise: Promise<void>, name: string) => void
}

export default class BaseProfileStore
Expand Down Expand Up @@ -198,41 +198,47 @@ export default class BaseProfileStore
noop(this.isRevampAnnounced);
noop(this.didUserMigratedToRevampTheme);
this.stores.loading.registerBlockingLoadingRequest(
this._loadAcceptedTosVersion()
this._loadAcceptedTosVersion(),
'load-tos-version',
);
this.stores.loading.registerBlockingLoadingRequest(
(async () => {
const option = await this.getIsAnalyticsAllowed.execute()
const AMPLI_FLUSH_INTERVAL_MS = 5000;
await ampli.load(({
environment: environment.isProduction() ? 'production' : 'development',
client: {
configuration: {
optOut: !option,
flushIntervalMillis: AMPLI_FLUSH_INTERVAL_MS,
trackingOptions: {
ipAddress: false,
},
defaultTracking: false,
},
},
}: LoadOptionsWithEnvironment)).promise;

if (environment.isDev()) {
ampli.client.add({
name: 'info-plugin',
type: 'enrichment',
setup: () => Promise.resolve(),
execute: async (event) => {
console.info('[metrics]', event.event_type, event.event_properties)
return Promise.resolve(event)
},
});
}
})()
this._loadWhetherAnalyticsAllowed(),
'load-analytics-flag',
);
}

_loadWhetherAnalyticsAllowed: () => Promise<void> = async () => {
const isAnalyticsAllowed = await this.getIsAnalyticsAllowed.execute();
const AMPLI_FLUSH_INTERVAL_MS = 5000;
if (ampli.load == null || typeof ampli.load !== 'function') {
throw new Error(`ampli.load is not available or not a function (${typeof ampli.load})`)
}
await ampli.load(({
environment: environment.isProduction() ? 'production' : 'development',
client: {
configuration: {
optOut: !isAnalyticsAllowed,
flushIntervalMillis: AMPLI_FLUSH_INTERVAL_MS,
trackingOptions: {
ipAddress: false,
},
defaultTracking: false,
},
},
}: LoadOptionsWithEnvironment)).promise;
if (environment.isDev()) {
ampli.client.add({
name: 'info-plugin',
type: 'enrichment',
setup: () => Promise.resolve(),
execute: async (event) => {
console.info('[metrics]', event.event_type, event.event_properties)
return Promise.resolve(event)
},
});
}
}

teardown(): void {
super.teardown();
}
Expand Down Expand Up @@ -563,10 +569,10 @@ export default class BaseProfileStore
return this.getUnitOfAccountRequest.wasExecuted && this.getUnitOfAccountRequest.result !== null;
}

_onOptForAnalytics: (boolean) => void = (option) => {
this.getIsAnalyticsAllowed.patch(_ => option);
this.api.localStorage.saveIsAnalysticsAllowed(option);
ampli.client.setOptOut(!option);
_onOptForAnalytics: (boolean) => void = (isAnalyticsAllowed) => {
this.getIsAnalyticsAllowed.patch(_ => isAnalyticsAllowed);
this.api.localStorage.saveIsAnalysticsAllowed(isAnalyticsAllowed);
ampli.client.setOptOut(!isAnalyticsAllowed);
}

@computed get isAnalyticsOpted(): boolean {
Expand Down
3 changes: 0 additions & 3 deletions packages/yoroi-extension/chrome/extension/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { LazyLoadPromises } from '../../app/Routes';
import environment from '../../app/environment';
import { ampli } from '../../ampli/index';
import { ROUTES } from '../../app/routes-config';
import { RustModule } from '../../app/api/ada/lib/cardanoCrypto/rustLoader';

// run MobX in strict mode
configure({ enforceActions: 'always' });
Expand All @@ -29,8 +28,6 @@ BigNumber.DEBUG = true;
// Entry point into our application
const initializeYoroi: void => Promise<void> = async () => {

await RustModule.load();

const api = await setupApi();
const router = new RouterStore();
const hashHistory = createHashHistory();
Expand Down
Loading