Skip to content

Commit

Permalink
fix(hmr): support for multi module replacement
Browse files Browse the repository at this point in the history
  • Loading branch information
manoldonev committed May 31, 2019
1 parent 4d6733d commit 7c22ffe
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 102 deletions.
3 changes: 3 additions & 0 deletions tests/app/livesync/livesync-button-page.scss
@@ -0,0 +1,3 @@
Button {
color: cyan;
}
56 changes: 54 additions & 2 deletions tests/app/livesync/livesync-tests.ts
Expand Up @@ -5,7 +5,6 @@ import * as app from "tns-core-modules/application/application";
import * as frame from "tns-core-modules/ui/frame";

import { Color } from "tns-core-modules/color";
import { isAndroid } from "tns-core-modules/platform";
import { createViewFromEntry } from "tns-core-modules/ui/builder";
import { Page } from "tns-core-modules/ui/page";
import { Frame } from "tns-core-modules/ui/frame";
Expand All @@ -20,6 +19,7 @@ const buttonHtmlPageFileName = "./livesync/livesync-button-page.html";
const buttonXmlPageFileName = "./livesync/livesync-button-page.xml";
const buttonJsPageFileName = "./livesync/livesync-button-page.js";
const buttonTsPageFileName = "./livesync/livesync-button-page.ts";
const buttonScssPageFileName = "./livesync/livesync-button-page.scss";
const labelPageModuleName = "livesync/livesync-label-page";

const green = new Color("green");
Expand Down Expand Up @@ -60,6 +60,36 @@ export function test_onLiveSync_ModuleContext_Markup_XmlFile() {
_test_onLiveSync_ModuleReplace({ type: "markup", path: buttonXmlPageFileName });
}

export function test_onLiveSync_ModuleContext_Markup_Script_XmlFile() {
_test_onLiveSync_ModuleReplace_Multiple([
{ type: "script", path: buttonTsPageFileName },
{ type: "markup", path: buttonXmlPageFileName }
]);
}

export function test_onLiveSync_ModuleContext_Markup_Script_Style_XmlFile() {
_test_onLiveSync_ModuleReplace_Multiple([
{ type: "script", path: buttonTsPageFileName },
{ type: "markup", path: buttonXmlPageFileName },
{ type: "style", path: buttonScssPageFileName }
]);
}

export function test_onLiveSync_ModuleContext_Markup_Script_HtmlFile() {
_test_onLiveSync_ModuleReplace_Multiple([
{ type: "script", path: buttonTsPageFileName },
{ type: "markup", path: buttonHtmlPageFileName }
]);
}

export function test_onLiveSync_ModuleContext_Markup_Script_Style_HtmlFile() {
_test_onLiveSync_ModuleReplace_Multiple([
{ type: "script", path: buttonTsPageFileName },
{ type: "markup", path: buttonHtmlPageFileName },
{ type: "style", path: buttonScssPageFileName }
]);
}

