fix: improve deinit logic and flow

This commit is contained in:
Mo
2022-02-08 21:35:31 -06:00
parent 1dd70364e7
commit e43c8a6f07
6 changed files with 82 additions and 51 deletions

View File

@@ -196,10 +196,17 @@ export class ApplicationView extends PureComponent<Props, State> {
}; };
render() { render() {
if (this.application['dealloced'] === true) {
console.error('Attempting to render dealloced application');
return <div></div>;
}
const renderAppContents = !this.state.needsUnlock && this.state.launched;
return ( return (
<PremiumModalProvider state={this.appState.features}> <PremiumModalProvider state={this.appState?.features}>
<div className={this.platformString + ' main-ui-view sn-component'}> <div className={this.platformString + ' main-ui-view sn-component'}>
{!this.state.needsUnlock && this.state.launched && ( {renderAppContents && (
<div <div
id="app" id="app"
className={this.state.appClass + ' app app-column-container'} className={this.state.appClass + ' app app-column-container'}
@@ -215,23 +222,25 @@ export class ApplicationView extends PureComponent<Props, State> {
</div> </div>
)} )}
{!this.state.needsUnlock && this.state.launched && ( {renderAppContents && (
<Footer <>
application={this.application} <Footer
applicationGroup={this.props.mainApplicationGroup} application={this.application}
/> applicationGroup={this.props.mainApplicationGroup}
/>
<SessionsModal
application={this.application}
appState={this.appState}
/>
<PreferencesViewWrapper
appState={this.appState}
application={this.application}
/>
</>
)} )}
<SessionsModal
application={this.application}
appState={this.appState}
/>
<PreferencesViewWrapper
appState={this.appState}
application={this.application}
/>
{this.state.challenges.map((challenge) => { {this.state.challenges.map((challenge) => {
return ( return (
<div className="sk-modal"> <div className="sk-modal">
@@ -245,15 +254,19 @@ export class ApplicationView extends PureComponent<Props, State> {
); );
})} })}
<NotesContextMenu {renderAppContents && (
application={this.application} <>
appState={this.appState} <NotesContextMenu
/> application={this.application}
appState={this.appState}
/>
<PurchaseFlowWrapper <PurchaseFlowWrapper
application={this.application} application={this.application}
appState={this.appState} appState={this.appState}
/> />
</>
)}
</div> </div>
</PremiumModalProvider> </PremiumModalProvider>
); );

View File

@@ -1,7 +1,7 @@
import { FeaturesState } from '@/ui_models/app_state/features_state'; import { FeaturesState } from '@/ui_models/app_state/features_state';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { FunctionalComponent } from 'preact'; import { FunctionalComponent } from 'preact';
import { useCallback, useContext, useState } from 'preact/hooks'; import { useContext } from 'preact/hooks';
import { createContext } from 'react'; import { createContext } from 'react';
import { PremiumFeaturesModal } from '../PremiumFeaturesModal'; import { PremiumFeaturesModal } from '../PremiumFeaturesModal';

View File

@@ -93,7 +93,6 @@ export class AppState {
private readonly tagChangedDisposer: IReactionDisposer; private readonly tagChangedDisposer: IReactionDisposer;
/* @ngInject */
constructor(application: WebApplication, private bridge: Bridge) { constructor(application: WebApplication, private bridge: Bridge) {
this.application = application; this.application = application;
this.notes = new NotesState( this.notes = new NotesState(
@@ -171,18 +170,39 @@ export class AppState {
storage.remove(StorageKey.ShowBetaWarning); storage.remove(StorageKey.ShowBetaWarning);
this.noAccountWarning.reset(); this.noAccountWarning.reset();
} }
(this.application as unknown) = undefined;
this.actionsMenu.reset(); this.actionsMenu.reset();
this.unsubApp?.(); this.unsubApp?.();
this.unsubApp = undefined; this.unsubApp = undefined;
this.observers.length = 0; this.observers.length = 0;
this.appEventObserverRemovers.forEach((remover) => remover()); this.appEventObserverRemovers.forEach((remover) => remover());
this.features.deinit();
this.appEventObserverRemovers.length = 0; this.appEventObserverRemovers.length = 0;
this.features.deinit();
(this.features as unknown) = undefined;
this.webAppEventDisposer?.(); this.webAppEventDisposer?.();
this.webAppEventDisposer = undefined; this.webAppEventDisposer = undefined;
(this.quickSettingsMenu as unknown) = undefined;
(this.accountMenu as unknown) = undefined;
(this.actionsMenu as unknown) = undefined;
(this.preferences as unknown) = undefined;
(this.purchaseFlow as unknown) = undefined;
(this.noteTags as unknown) = undefined;
(this.sync as unknown) = undefined;
(this.searchOptions as unknown) = undefined;
(this.notes as unknown) = undefined;
(this.features as unknown) = undefined;
(this.tags as unknown) = undefined;
(this.notesView as unknown) = undefined;
document.removeEventListener('visibilitychange', this.onVisibilityChange); document.removeEventListener('visibilitychange', this.onVisibilityChange);
this.onVisibilityChange = undefined; this.onVisibilityChange = undefined;
this.tagChangedDisposer(); this.tagChangedDisposer();
(this.tagChangedDisposer as unknown) = undefined;
} }
openSessionsModal(): void { openSessionsModal(): void {

View File

@@ -39,8 +39,8 @@ export type WebEventObserver = (event: WebAppEvent) => void;
export class WebApplication extends SNApplication { export class WebApplication extends SNApplication {
private webServices!: WebServices; private webServices!: WebServices;
public noteControllerGroup: NoteGroupController;
private webEventObservers: WebEventObserver[] = []; private webEventObservers: WebEventObserver[] = [];
public noteControllerGroup: NoteGroupController;
public iconsController: IconsController; public iconsController: IconsController;
constructor( constructor(
@@ -70,28 +70,26 @@ export class WebApplication extends SNApplication {
this.iconsController = new IconsController(); this.iconsController = new IconsController();
} }
/** @override */
deinit(source: DeinitSource): void { deinit(source: DeinitSource): void {
for (const service of Object.values(this.webServices)) { super.deinit(source);
if ('deinit' in service) { try {
service.deinit?.(source); for (const service of Object.values(this.webServices)) {
if ('deinit' in service) {
service.deinit?.(source);
}
(service as any).application = undefined;
} }
(service as any).application = undefined; this.webServices = {} as WebServices;
} this.noteControllerGroup.deinit();
this.webServices = {} as WebServices; this.iconsController.deinit();
this.noteControllerGroup.deinit(); this.webEventObservers.length = 0;
this.iconsController.deinit();
this.webEventObservers.length = 0;
/**
* Allow any pending renders to complete before destroying the global
* application instance and all its services
*/
setTimeout(() => {
super.deinit(source);
if (source === DeinitSource.SignOut) { if (source === DeinitSource.SignOut) {
this.bridge.onSignOut(); this.bridge.onSignOut();
} }
}, 0); } catch (error) {
console.error('Error while deiniting application', error);
}
} }
setWebServices(services: WebServices): void { setWebServices(services: WebServices): void {

View File

@@ -84,7 +84,7 @@
"@reach/tooltip": "^0.16.2", "@reach/tooltip": "^0.16.2",
"@standardnotes/components": "1.7.0", "@standardnotes/components": "1.7.0",
"@standardnotes/features": "1.29.0", "@standardnotes/features": "1.29.0",
"@standardnotes/snjs": "2.52.1", "@standardnotes/snjs": "2.52.2",
"@standardnotes/settings": "^1.11.3", "@standardnotes/settings": "^1.11.3",
"@standardnotes/sncrypto-web": "1.6.2", "@standardnotes/sncrypto-web": "1.6.2",
"mobx": "^6.3.5", "mobx": "^6.3.5",

View File

@@ -2650,10 +2650,10 @@
buffer "^6.0.3" buffer "^6.0.3"
libsodium-wrappers "^0.7.9" libsodium-wrappers "^0.7.9"
"@standardnotes/snjs@2.52.1": "@standardnotes/snjs@2.52.2":
version "2.52.1" version "2.52.2"
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.52.1.tgz#00e2e1f7eaeef2c95b6830470031496e48ff4b83" resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.52.2.tgz#34d1edd3028aa860648548292291d6ecf8666a5f"
integrity sha512-U35dangXmhl1uEnPcDmWrRlqHyT/N5KIE5izT8w0xkhrWpc5a62j+m0u2uJNYU7C00d6ERqVWXeHCDsdSXTx8A== integrity sha512-5w2RXSquTmyYNSyKgEYqmLLqbHyYCsyrqfWlL0nuQ//hBFcf8OUbsY+Ys132TodpGZn637Al+kygoZxGBVcSyA==
dependencies: dependencies:
"@standardnotes/auth" "^3.15.4" "@standardnotes/auth" "^3.15.4"
"@standardnotes/common" "^1.9.0" "@standardnotes/common" "^1.9.0"