feat(hmr): preserve navigation history on applying changes (#7146)

This commit is contained in:
Vasil Chimev
2019-04-23 17:47:29 +03:00
committed by GitHub
parent 4e56c89f7d
commit d35e14ed0f
23 changed files with 414 additions and 168 deletions

View File

@@ -160,7 +160,7 @@ export abstract class ViewBase extends Observable {
/**
* @deprecated use showModal with ShowModalOptions instead
*
*
* Shows the View contained in moduleName as a modal view.
* @param moduleName - The name of the module to load starting from the application root.
* @param context - Any context you want to pass to the modally shown view.
@@ -175,7 +175,7 @@ export abstract class ViewBase extends Observable {
/**
* @deprecated use showModal with ShowModalOptions instead
*
*
* Shows the view passed as parameter as a modal view.
* @param view - View instance to be shown modally.
* @param context - Any context you want to pass to the modally shown view. This same context will be available in the arguments of the shownModally event handler.
@@ -367,7 +367,7 @@ export abstract class ViewBase extends Observable {
public _goToVisualState(state: string): void;
/**
* @deprecated
*
*
* This used to be the way to set attribute values in early {N} versions.
* Now attributes are expected to be set as plain properties on the view instances.
*/

View File

@@ -7,8 +7,7 @@ import {
import {
ViewBase, Property, booleanConverter, eachDescendant, EventData, layout,
getEventOrGestureName, traceEnabled, traceWrite, traceCategories,
InheritedProperty,
ShowModalOptions
InheritedProperty, ShowModalOptions
} from "../view-base";
import { HorizontalAlignment, VerticalAlignment, Visibility, Length, PercentLength } from "../../styling/style-properties";
@@ -38,6 +37,12 @@ function ensureAnimationModule() {
}
}
export enum ModuleType {
markup = "markup",
script = "script",
style = "style"
}
export function CSSType(type: string): ClassDecorator {
return (cls) => {
cls.prototype.cssType = type;
@@ -138,12 +143,22 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
}
public _onLivesync(context?: ModuleContext): boolean {
if (traceEnabled()) {
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
}
_rootModalViews.forEach(v => v.closeModal());
_rootModalViews.length = 0;
// Currently, we pass `context` only for style modules
if (context && context.path) {
return this.changeLocalStyles(context.path);
if (context && context.type && context.path) {
// Handle local styles
if (context.type === ModuleType.style) {
return this.changeLocalStyles(context.path);
}
// Handle module markup and script changes
else {
return this.changeModule(context);
}
}
return false;
@@ -156,11 +171,16 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return true;
});
}
// Do not execute frame navigation for a change in styles
// Do not reset activity/window content for local styles changes
return true;
}
private changeStyles(view: ViewBase, contextPath: string): boolean {
if (traceEnabled()) {
traceWrite(`${view}.${view._moduleName}`, traceCategories.Livesync);
}
if (view._moduleName && contextPath.includes(view._moduleName)) {
(<this>view).changeCssFile(contextPath);
return true;
@@ -168,6 +188,23 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return false;
}
private changeModule(context: ModuleContext): boolean {
eachDescendant(this, (child: ViewBase) => {
if (traceEnabled()) {
traceWrite(`${child}.${child._moduleName}`, traceCategories.Livesync);
}
// Handle changes in module's Page
if (child._moduleName && context.path.includes(child._moduleName) && child.page) {
child.page._onLivesync(context);
}
return true;
});
// Do not reset activity/window content for module changes
return true;
}
_setupAsRootView(context: any): void {
super._setupAsRootView(context);
if (!this._styleScope) {

View File

@@ -232,22 +232,22 @@ export function _getAnimatedEntries(frameId: number): Set<BackstackEntry> {
export function _updateTransitions(entry: ExpandedEntry): void {
const fragment = entry.fragment;
const enterTransitionListener = entry.enterTransitionListener;
if (enterTransitionListener) {
if (enterTransitionListener && fragment) {
fragment.setEnterTransition(enterTransitionListener.transition);
}
const exitTransitionListener = entry.exitTransitionListener;
if (exitTransitionListener) {
if (exitTransitionListener && fragment) {
fragment.setExitTransition(exitTransitionListener.transition);
}
const reenterTransitionListener = entry.reenterTransitionListener;
if (reenterTransitionListener) {
if (reenterTransitionListener && fragment) {
fragment.setReenterTransition(reenterTransitionListener.transition);
}
const returnTransitionListener = entry.returnTransitionListener;
if (returnTransitionListener) {
if (returnTransitionListener && fragment) {
fragment.setReturnTransition(returnTransitionListener.transition);
}
}
@@ -374,7 +374,7 @@ function getAnimationListener(): android.animation.Animator.AnimatorListener {
return AnimationListener;
}
function addToWaitingQueue(entry: ExpandedEntry): void {
const frameId = entry.frameId;
let entries = waitingQueue.get(frameId);
@@ -659,7 +659,7 @@ function setupAllAnimation(entry: ExpandedEntry, transition: Transition): void {
setupExitAndPopEnterAnimation(entry, transition);
const listener = getAnimationListener();
// setupAllAnimation is called only for new fragments so we don't
// setupAllAnimation is called only for new fragments so we don't
// need to clearAnimationListener for enter & popExit animators.
const enterAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.enter);
enterAnimator.transitionType = AndroidTransitionType.enter;
@@ -720,7 +720,7 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry): void {
if (entries.size === 0) {
const frame = entry.resolvedPage.frame;
// 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);
completedEntries.delete(frameId);
waitingQueue.delete(frameId);
@@ -730,8 +730,8 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry): void {
// Will be null if Frame is shown modally...
// transitionOrAnimationCompleted fires again (probably bug in android).
if (current) {
const isBack = frame._isBack;
setTimeout(() => frame.setCurrent(current, isBack));
const navType = frame.navigationType;
setTimeout(() => frame.setCurrent(current, navType));
}
} else {
completedEntries.set(frameId, entry);

View File

@@ -11,6 +11,12 @@ import { profile } from "../../profiling";
import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _popFromFrameStack, _removeFromFrameStack } from "./frame-stack";
export * from "../core/view";
export enum NavigationType {
back,
forward,
replace
}
function buildEntryFromArgs(arg: any): NavigationEntry {
let entry: NavigationEntry;
if (typeof arg === "string") {
@@ -48,6 +54,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
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.
@@ -206,7 +213,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
return this._currentEntry === entry;
}
public setCurrent(entry: BackstackEntry, isBack: boolean): void {
public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void {
const newPage = entry.resolvedPage;
// In case we navigated forward to a page that was in the backstack
// with clearHistory: true
@@ -217,6 +224,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
this._currentEntry = entry;
const isBack = navigationType === NavigationType.back;
if (isBack) {
this._pushInFrameStack();
}
@@ -229,15 +237,18 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
this._executingEntry = null;
}
public _updateBackstack(entry: BackstackEntry, isBack: boolean): void {
public _updateBackstack(entry: BackstackEntry, navigationType: NavigationType): void {
const isBack = navigationType === NavigationType.back;
const isReplace = navigationType === NavigationType.replace;
this.raiseCurrentPageNavigatedEvents(isBack);
const current = this._currentEntry;
// Do nothing for Hot Module Replacement
if (isBack) {
const index = this._backStack.indexOf(entry);
this._backStack.splice(index + 1).forEach(e => this._removeEntry(e));
this._backStack.pop();
} else {
} else if (!isReplace) {
if (entry.entry.clearHistory) {
this._backStack.forEach(e => this._removeEntry(e));
this._backStack.length = 0;
@@ -345,7 +356,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
}
@profile
private performNavigation(navigationContext: NavigationContext) {
public performNavigation(navigationContext: NavigationContext) {
const navContext = navigationContext.entry;
this._executingEntry = navContext;
this._onNavigatingTo(navContext, navigationContext.isBackNavigation);
@@ -563,35 +574,39 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
return result;
}
public _onLivesync(context?: ModuleContext): boolean {
// Execute a navigation if not handled on `View` level
if (!super._onLivesync(context)) {
if (!this._currentEntry || !this._currentEntry.entry) {
public _onLivesync(): boolean {
// Reset activity/window content when:
// + Changes are not handled on View
// + There is no ModuleContext
if (traceEnabled()) {
traceWrite(`${this}._onLivesync()`, traceCategories.Livesync);
}
if (!this._currentEntry || !this._currentEntry.entry) {
return false;
}
const currentEntry = this._currentEntry.entry;
const newEntry: NavigationEntry = {
animated: false,
clearHistory: true,
context: currentEntry.context,
create: currentEntry.create,
moduleName: currentEntry.moduleName,
backstackVisible: currentEntry.backstackVisible
}
// If create returns the same page instance we can't recreate it.
// Instead of navigation set activity content.
// This could happen if current page was set in XML as a Page instance.
if (newEntry.create) {
const page = newEntry.create();
if (page === this.currentPage) {
return false;
}
const currentEntry = this._currentEntry.entry;
const newEntry: NavigationEntry = {
animated: false,
clearHistory: true,
context: currentEntry.context,
create: currentEntry.create,
moduleName: currentEntry.moduleName,
backstackVisible: currentEntry.backstackVisible
}
// If create returns the same page instance we can't recreate it.
// Instead of navigation set activity content.
// This could happen if current page was set in XML as a Page instance.
if (newEntry.create) {
const page = newEntry.create();
if (page === this.currentPage) {
return false;
}
}
this.navigate(newEntry);
}
this.navigate(newEntry);
return true;
}
}

View File

@@ -1,15 +1,16 @@
// Definitions.
import {
AndroidFrame as AndroidFrameDefinition, BackstackEntry,
NavigationTransition, AndroidFragmentCallbacks, AndroidActivityCallbacks
AndroidFrame as AndroidFrameDefinition, AndroidActivityCallbacks,
AndroidFragmentCallbacks, BackstackEntry, NavigationTransition
} from ".";
import { ModuleType } from "../../ui/core/view/view-common";
import { Page } from "../page";
// Types.
import * as application from "../../application";
import {
FrameBase, stack, goBack, View, Observable,
traceEnabled, traceWrite, traceCategories, traceError
FrameBase, goBack, stack, NavigationContext, NavigationType,
Observable, View, traceCategories, traceEnabled, traceError, traceWrite
} from "./frame-common";
import {
@@ -21,6 +22,7 @@ import { profile } from "../../profiling";
// TODO: Remove this and get it from global to decouple builder for angular
import { createViewFromEntry } from "../builder";
import { getModuleName } from "../../utils/utils";
export * from "./frame-common";
@@ -87,8 +89,16 @@ export function reloadPage(context?: ModuleContext): void {
const callbacks: AndroidActivityCallbacks = activity[CALLBACKS];
if (callbacks) {
const rootView: View = callbacks.getRootView();
// Handle application root module
const isAppRootModuleChanged = context && context.path && context.path.includes(application.getMainEntry().moduleName) && context.type !== ModuleType.style;
if (!rootView || !rootView._onLivesync(context)) {
// Reset activity content when:
// + Application root module is changed
// + View did not handle the change
// Note:
// The case when neither app root module is changed, neighter livesync is handled on View,
// then changes will not apply until navigate forward to the module.
if (isAppRootModuleChanged || !rootView || !rootView._onLivesync(context)) {
callbacks.resetActivityContent(activity);
}
} else {
@@ -104,7 +114,6 @@ export class Frame extends FrameBase {
private _containerViewId: number = -1;
private _tearDownPending = false;
private _attachedToWindow = false;
public _isBack: boolean = true;
private _cachedAnimatorState: AnimatorState;
constructor() {
@@ -263,11 +272,11 @@ export class Frame extends FrameBase {
return newFragment;
}
public setCurrent(entry: BackstackEntry, isBack: boolean): void {
public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void {
const current = this._currentEntry;
const currentEntryChanged = current !== entry;
if (currentEntryChanged) {
this._updateBackstack(entry, isBack);
this._updateBackstack(entry, navigationType);
// If activity was destroyed we need to destroy fragment and UI
// of current and new entries.
@@ -296,7 +305,7 @@ export class Frame extends FrameBase {
}
}
super.setCurrent(entry, isBack);
super.setCurrent(entry, navigationType);
// If we had real navigation process queue.
this._processNavigationQueue(entry.resolvedPage);
@@ -330,10 +339,48 @@ export class Frame extends FrameBase {
return false;
}
public _onLivesync(context?: ModuleContext): boolean {
if (traceEnabled()) {
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
}
if (!this._currentEntry || !this._currentEntry.entry) {
return false;
}
if (context && context.type && context.path) {
// Set NavigationType.replace for HMR.
this.navigationType = NavigationType.replace;
const currentBackstackEntry = this._currentEntry;
const contextModuleName = getModuleName(context.path);
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
const newBackstackEntry: BackstackEntry = {
entry: currentBackstackEntry.entry,
resolvedPage: newPage,
navDepth: currentBackstackEntry.navDepth,
fragmentTag: currentBackstackEntry.fragmentTag,
frameId: currentBackstackEntry.frameId
};
const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false };
this.performNavigation(navContext);
return true;
} else {
// Fallback
return super._onLivesync();
}
}
@profile
public _navigateCore(newEntry: BackstackEntry) {
super._navigateCore(newEntry);
this._isBack = false;
// 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
newEntry.frameId = this._android.frameId;
@@ -360,7 +407,10 @@ export class Frame extends FrameBase {
navDepth = -1;
}
navDepth++;
if (!isReplace) {
navDepth++;
}
fragmentId++;
const newFragmentTag = `fragment${fragmentId}[${navDepth}]`;
const newFragment = this.createFragment(newEntry, newFragmentTag);
@@ -383,7 +433,7 @@ export class Frame extends FrameBase {
}
public _goBackCore(backstackEntry: BackstackEntry) {
this._isBack = true;
this.navigationType = NavigationType.back;
super._goBackCore(backstackEntry);
navDepth = backstackEntry.navDepth;
@@ -1282,4 +1332,4 @@ export function setActivityCallbacks(activity: android.support.v7.app.AppCompatA
export function setFragmentCallbacks(fragment: android.support.v4.app.Fragment): void {
fragment[CALLBACKS] = new FragmentCallbacksImplementation();
}
}

View File

@@ -3,14 +3,14 @@
* @module "ui/frame"
*/ /** */
import { NavigationType } from "./frame-common";
import { Page, View, Observable, EventData } from "../page";
import { Transition } from "../transition";
export * from "../page";
/**
* Represents the logical View unit that is responsible for navigation withing an application.
* Typically an application will have a Frame object at a root level.
* Represents the logical View unit that is responsible for navigation within an application.
* Nested frames are supported, enabling hierarchical navigation scenarios.
*/
export class Frame extends View {
@@ -113,12 +113,13 @@ export class Frame extends View {
* @param entry to check
*/
isCurrent(entry: BackstackEntry): boolean;
/**
* @private
* @param entry to set as current
* @param isBack true when we set current because of back navigation.
* @param navigationType
*/
setCurrent(entry: BackstackEntry, isBack: boolean): void;
setCurrent(entry: BackstackEntry, navigationType: NavigationType): void;
/**
* @private
*/
@@ -143,6 +144,11 @@ export class Frame extends View {
* @private
*/
_updateActionBar(page?: Page, disableNavBarAnimation?: boolean);
/**
* @private
* @param navigationContext
*/
public performNavigation(navigationContext: NavigationContext): void;
/**
* @private
*/
@@ -154,7 +160,7 @@ export class Frame extends View {
/**
* @private
*/
_updateBackstack(entry: BackstackEntry, isBack: boolean): void;
_updateBackstack(entry: BackstackEntry, navigationType: NavigationType): void;
/**
* @private
*/
@@ -167,10 +173,12 @@ export class Frame extends View {
* @private
*/
_removeFromFrameStack();
/**
* @private
* Represents the type of navigation.
*/
_isBack?: boolean;
navigationType: NavigationType;
//@endprivate
/**
@@ -275,6 +283,14 @@ export interface NavigationEntry extends ViewEntry {
clearHistory?: boolean;
}
/**
* Represents a context passed to navigation methods.
*/
export interface NavigationContext {
entry: BackstackEntry;
isBackNavigation: boolean;
}
/**
* Represents an object specifying a page navigation transition.
*/

View File

@@ -1,12 +1,18 @@
// Definitions.
import { iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition } from ".";
import {
iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition
} from ".";
import { Page } from "../page";
import { profile } from "../../profiling";
//Types.
import { FrameBase, View, layout, traceEnabled, traceWrite, traceCategories, isCategorySet } from "./frame-common";
import {
FrameBase, View, isCategorySet, layout, NavigationContext,
NavigationType, traceCategories, traceEnabled, traceWrite
} from "./frame-common";
import { _createIOSAnimatedTransitioning } from "./fragment.transitions";
import { createViewFromEntry } from "../builder";
import * as utils from "../../utils/utils";
export * from "./frame-common";
@@ -14,9 +20,10 @@ export * from "./frame-common";
const majorVersion = utils.ios.MajorVersion;
const ENTRY = "_entry";
const DELEGATE = "_delegate";
const NAV_DEPTH = "_navDepth";
const TRANSITION = "_transition";
const DELEGATE = "_delegate";
const NON_ANIMATED_TRANSITION = "non-animated";
let navDepth = -1;
@@ -46,18 +53,57 @@ export class Frame extends FrameBase {
return this._ios;
}
public setCurrent(entry: BackstackEntry, isBack: boolean): void {
public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void {
const current = this._currentEntry;
const currentEntryChanged = current !== entry;
if (currentEntryChanged) {
this._updateBackstack(entry, isBack);
this._updateBackstack(entry, navigationType);
super.setCurrent(entry, isBack);
super.setCurrent(entry, navigationType);
}
}
public _onLivesync(context?: ModuleContext): boolean {
if (traceEnabled()) {
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
}
if (!this._currentEntry || !this._currentEntry.entry) {
return false;
}
if (context && context.type && context.path) {
// Set NavigationType.replace for HMR.
// When `viewDidAppear()` set to NavigationType.forward.
this.navigationType = NavigationType.replace;
const currentBackstackEntry = this._currentEntry;
const contextModuleName = utils.getModuleName(context.path);
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
const newBackstackEntry: BackstackEntry = {
entry: currentBackstackEntry.entry,
resolvedPage: newPage,
navDepth: currentBackstackEntry.navDepth,
fragmentTag: undefined
}
const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false };
this.performNavigation(navContext);
return true;
} else {
// Fallback
return super._onLivesync();
}
}
@profile
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);
let viewController: UIViewController = backstackEntry.resolvedPage.ios;
@@ -69,7 +115,9 @@ export class Frame extends FrameBase {
if (clearHistory) {
navDepth = -1;
}
navDepth++;
if (!isReplace) {
navDepth++;
}
let navigationTransition: NavigationTransition;
let animated = this.currentPage ? this._getIsAnimatedNavigation(backstackEntry.entry) : false;
@@ -81,7 +129,7 @@ export class Frame extends FrameBase {
}
else {
//https://github.com/NativeScript/NativeScript/issues/1787
viewController[TRANSITION] = { name: "non-animated" };
viewController[TRANSITION] = { name: NON_ANIMATED_TRANSITION };
}
let nativeTransition = _getNativeTransition(navigationTransition, true);
@@ -136,7 +184,8 @@ export class Frame extends FrameBase {
}
// We should hide the current entry from the back stack.
if (!Frame._isEntryBackstackVisible(this._currentEntry)) {
// This is the case for HMR when NavigationType.replace.
if (!Frame._isEntryBackstackVisible(this._currentEntry) || isReplace) {
let newControllers = NSMutableArray.alloc<UIViewController>().initWithArray(this._ios.controller.viewControllers);
if (newControllers.count === 0) {
throw new Error("Wrong controllers count.");
@@ -168,6 +217,7 @@ export class Frame extends FrameBase {
}
public _goBackCore(backstackEntry: BackstackEntry) {
this.navigationType = NavigationType.back;
super._goBackCore(backstackEntry);
navDepth = backstackEntry[NAV_DEPTH];
@@ -469,7 +519,7 @@ class UINavigationControllerImpl extends UINavigationController {
traceWrite(`UINavigationControllerImpl.popViewControllerAnimated(${animated}); transition: ${JSON.stringify(navigationTransition)}`, traceCategories.NativeLifecycle);
}
if (navigationTransition && navigationTransition.name === "non-animated") {
if (navigationTransition && navigationTransition.name === NON_ANIMATED_TRANSITION) {
//https://github.com/NativeScript/NativeScript/issues/1787
return super.popViewControllerAnimated(false);
}
@@ -493,7 +543,7 @@ class UINavigationControllerImpl extends UINavigationController {
traceWrite(`UINavigationControllerImpl.popToViewControllerAnimated(${viewController}, ${animated}); transition: ${JSON.stringify(navigationTransition)}`, traceCategories.NativeLifecycle);
}
if (navigationTransition && navigationTransition.name === "non-animated") {
if (navigationTransition && navigationTransition.name === NON_ANIMATED_TRANSITION) {
//https://github.com/NativeScript/NativeScript/issues/1787
return super.popToViewControllerAnimated(viewController, false);
}

View File

@@ -99,6 +99,10 @@ export class PageBase extends ContentView implements PageDefinition {
};
}
public _onLivesync(context?: ModuleContext): boolean {
return this.frame ? this.frame._onLivesync(context) : false;
}
@profile
public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) {
this._navigationContext = context;
@@ -190,4 +194,4 @@ export const androidStatusBarBackgroundProperty = new CssProperty<Style, Color>(
name: "androidStatusBarBackground", cssName: "android-status-bar-background",
equalityComparer: Color.equals, valueConverter: (v) => new Color(v)
});
androidStatusBarBackgroundProperty.register(Style);
androidStatusBarBackgroundProperty.register(Style);

View File

@@ -1,11 +1,11 @@
// Definitions.
import { Frame } from "../frame";
import { NavigationType } from "../frame/frame-common";
// Types.
import { ios as iosView } from "../core/view";
import {
PageBase, View, layout,
actionBarHiddenProperty, statusBarStyleProperty, Color
PageBase, View, layout, actionBarHiddenProperty, statusBarStyleProperty, Color
} from "./page-common";
import { profile } from "../../profiling";
@@ -20,7 +20,7 @@ const majorVersion = iosUtils.MajorVersion;
function isBackNavigationTo(page: Page, entry): boolean {
const frame = page.frame;
if (!frame) {
if (!frame || frame.navigationType === NavigationType.replace) {
return false;
}
@@ -133,14 +133,20 @@ class UIViewControllerImpl extends UIViewController {
const newEntry = this[ENTRY];
let isBack: boolean;
let navType = frame.navigationType;
// We are on the current page which happens when navigation is canceled so isBack should be false.
if (frame.currentPage === owner && frame._navigationQueue.length === 0) {
if (navType !== NavigationType.replace && frame.currentPage === owner && frame._navigationQueue.length === 0) {
isBack = false;
navType = NavigationType.forward;
} else {
isBack = isBackNavigationTo(owner, newEntry);
if (isBack) {
navType = NavigationType.back;
}
}
frame.setCurrent(newEntry, isBack);
frame.setCurrent(newEntry, navType);
frame.navigationType = isBack ? NavigationType.back : NavigationType.forward;
// If page was shown with custom animation - we need to set the navigationController.delegate to the animatedDelegate.
frame.ios.controller.delegate = this[DELEGATE];
@@ -182,7 +188,7 @@ class UIViewControllerImpl extends UIViewController {
const frame = owner.frame;
// Skip navigation events if we are hiding because we are about to show a modal page,
// or because we are closing a modal page,
// or because we are closing a modal page,
// or because we are in tab and another controller is selected.
const tab = this.tabBarController;
if (owner.onNavigatingFrom && !owner._presentedViewController && !this.presentingViewController && frame && frame.currentPage === owner) {