export function setUp() {
const labelPage = <Page>createViewFromEntry(({ moduleName: labelPageModuleName }));
helper.navigate(() => labelPage);
Expand Down Expand Up @@ -121,6 +151,28 @@ function _test_onLiveSync_ModuleReplace(context: { type, path }) {
TKUnit.assertEqual(pageBeforeNavigation, pageAfterBackNavigation, "Pages are different!");
}

function _test_onLiveSync_ModuleReplace_Multiple(context: { type: string, path: string }[]) {
const pageBeforeNavigation = helper.getCurrentPage();
const buttonPage = <Page>createViewFromEntry(({ moduleName: buttonPageModuleName }));
helper.navigateWithHistory(() => buttonPage);

context.forEach(item => {
global.__onLiveSync(item);
});

const topmostFrame = frame.topmost();
waitUntilLivesyncComplete(topmostFrame);
TKUnit.assertTrue(topmostFrame.currentPage.getViewById("button").isLoaded, "Button page is NOT loaded!");
TKUnit.assertEqual(topmostFrame.backStack.length, 1, "Backstack is clean!");
TKUnit.assertTrue(topmostFrame.canGoBack(), "Can NOT go back!");

helper.goBack();
const pageAfterBackNavigation = helper.getCurrentPage();
TKUnit.assertTrue(topmostFrame.currentPage.getViewById("label").isLoaded, "Label page is NOT loaded!");
TKUnit.assertEqual(topmostFrame.backStack.length, 0, "Backstack is NOT clean!");
TKUnit.assertEqual(pageBeforeNavigation, pageAfterBackNavigation, "Pages are different!");
}

function _test_onLiveSync_ModuleContext_TypeStyle(context: { type, path }) {
const pageBeforeNavigation = helper.getCurrentPage();
const buttonPage = <Page>createViewFromEntry(({ moduleName: buttonPageModuleName }));
Expand All @@ -146,5 +198,5 @@ function _test_onLiveSync_ModuleContext_TypeStyle(context: { type, path }) {
}

function waitUntilLivesyncComplete(frame: Frame) {
TKUnit.waitUntilReady(() => frame._executingEntry === null);
TKUnit.waitUntilReady(() => frame.navigationQueueIsEmpty());
}
3 changes: 1 addition & 2 deletions tns-core-modules/ui/frame/fragment.transitions.android.ts
Expand Up @@ -735,8 +735,7 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry): void {
// Will be null if Frame is shown modally...
// transitionOrAnimationCompleted fires again (probably bug in android).
if (current) {
const navType = frame.navigationType;
setTimeout(() => frame.setCurrent(current, navType));
setTimeout(() => frame.setCurrent(current));
}
} else {
completedEntries.set(frameId, entry);
Expand Down
54 changes: 33 additions & 21 deletions tns-core-modules/ui/frame/frame-common.ts
Expand Up @@ -13,6 +13,7 @@ import { getModuleName } from "../../utils/utils";
export * from "../core/view";

export enum NavigationType {
unset,
back,
forward,
replace
Expand All @@ -37,7 +38,9 @@ function buildEntryFromArgs(arg: any): NavigationEntry {

export interface NavigationContext {
entry: BackstackEntry;
// TODO: remove isBackNavigation for NativeScript 6.0
isBackNavigation: boolean;
navigationType: NavigationType
}

@CSSType("Frame")
Expand All @@ -51,11 +54,10 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {

public actionBarVisibility: "auto" | "never" | "always";
public _currentEntry: BackstackEntry;
public _executingEntry: BackstackEntry;
public _executingContext: NavigationContext;
public _isInFrameStack = false;
public static defaultAnimatedNavigation = true;
public static defaultTransition: NavigationTransition;
public navigationType: NavigationType;

// TODO: Currently our navigation will not be synchronized in case users directly call native navigation methods like Activity.startActivity.

Expand All @@ -75,7 +77,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
let previousForwardNotInBackstack = false;
this._navigationQueue.forEach(item => {
const entry = item.entry;
if (item.isBackNavigation) {
const isBackNavigation = item.navigationType === NavigationType.back;
if (isBackNavigation) {
previousForwardNotInBackstack = false;
if (!entry) {
backstack--;
Expand Down Expand Up @@ -135,7 +138,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {

const navigationContext: NavigationContext = {
entry: backstackEntry,
isBackNavigation: true
isBackNavigation: true,
navigationType: NavigationType.back
}

this._navigationQueue.push(navigationContext);
Expand Down Expand Up @@ -203,7 +207,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {

const navigationContext: NavigationContext = {
entry: backstackEntry,
isBackNavigation: false
isBackNavigation: false,
navigationType: NavigationType.forward
}

this._navigationQueue.push(navigationContext);
Expand All @@ -214,7 +219,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
return this._currentEntry === entry;
}

public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void {
public setCurrent(entry: BackstackEntry): void {
const newPage = entry.resolvedPage;
// In case we navigated forward to a page that was in the backstack
// with clearHistory: true
Expand All @@ -225,17 +230,19 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {

this._currentEntry = entry;

const navigationContext = this._executingContext || { navigationType: NavigationType.unset };
const navigationType = navigationContext.navigationType;
const isBack = navigationType === NavigationType.back;
if (isBack) {
this._pushInFrameStack();
}

newPage.onNavigatedTo(isBack);

// Reset executing entry after NavigatedTo is raised;
// Reset executing context after NavigatedTo is raised;
// we do not want to execute two navigations in parallel in case
// additional navigation is triggered from the NavigatedTo handler.
this._executingEntry = null;
this._executingContext = null;
}

public _updateBackstack(entry: BackstackEntry, navigationType: NavigationType): void {
Expand Down Expand Up @@ -342,13 +349,14 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
}

protected _processNextNavigationEntry() {
if (!this.isLoaded || this._executingEntry) {
if (!this.isLoaded || this._executingContext) {
return;
}

if (this._navigationQueue.length > 0) {
const navigationContext = this._navigationQueue[0];
if (navigationContext.isBackNavigation) {
const isBackNavigation = navigationContext.navigationType === NavigationType.back;
if (isBackNavigation) {
this.performGoBack(navigationContext);
} else {
this.performNavigation(navigationContext);
Expand All @@ -358,10 +366,12 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {

@profile
public performNavigation(navigationContext: NavigationContext) {
const navContext = navigationContext.entry;
this._executingEntry = navContext;
this._onNavigatingTo(navContext, navigationContext.isBackNavigation);
this._navigateCore(navContext);
this._executingContext = navigationContext;

const backstackEntry = navigationContext.entry;
const isBackNavigation = navigationContext.navigationType === NavigationType.back;
this._onNavigatingTo(backstackEntry, isBackNavigation);
this._navigateCore(backstackEntry);
}

@profile
Expand All @@ -373,7 +383,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
navigationContext.entry = backstackEntry;
}

this._executingEntry = backstackEntry;
this._executingContext = navigationContext;
this._onNavigatingTo(backstackEntry, true);
this._goBackCore(backstackEntry);
}
Expand Down Expand Up @@ -643,9 +653,6 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
}

protected replacePage(context: ModuleContext): void {
// Set NavigationType.replace for HMR.
// In IOS on `viewDidAppear()` this will be set to NavigationType.forward.
this.navigationType = NavigationType.replace;
const currentBackstackEntry = this._currentEntry;
const contextModuleName = getModuleName(context.path);

Expand All @@ -658,10 +665,15 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
frameId: currentBackstackEntry.frameId
};

const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false };
this.performNavigation(navContext);
}
const navigationContext: NavigationContext = {
entry: newBackstackEntry,
isBackNavigation: false,
navigationType: NavigationType.replace
};

this._navigationQueue.push(navigationContext);
this._processNextNavigationEntry();
}
}

export function getFrameById(id: string): FrameBase {
Expand Down

0 comments on commit 7c22ffe

Please sign in to comment.