mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-17 21:01:34 +08:00
Merge pull request #7293 from NativeScript/mdonev/hmr-multliple-fix
fix(hmr): support for multi module replacement
This commit is contained in:
3
tests/app/livesync/livesync-button-page.scss
Normal file
3
tests/app/livesync/livesync-button-page.scss
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Button {
|
||||||
|
color: cyan;
|
||||||
|
}
|
@ -5,7 +5,6 @@ import * as app from "tns-core-modules/application/application";
|
|||||||
import * as frame from "tns-core-modules/ui/frame";
|
import * as frame from "tns-core-modules/ui/frame";
|
||||||
|
|
||||||
import { Color } from "tns-core-modules/color";
|
import { Color } from "tns-core-modules/color";
|
||||||
import { isAndroid } from "tns-core-modules/platform";
|
|
||||||
import { createViewFromEntry } from "tns-core-modules/ui/builder";
|
import { createViewFromEntry } from "tns-core-modules/ui/builder";
|
||||||
import { Page } from "tns-core-modules/ui/page";
|
import { Page } from "tns-core-modules/ui/page";
|
||||||
import { Frame } from "tns-core-modules/ui/frame";
|
import { Frame } from "tns-core-modules/ui/frame";
|
||||||
@ -20,6 +19,7 @@ const buttonHtmlPageFileName = "./livesync/livesync-button-page.html";
|
|||||||
const buttonXmlPageFileName = "./livesync/livesync-button-page.xml";
|
const buttonXmlPageFileName = "./livesync/livesync-button-page.xml";
|
||||||
const buttonJsPageFileName = "./livesync/livesync-button-page.js";
|
const buttonJsPageFileName = "./livesync/livesync-button-page.js";
|
||||||
const buttonTsPageFileName = "./livesync/livesync-button-page.ts";
|
const buttonTsPageFileName = "./livesync/livesync-button-page.ts";
|
||||||
|
const buttonScssPageFileName = "./livesync/livesync-button-page.scss";
|
||||||
const labelPageModuleName = "livesync/livesync-label-page";
|
const labelPageModuleName = "livesync/livesync-label-page";
|
||||||
|
|
||||||
const green = new Color("green");
|
const green = new Color("green");
|
||||||
@ -60,6 +60,36 @@ export function test_onLiveSync_ModuleContext_Markup_XmlFile() {
|
|||||||
_test_onLiveSync_ModuleReplace({ type: "markup", path: buttonXmlPageFileName });
|
_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() {
|
export function setUp() {
|
||||||
const labelPage = <Page>createViewFromEntry(({ moduleName: labelPageModuleName }));
|
const labelPage = <Page>createViewFromEntry(({ moduleName: labelPageModuleName }));
|
||||||
helper.navigate(() => labelPage);
|
helper.navigate(() => labelPage);
|
||||||
@ -121,6 +151,28 @@ function _test_onLiveSync_ModuleReplace(context: { type, path }) {
|
|||||||
TKUnit.assertEqual(pageBeforeNavigation, pageAfterBackNavigation, "Pages are different!");
|
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 }) {
|
function _test_onLiveSync_ModuleContext_TypeStyle(context: { type, path }) {
|
||||||
const pageBeforeNavigation = helper.getCurrentPage();
|
const pageBeforeNavigation = helper.getCurrentPage();
|
||||||
const buttonPage = <Page>createViewFromEntry(({ moduleName: buttonPageModuleName }));
|
const buttonPage = <Page>createViewFromEntry(({ moduleName: buttonPageModuleName }));
|
||||||
@ -146,5 +198,5 @@ function _test_onLiveSync_ModuleContext_TypeStyle(context: { type, path }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function waitUntilLivesyncComplete(frame: Frame) {
|
function waitUntilLivesyncComplete(frame: Frame) {
|
||||||
TKUnit.waitUntilReady(() => frame._executingEntry === null);
|
TKUnit.waitUntilReady(() => frame.navigationQueueIsEmpty());
|
||||||
}
|
}
|
||||||
|
@ -724,6 +724,7 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry): void {
|
|||||||
entries.delete(entry);
|
entries.delete(entry);
|
||||||
if (entries.size === 0) {
|
if (entries.size === 0) {
|
||||||
const frame = entry.resolvedPage.frame;
|
const frame = entry.resolvedPage.frame;
|
||||||
|
|
||||||
// We have 0 or 1 entry per frameId in completedEntries
|
// We have 0 or 1 entry per frameId in completedEntries
|
||||||
// So there is no need to make it to Set like waitingQueue
|
// So there is no need to make it to Set like waitingQueue
|
||||||
const previousCompletedAnimationEntry = completedEntries.get(frameId);
|
const previousCompletedAnimationEntry = completedEntries.get(frameId);
|
||||||
@ -734,9 +735,8 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry): void {
|
|||||||
current = current || entry;
|
current = current || entry;
|
||||||
// Will be null if Frame is shown modally...
|
// Will be null if Frame is shown modally...
|
||||||
// transitionOrAnimationCompleted fires again (probably bug in android).
|
// transitionOrAnimationCompleted fires again (probably bug in android).
|
||||||
if (current) {
|
if (current && frame._executingContext) {
|
||||||
const navType = frame.navigationType;
|
setTimeout(() => frame.setCurrent(current, frame._executingContext.navigationType));
|
||||||
setTimeout(() => frame.setCurrent(current, navType));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
completedEntries.set(frameId, entry);
|
completedEntries.set(frameId, entry);
|
||||||
|
@ -37,7 +37,9 @@ function buildEntryFromArgs(arg: any): NavigationEntry {
|
|||||||
|
|
||||||
export interface NavigationContext {
|
export interface NavigationContext {
|
||||||
entry: BackstackEntry;
|
entry: BackstackEntry;
|
||||||
|
// TODO: remove isBackNavigation for NativeScript 6.0
|
||||||
isBackNavigation: boolean;
|
isBackNavigation: boolean;
|
||||||
|
navigationType: NavigationType
|
||||||
}
|
}
|
||||||
|
|
||||||
@CSSType("Frame")
|
@CSSType("Frame")
|
||||||
@ -51,11 +53,10 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
|
|
||||||
public actionBarVisibility: "auto" | "never" | "always";
|
public actionBarVisibility: "auto" | "never" | "always";
|
||||||
public _currentEntry: BackstackEntry;
|
public _currentEntry: BackstackEntry;
|
||||||
public _executingEntry: BackstackEntry;
|
public _executingContext: NavigationContext;
|
||||||
public _isInFrameStack = false;
|
public _isInFrameStack = false;
|
||||||
public static defaultAnimatedNavigation = true;
|
public static defaultAnimatedNavigation = true;
|
||||||
public static defaultTransition: NavigationTransition;
|
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.
|
// TODO: Currently our navigation will not be synchronized in case users directly call native navigation methods like Activity.startActivity.
|
||||||
|
|
||||||
@ -75,7 +76,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
let previousForwardNotInBackstack = false;
|
let previousForwardNotInBackstack = false;
|
||||||
this._navigationQueue.forEach(item => {
|
this._navigationQueue.forEach(item => {
|
||||||
const entry = item.entry;
|
const entry = item.entry;
|
||||||
if (item.isBackNavigation) {
|
const isBackNavigation = item.navigationType === NavigationType.back;
|
||||||
|
if (isBackNavigation) {
|
||||||
previousForwardNotInBackstack = false;
|
previousForwardNotInBackstack = false;
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
backstack--;
|
backstack--;
|
||||||
@ -135,7 +137,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
|
|
||||||
const navigationContext: NavigationContext = {
|
const navigationContext: NavigationContext = {
|
||||||
entry: backstackEntry,
|
entry: backstackEntry,
|
||||||
isBackNavigation: true
|
isBackNavigation: true,
|
||||||
|
navigationType: NavigationType.back
|
||||||
}
|
}
|
||||||
|
|
||||||
this._navigationQueue.push(navigationContext);
|
this._navigationQueue.push(navigationContext);
|
||||||
@ -203,7 +206,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
|
|
||||||
const navigationContext: NavigationContext = {
|
const navigationContext: NavigationContext = {
|
||||||
entry: backstackEntry,
|
entry: backstackEntry,
|
||||||
isBackNavigation: false
|
isBackNavigation: false,
|
||||||
|
navigationType: NavigationType.forward
|
||||||
}
|
}
|
||||||
|
|
||||||
this._navigationQueue.push(navigationContext);
|
this._navigationQueue.push(navigationContext);
|
||||||
@ -232,10 +236,10 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
|
|
||||||
newPage.onNavigatedTo(isBack);
|
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
|
// we do not want to execute two navigations in parallel in case
|
||||||
// additional navigation is triggered from the NavigatedTo handler.
|
// additional navigation is triggered from the NavigatedTo handler.
|
||||||
this._executingEntry = null;
|
this._executingContext = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public _updateBackstack(entry: BackstackEntry, navigationType: NavigationType): void {
|
public _updateBackstack(entry: BackstackEntry, navigationType: NavigationType): void {
|
||||||
@ -342,13 +346,14 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected _processNextNavigationEntry() {
|
protected _processNextNavigationEntry() {
|
||||||
if (!this.isLoaded || this._executingEntry) {
|
if (!this.isLoaded || this._executingContext) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._navigationQueue.length > 0) {
|
if (this._navigationQueue.length > 0) {
|
||||||
const navigationContext = this._navigationQueue[0];
|
const navigationContext = this._navigationQueue[0];
|
||||||
if (navigationContext.isBackNavigation) {
|
const isBackNavigation = navigationContext.navigationType === NavigationType.back;
|
||||||
|
if (isBackNavigation) {
|
||||||
this.performGoBack(navigationContext);
|
this.performGoBack(navigationContext);
|
||||||
} else {
|
} else {
|
||||||
this.performNavigation(navigationContext);
|
this.performNavigation(navigationContext);
|
||||||
@ -358,10 +363,12 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
|
|
||||||
@profile
|
@profile
|
||||||
public performNavigation(navigationContext: NavigationContext) {
|
public performNavigation(navigationContext: NavigationContext) {
|
||||||
const navContext = navigationContext.entry;
|
this._executingContext = navigationContext;
|
||||||
this._executingEntry = navContext;
|
|
||||||
this._onNavigatingTo(navContext, navigationContext.isBackNavigation);
|
const backstackEntry = navigationContext.entry;
|
||||||
this._navigateCore(navContext);
|
const isBackNavigation = navigationContext.navigationType === NavigationType.back;
|
||||||
|
this._onNavigatingTo(backstackEntry, isBackNavigation);
|
||||||
|
this._navigateCore(backstackEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
@ -373,7 +380,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
navigationContext.entry = backstackEntry;
|
navigationContext.entry = backstackEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._executingEntry = backstackEntry;
|
this._executingContext = navigationContext;
|
||||||
this._onNavigatingTo(backstackEntry, true);
|
this._onNavigatingTo(backstackEntry, true);
|
||||||
this._goBackCore(backstackEntry);
|
this._goBackCore(backstackEntry);
|
||||||
}
|
}
|
||||||
@ -643,9 +650,6 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected replacePage(context: ModuleContext): void {
|
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 currentBackstackEntry = this._currentEntry;
|
||||||
const contextModuleName = getModuleName(context.path);
|
const contextModuleName = getModuleName(context.path);
|
||||||
|
|
||||||
@ -658,10 +662,15 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
frameId: currentBackstackEntry.frameId
|
frameId: currentBackstackEntry.frameId
|
||||||
};
|
};
|
||||||
|
|
||||||
const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false };
|
const navigationContext: NavigationContext = {
|
||||||
this.performNavigation(navContext);
|
entry: newBackstackEntry,
|
||||||
}
|
isBackNavigation: false,
|
||||||
|
navigationType: NavigationType.replace
|
||||||
|
};
|
||||||
|
|
||||||
|
this._navigationQueue.push(navigationContext);
|
||||||
|
this._processNextNavigationEntry();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFrameById(id: string): FrameBase {
|
export function getFrameById(id: string): FrameBase {
|
||||||
|
@ -163,24 +163,12 @@ export class Frame extends FrameBase {
|
|||||||
// In this case call _navigateCore in order to recreate the current fragment.
|
// In this case call _navigateCore in order to recreate the current fragment.
|
||||||
// Don't call navigate because it will fire navigation events.
|
// Don't call navigate because it will fire navigation events.
|
||||||
// As JS instances are alive it is already done for the current page.
|
// As JS instances are alive it is already done for the current page.
|
||||||
if (!this.isLoaded || this._executingEntry || !this._attachedToWindow) {
|
if (!this.isLoaded || this._executingContext || !this._attachedToWindow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const animatedEntries = _getAnimatedEntries(this._android.frameId);
|
const animatedEntries = _getAnimatedEntries(this._android.frameId);
|
||||||
if (animatedEntries) {
|
if (animatedEntries) {
|
||||||
// // recreate UI on the animated fragments because we have new context.
|
|
||||||
// // We need to recreate the UI because it Frame will do it only for currentPage.
|
|
||||||
// // Once currentPage is changed due to transition end we will have no UI on the
|
|
||||||
// // new Page.
|
|
||||||
// animatedEntries.forEach(entry => {
|
|
||||||
// const page = entry.resolvedPage;
|
|
||||||
// if (page._context !== this._context) {
|
|
||||||
// page._tearDownUI(true);
|
|
||||||
// page._setupUI(this._context);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Wait until animations are completed.
|
// Wait until animations are completed.
|
||||||
if (animatedEntries.size > 0) {
|
if (animatedEntries.size > 0) {
|
||||||
return;
|
return;
|
||||||
@ -212,7 +200,13 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _getChildFragmentManager() {
|
public _getChildFragmentManager() {
|
||||||
const backstackEntry = this._executingEntry || this._currentEntry;
|
let backstackEntry;
|
||||||
|
if (this._executingContext && this._executingContext.entry) {
|
||||||
|
backstackEntry = this._executingContext.entry;
|
||||||
|
} else {
|
||||||
|
backstackEntry = this._currentEntry;
|
||||||
|
}
|
||||||
|
|
||||||
if (backstackEntry && backstackEntry.fragment && backstackEntry.fragment.isAdded()) {
|
if (backstackEntry && backstackEntry.fragment && backstackEntry.fragment.isAdded()) {
|
||||||
return backstackEntry.fragment.getChildFragmentManager();
|
return backstackEntry.fragment.getChildFragmentManager();
|
||||||
}
|
}
|
||||||
@ -324,7 +318,7 @@ export class Frame extends FrameBase {
|
|||||||
if (navigationType === NavigationType.replace) {
|
if (navigationType === NavigationType.replace) {
|
||||||
_clearEntry(entry);
|
_clearEntry(entry);
|
||||||
|
|
||||||
const animated = entry.entry.animated;
|
const animated = this._getIsAnimatedNavigation(entry.entry);
|
||||||
const navigationTransition = this._getNavigationTransition(entry.entry);
|
const navigationTransition = this._getNavigationTransition(entry.entry);
|
||||||
const currentEntry = null;
|
const currentEntry = null;
|
||||||
const newEntry = entry;
|
const newEntry = entry;
|
||||||
@ -353,12 +347,6 @@ export class Frame extends FrameBase {
|
|||||||
@profile
|
@profile
|
||||||
public _navigateCore(newEntry: BackstackEntry) {
|
public _navigateCore(newEntry: BackstackEntry) {
|
||||||
super._navigateCore(newEntry);
|
super._navigateCore(newEntry);
|
||||||
// NavigationType.replace for HMR.
|
|
||||||
// Otherwise, default to NavigationType.forward.
|
|
||||||
const isReplace = this.navigationType === NavigationType.replace;
|
|
||||||
if (!isReplace) {
|
|
||||||
this.navigationType = NavigationType.forward;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set frameId here so that we could use it in fragment.transitions
|
// set frameId here so that we could use it in fragment.transitions
|
||||||
newEntry.frameId = this._android.frameId;
|
newEntry.frameId = this._android.frameId;
|
||||||
@ -385,6 +373,7 @@ export class Frame extends FrameBase {
|
|||||||
navDepth = -1;
|
navDepth = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isReplace = this._executingContext && this._executingContext.navigationType === NavigationType.replace;
|
||||||
if (!isReplace) {
|
if (!isReplace) {
|
||||||
navDepth++;
|
navDepth++;
|
||||||
}
|
}
|
||||||
@ -419,7 +408,6 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _goBackCore(backstackEntry: BackstackEntry) {
|
public _goBackCore(backstackEntry: BackstackEntry) {
|
||||||
this.navigationType = NavigationType.back;
|
|
||||||
super._goBackCore(backstackEntry);
|
super._goBackCore(backstackEntry);
|
||||||
navDepth = backstackEntry.navDepth;
|
navDepth = backstackEntry.navDepth;
|
||||||
|
|
||||||
@ -477,18 +465,18 @@ export class Frame extends FrameBase {
|
|||||||
const listener = getAttachListener();
|
const listener = getAttachListener();
|
||||||
this.nativeViewProtected.removeOnAttachStateChangeListener(listener);
|
this.nativeViewProtected.removeOnAttachStateChangeListener(listener);
|
||||||
this.nativeViewProtected[ownerSymbol] = null;
|
this.nativeViewProtected[ownerSymbol] = null;
|
||||||
this._tearDownPending = !!this._executingEntry;
|
this._tearDownPending = !!this._executingContext;
|
||||||
const current = this._currentEntry;
|
const current = this._currentEntry;
|
||||||
|
const executingEntry = this._executingContext ? this._executingContext.entry : null;
|
||||||
this.backStack.forEach(entry => {
|
this.backStack.forEach(entry => {
|
||||||
// Don't destroy current and executing entries or UI will look blank.
|
// Don't destroy current and executing entries or UI will look blank.
|
||||||
// We will do it in setCurrent.
|
// We will do it in setCurrent.
|
||||||
if (entry !== this._executingEntry) {
|
if (entry !== executingEntry) {
|
||||||
clearEntry(entry);
|
clearEntry(entry);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (current && !this._executingEntry) {
|
if (current && !executingEntry) {
|
||||||
clearEntry(current);
|
clearEntry(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,11 +699,11 @@ function findPageForFragment(fragment: android.support.v4.app.Fragment, frame: F
|
|||||||
|
|
||||||
let entry: BackstackEntry;
|
let entry: BackstackEntry;
|
||||||
const current = frame._currentEntry;
|
const current = frame._currentEntry;
|
||||||
const navigating = frame._executingEntry;
|
const executingContext = frame._executingContext;
|
||||||
if (current && current.fragmentTag === fragmentTag) {
|
if (current && current.fragmentTag === fragmentTag) {
|
||||||
entry = current;
|
entry = current;
|
||||||
} else if (navigating && navigating.fragmentTag === fragmentTag) {
|
} else if (executingContext && executingContext.entry && executingContext.entry.fragmentTag === fragmentTag) {
|
||||||
entry = navigating;
|
entry = executingContext.entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
let page: Page;
|
let page: Page;
|
||||||
|
17
tns-core-modules/ui/frame/frame.d.ts
vendored
17
tns-core-modules/ui/frame/frame.d.ts
vendored
@ -135,11 +135,19 @@ export class Frame extends View {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_executingEntry: BackstackEntry;
|
_executingContext: NavigationContext;
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_processNavigationQueue(page: Page);
|
_processNavigationQueue(page: Page);
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getIsAnimatedNavigation(entry: NavigationEntry): boolean;
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getNavigationTransition(entry: NavigationEntry): NavigationTransition;
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -173,12 +181,6 @@ export class Frame extends View {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_removeFromFrameStack();
|
_removeFromFrameStack();
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* Represents the type of navigation.
|
|
||||||
*/
|
|
||||||
navigationType: NavigationType;
|
|
||||||
//@endprivate
|
//@endprivate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -289,6 +291,7 @@ export interface NavigationEntry extends ViewEntry {
|
|||||||
export interface NavigationContext {
|
export interface NavigationContext {
|
||||||
entry: BackstackEntry;
|
entry: BackstackEntry;
|
||||||
isBackNavigation: boolean;
|
isBackNavigation: boolean;
|
||||||
|
navigationType: NavigationType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,12 +65,6 @@ export class Frame extends FrameBase {
|
|||||||
|
|
||||||
@profile
|
@profile
|
||||||
public _navigateCore(backstackEntry: BackstackEntry) {
|
public _navigateCore(backstackEntry: BackstackEntry) {
|
||||||
// NavigationType.replace for HMR.
|
|
||||||
// Otherwise, default to NavigationType.forward.
|
|
||||||
const isReplace = this.navigationType === NavigationType.replace;
|
|
||||||
if (!isReplace) {
|
|
||||||
this.navigationType = NavigationType.forward;
|
|
||||||
}
|
|
||||||
super._navigateCore(backstackEntry);
|
super._navigateCore(backstackEntry);
|
||||||
|
|
||||||
let viewController: UIViewController = backstackEntry.resolvedPage.ios;
|
let viewController: UIViewController = backstackEntry.resolvedPage.ios;
|
||||||
@ -82,6 +76,8 @@ export class Frame extends FrameBase {
|
|||||||
if (clearHistory) {
|
if (clearHistory) {
|
||||||
navDepth = -1;
|
navDepth = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isReplace = this._executingContext && this._executingContext.navigationType === NavigationType.replace;
|
||||||
if (!isReplace) {
|
if (!isReplace) {
|
||||||
navDepth++;
|
navDepth++;
|
||||||
}
|
}
|
||||||
@ -187,7 +183,6 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _goBackCore(backstackEntry: BackstackEntry) {
|
public _goBackCore(backstackEntry: BackstackEntry) {
|
||||||
this.navigationType = NavigationType.back;
|
|
||||||
super._goBackCore(backstackEntry);
|
super._goBackCore(backstackEntry);
|
||||||
navDepth = backstackEntry[NAV_DEPTH];
|
navDepth = backstackEntry[NAV_DEPTH];
|
||||||
|
|
||||||
|
@ -22,18 +22,25 @@ const majorVersion = iosUtils.MajorVersion;
|
|||||||
|
|
||||||
function isBackNavigationTo(page: Page, entry): boolean {
|
function isBackNavigationTo(page: Page, entry): boolean {
|
||||||
const frame = page.frame;
|
const frame = page.frame;
|
||||||
if (!frame || frame.navigationType === NavigationType.replace) {
|
if (!frame) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if executing context is null here this most probably means back navigation through iOS back button
|
||||||
|
const navigationContext = frame._executingContext || { navigationType: NavigationType.back };
|
||||||
|
const isReplace = navigationContext.navigationType === NavigationType.replace;
|
||||||
|
if (isReplace) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame.navigationQueueIsEmpty()) {
|
if (frame.navigationQueueIsEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
const navigationQueue = (<any>frame)._navigationQueue;
|
const navigationQueue = (<any>frame)._navigationQueue;
|
||||||
for (let i = 0; i < navigationQueue.length; i++) {
|
for (let i = 0; i < navigationQueue.length; i++) {
|
||||||
if (navigationQueue[i].entry === entry) {
|
if (navigationQueue[i].entry === entry) {
|
||||||
return navigationQueue[i].isBackNavigation;
|
return navigationQueue[i].navigationType === NavigationType.back;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,30 +136,23 @@ class UIViewControllerImpl extends UIViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const navigationController = this.navigationController;
|
const navigationController = this.navigationController;
|
||||||
const frame = navigationController ? (<any>navigationController).owner : null;
|
const frame: Frame = navigationController ? (<any>navigationController).owner : null;
|
||||||
// Skip navigation events if modal page is shown.
|
// Skip navigation events if modal page is shown.
|
||||||
if (!owner._presentedViewController && frame) {
|
if (!owner._presentedViewController && frame) {
|
||||||
const newEntry: BackstackEntry = this[ENTRY];
|
const newEntry: BackstackEntry = this[ENTRY];
|
||||||
|
|
||||||
let isBack: boolean;
|
// frame.setCurrent(...) will reset executing context so retrieve it here
|
||||||
let navType = frame.navigationType;
|
// if executing context is null here this most probably means back navigation through iOS back button
|
||||||
// We are on the current page which happens when navigation is canceled so isBack should be false.
|
const navigationContext = frame._executingContext || { navigationType: NavigationType.back };
|
||||||
if (navType !== NavigationType.replace && frame.currentPage === owner && frame._navigationQueue.length === 0) {
|
const isReplace = navigationContext.navigationType === NavigationType.replace;
|
||||||
isBack = false;
|
|
||||||
navType = NavigationType.forward;
|
|
||||||
} else {
|
|
||||||
isBack = isBackNavigationTo(owner, newEntry);
|
|
||||||
if (isBack) {
|
|
||||||
navType = NavigationType.back;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.setCurrent(newEntry, navType);
|
frame.setCurrent(newEntry, navigationContext.navigationType);
|
||||||
|
|
||||||
if (frame.navigationType === NavigationType.replace) {
|
if (isReplace) {
|
||||||
let controller = newEntry.resolvedPage.ios;
|
let controller = newEntry.resolvedPage.ios;
|
||||||
if (controller) {
|
if (controller) {
|
||||||
if (newEntry.entry.animated) {
|
const animated = frame._getIsAnimatedNavigation(newEntry.entry);
|
||||||
|
if (animated) {
|
||||||
controller[TRANSITION] = frame._getNavigationTransition(newEntry.entry);
|
controller[TRANSITION] = frame._getNavigationTransition(newEntry.entry);
|
||||||
} else {
|
} else {
|
||||||
controller[TRANSITION] = { name: NON_ANIMATED_TRANSITION };
|
controller[TRANSITION] = { name: NON_ANIMATED_TRANSITION };
|
||||||
@ -160,8 +160,6 @@ class UIViewControllerImpl extends UIViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.navigationType = isBack ? NavigationType.back : NavigationType.forward;
|
|
||||||
|
|
||||||
// If page was shown with custom animation - we need to set the navigationController.delegate to the animatedDelegate.
|
// If page was shown with custom animation - we need to set the navigationController.delegate to the animatedDelegate.
|
||||||
frame.ios.controller.delegate = this[DELEGATE];
|
frame.ios.controller.delegate = this[DELEGATE];
|
||||||
|
|
||||||
@ -209,7 +207,7 @@ class UIViewControllerImpl extends UIViewController {
|
|||||||
const willSelectViewController = tab && (<any>tab)._willSelectViewController;
|
const willSelectViewController = tab && (<any>tab)._willSelectViewController;
|
||||||
if (!willSelectViewController
|
if (!willSelectViewController
|
||||||
|| willSelectViewController === tab.selectedViewController) {
|
|| willSelectViewController === tab.selectedViewController) {
|
||||||
let isBack = isBackNavigationFrom(this, owner);
|
const isBack = isBackNavigationFrom(this, owner);
|
||||||
owner.onNavigatingFrom(isBack);
|
owner.onNavigatingFrom(isBack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user