mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 19:26:42 +08:00
feat(hmr): preserve navigation history on applying changes (#7146)
This commit is contained in:
5
tests/app/app/button-page.xml
Normal file
5
tests/app/app/button-page.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<Page loaded="onLoaded">
|
||||||
|
<StackLayout>
|
||||||
|
<Button id="button" text="button"></Button>
|
||||||
|
</StackLayout>
|
||||||
|
</Page>
|
3
tests/app/livesync/livesync-button-page.ts
Normal file
3
tests/app/livesync/livesync-button-page.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function onLoaded() {
|
||||||
|
console.log("Button page loaded!");
|
||||||
|
}
|
5
tests/app/livesync/livesync-button-page.xml
Normal file
5
tests/app/livesync/livesync-button-page.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<Page loaded="onLoaded">
|
||||||
|
<StackLayout>
|
||||||
|
<Button id="button" text="button"></Button>
|
||||||
|
</StackLayout>
|
||||||
|
</Page>
|
3
tests/app/livesync/livesync-label-page.ts
Normal file
3
tests/app/livesync/livesync-label-page.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function onLoaded() {
|
||||||
|
console.log("Label page loaded!");
|
||||||
|
}
|
5
tests/app/livesync/livesync-label-page.xml
Normal file
5
tests/app/livesync/livesync-label-page.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<Page loaded="onLoaded">
|
||||||
|
<StackLayout>
|
||||||
|
<Label id="label" text="label"></Label>
|
||||||
|
</StackLayout>
|
||||||
|
</Page>
|
@ -1,37 +1,29 @@
|
|||||||
import * as app from "tns-core-modules/application/application";
|
|
||||||
import * as frame from "tns-core-modules/ui/frame";
|
|
||||||
import * as helper from "../ui/helper";
|
import * as helper from "../ui/helper";
|
||||||
import * as TKUnit from "../TKUnit";
|
import * as TKUnit from "../TKUnit";
|
||||||
|
|
||||||
|
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 { Color } from "tns-core-modules/color";
|
||||||
import { parse } from "tns-core-modules/ui/builder";
|
import { isAndroid } from "tns-core-modules/platform";
|
||||||
|
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";
|
||||||
|
|
||||||
const appCssFileName = "./app/application.css";
|
const appCssFileName = "./app/application.css";
|
||||||
const appNewCssFileName = "./app/app-new.css";
|
const appNewCssFileName = "./app/app-new.css";
|
||||||
const appNewScssFileName = "./app/app-new.scss";
|
const appNewScssFileName = "./app/app-new.scss";
|
||||||
const appJsFileName = "./app/app.js";
|
const buttonCssFileName = "./app/button-page.css";
|
||||||
const appTsFileName = "./app/app.ts";
|
|
||||||
const mainPageCssFileName = "./app/main-page.css";
|
const buttonPageModuleName = "livesync/livesync-button-page";
|
||||||
const mainPageHtmlFileName = "./app/main-page.html";
|
const buttonHtmlPageFileName = "./livesync/livesync-button-page.html";
|
||||||
const mainPageXmlFileName = "./app/main-page.xml";
|
const buttonXmlPageFileName = "./livesync/livesync-button-page.xml";
|
||||||
|
const buttonJsPageFileName = "./livesync/livesync-button-page.js";
|
||||||
|
const buttonTsPageFileName = "./livesync/livesync-button-page.ts";
|
||||||
|
const labelPageModuleName = "livesync/livesync-label-page";
|
||||||
|
|
||||||
const black = new Color("black");
|
|
||||||
const green = new Color("green");
|
const green = new Color("green");
|
||||||
|
|
||||||
const mainPageTemplate = `
|
|
||||||
<Page>
|
|
||||||
<StackLayout>
|
|
||||||
<Label id="label" text="label"></Label>
|
|
||||||
</StackLayout>
|
|
||||||
</Page>`;
|
|
||||||
|
|
||||||
const pageTemplate = `
|
|
||||||
<Page>
|
|
||||||
<StackLayout>
|
|
||||||
<Button id="button" text="button"></Button>
|
|
||||||
</StackLayout>
|
|
||||||
</Page>`;
|
|
||||||
|
|
||||||
export function test_onLiveSync_ModuleContext_AppStyle_AppNewCss() {
|
export function test_onLiveSync_ModuleContext_AppStyle_AppNewCss() {
|
||||||
_test_onLiveSync_ModuleContext_AppStyle(appNewCssFileName);
|
_test_onLiveSync_ModuleContext_AppStyle(appNewCssFileName);
|
||||||
}
|
}
|
||||||
@ -48,29 +40,29 @@ export function test_onLiveSync_ModuleContext_ModuleUndefined() {
|
|||||||
_test_onLiveSync_ModuleContext({ type: "script", path: undefined });
|
_test_onLiveSync_ModuleContext({ type: "script", path: undefined });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function test_onLiveSync_ModuleContext_Script_AppJs() {
|
export function test_onLiveSync_ModuleContext_Script_JsFile() {
|
||||||
_test_onLiveSync_ModuleContext({ type: "script", path: appJsFileName });
|
_test_onLiveSync_ModuleReplace({ type: "script", path: buttonJsPageFileName });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function test_onLiveSync_ModuleContext_Script_AppTs() {
|
export function test_onLiveSync_ModuleContext_Script_TsFile() {
|
||||||
_test_onLiveSync_ModuleContext({ type: "script", path: appTsFileName });
|
_test_onLiveSync_ModuleReplace({ type: "script", path: buttonTsPageFileName });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function test_onLiveSync_ModuleContext_Style_MainPageCss() {
|
export function test_onLiveSync_ModuleContext_Style_CssFile() {
|
||||||
_test_onLiveSync_ModuleContext_TypeStyle({ type: "style", path: mainPageCssFileName });
|
_test_onLiveSync_ModuleContext_TypeStyle({ type: "style", path: buttonCssFileName });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function test_onLiveSync_ModuleContext_Markup_MainPageHtml() {
|
export function test_onLiveSync_ModuleContext_Markup_HtmlFile() {
|
||||||
_test_onLiveSync_ModuleContext({ type: "markup", path: mainPageHtmlFileName });
|
_test_onLiveSync_ModuleReplace({ type: "markup", path: buttonHtmlPageFileName });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function test_onLiveSync_ModuleContext_Markup_MainPageXml() {
|
export function test_onLiveSync_ModuleContext_Markup_XmlFile() {
|
||||||
_test_onLiveSync_ModuleContext({ type: "markup", path: mainPageXmlFileName });
|
_test_onLiveSync_ModuleReplace({ type: "markup", path: buttonXmlPageFileName });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setUpModule() {
|
export function setUp() {
|
||||||
const mainPage = <Page>parse(mainPageTemplate);
|
const labelPage = <Page>createViewFromEntry(({ moduleName: labelPageModuleName }));
|
||||||
helper.navigate(() => mainPage);
|
helper.navigate(() => labelPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function tearDown() {
|
export function tearDown() {
|
||||||
@ -79,32 +71,29 @@ export function tearDown() {
|
|||||||
|
|
||||||
function _test_onLiveSync_ModuleContext_AppStyle(styleFileName: string) {
|
function _test_onLiveSync_ModuleContext_AppStyle(styleFileName: string) {
|
||||||
const pageBeforeNavigation = helper.getCurrentPage();
|
const pageBeforeNavigation = helper.getCurrentPage();
|
||||||
|
const buttonPage = <Page>createViewFromEntry(({ moduleName: buttonPageModuleName }));
|
||||||
|
helper.navigateWithHistory(() => buttonPage);
|
||||||
|
|
||||||
const page = <Page>parse(pageTemplate);
|
|
||||||
helper.navigateWithHistory(() => page);
|
|
||||||
app.setCssFileName(styleFileName);
|
app.setCssFileName(styleFileName);
|
||||||
|
|
||||||
const pageBeforeLiveSync = helper.getCurrentPage();
|
const pageBeforeLiveSync = helper.getCurrentPage();
|
||||||
global.__onLiveSync({ type: "style", path: styleFileName });
|
global.__onLiveSync({ type: "style", path: styleFileName });
|
||||||
|
|
||||||
const pageAfterLiveSync = helper.getCurrentPage();
|
const pageAfterLiveSync = helper.getCurrentPage();
|
||||||
TKUnit.waitUntilReady(() => pageAfterLiveSync.getViewById("button").style.color.toString() === green.toString());
|
TKUnit.waitUntilReady(() => pageAfterLiveSync.getViewById("button").style.color.toString() === green.toString());
|
||||||
|
TKUnit.assertTrue(pageAfterLiveSync.frame.canGoBack(), "Can NOT go back!");
|
||||||
TKUnit.assertTrue(pageAfterLiveSync.frame.canGoBack(), "App styles NOT applied - livesync navigation executed!");
|
TKUnit.assertEqual(pageAfterLiveSync, pageBeforeLiveSync, "Pages are different!");
|
||||||
TKUnit.assertEqual(pageAfterLiveSync, pageBeforeLiveSync, "Pages are different - livesync navigation executed!");
|
TKUnit.assertTrue(pageAfterLiveSync._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version is NOT applied!");
|
||||||
TKUnit.assertTrue(pageAfterLiveSync._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version NOT applied!");
|
|
||||||
|
|
||||||
helper.goBack();
|
helper.goBack();
|
||||||
|
|
||||||
const pageAfterNavigationBack = helper.getCurrentPage();
|
const pageAfterNavigationBack = helper.getCurrentPage();
|
||||||
TKUnit.assertEqual(pageAfterNavigationBack.getViewById("label").style.color, green, "App styles NOT applied on back navigation!");
|
TKUnit.assertEqual(pageAfterNavigationBack.getViewById("label").style.color, green, "App styles NOT applied on back navigation!");
|
||||||
TKUnit.assertEqual(pageBeforeNavigation, pageAfterNavigationBack, "Pages are different - livesync navigation executed!");
|
TKUnit.assertEqual(pageBeforeNavigation, pageAfterNavigationBack, "Pages are different");
|
||||||
TKUnit.assertTrue(pageAfterNavigationBack._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version is NOT applied!");
|
TKUnit.assertTrue(pageAfterNavigationBack._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version is NOT applied!");
|
||||||
}
|
}
|
||||||
|
|
||||||
function _test_onLiveSync_ModuleContext(context: { type, path }) {
|
function _test_onLiveSync_ModuleContext(context: { type, path }) {
|
||||||
const page = <Page>parse(pageTemplate);
|
const buttonPage = <Page>createViewFromEntry(({ moduleName: buttonPageModuleName }));
|
||||||
helper.navigateWithHistory(() => page);
|
helper.navigateWithHistory(() => buttonPage);
|
||||||
global.__onLiveSync({ type: context.type, path: context.path });
|
global.__onLiveSync({ type: context.type, path: context.path });
|
||||||
|
|
||||||
TKUnit.waitUntilReady(() => !!frame.topmost());
|
TKUnit.waitUntilReady(() => !!frame.topmost());
|
||||||
@ -113,27 +102,53 @@ function _test_onLiveSync_ModuleContext(context: { type, path }) {
|
|||||||
TKUnit.assertTrue(topmostFrame.currentPage.getViewById("label").isLoaded);
|
TKUnit.assertTrue(topmostFrame.currentPage.getViewById("label").isLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _test_onLiveSync_ModuleReplace(context: { type, path }) {
|
||||||
|
const pageBeforeNavigation = helper.getCurrentPage();
|
||||||
|
const buttonPage = <Page>createViewFromEntry(({ moduleName: buttonPageModuleName }));
|
||||||
|
helper.navigateWithHistory(() => buttonPage);
|
||||||
|
|
||||||
|
global.__onLiveSync({ type: context.type, path: context.path });
|
||||||
|
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 page = <Page>parse(pageTemplate);
|
helper.navigateWithHistory(() => buttonPage);
|
||||||
helper.navigateWithHistory(() => page);
|
|
||||||
|
|
||||||
const pageBeforeLiveSync = helper.getCurrentPage();
|
const pageBeforeLiveSync = helper.getCurrentPage();
|
||||||
pageBeforeLiveSync._moduleName = "main-page";
|
pageBeforeLiveSync._moduleName = "button-page";
|
||||||
|
|
||||||
global.__onLiveSync({ type: context.type, path: context.path });
|
global.__onLiveSync({ type: context.type, path: context.path });
|
||||||
|
const topmostFrame = frame.topmost();
|
||||||
|
waitUntilLivesyncComplete(topmostFrame);
|
||||||
|
|
||||||
const pageAfterLiveSync = helper.getCurrentPage();
|
const pageAfterLiveSync = helper.getCurrentPage();
|
||||||
TKUnit.waitUntilReady(() => pageAfterLiveSync.getViewById("button").style.color.toString() === green.toString());
|
TKUnit.waitUntilReady(() => pageAfterLiveSync.getViewById("button").style.color.toString() === green.toString());
|
||||||
|
TKUnit.assertTrue(pageAfterLiveSync.frame.canGoBack(), "Can NOT go back!");
|
||||||
TKUnit.assertTrue(pageAfterLiveSync.frame.canGoBack(), "Local styles NOT applied - livesync navigation executed!");
|
TKUnit.assertEqual(topmostFrame.backStack.length, 1, "Backstack is clean!");
|
||||||
TKUnit.assertEqual(pageAfterLiveSync, pageBeforeLiveSync, "Pages are different - livesync navigation executed!");
|
TKUnit.assertTrue(pageAfterLiveSync._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version is NOT applied!");
|
||||||
TKUnit.assertTrue(pageAfterLiveSync._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version NOT applied!");
|
|
||||||
|
|
||||||
helper.goBack();
|
helper.goBack();
|
||||||
|
|
||||||
const pageAfterNavigationBack = helper.getCurrentPage();
|
const pageAfterNavigationBack = helper.getCurrentPage();
|
||||||
TKUnit.assertEqual(pageAfterNavigationBack.getViewById("label").style.color, black, "App styles applied on back navigation!");
|
TKUnit.assertEqual(pageBeforeNavigation, pageAfterNavigationBack, "Pages are different!");
|
||||||
TKUnit.assertEqual(pageBeforeNavigation, pageAfterNavigationBack, "Pages are different - livesync navigation executed!");
|
|
||||||
TKUnit.assertTrue(pageAfterNavigationBack._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version is NOT applied!");
|
TKUnit.assertTrue(pageAfterNavigationBack._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version is NOT applied!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function waitUntilLivesyncComplete(frame: Frame) {
|
||||||
|
if (isAndroid) {
|
||||||
|
TKUnit.waitUntilReady(() => frame._executingEntry === null);
|
||||||
|
} else {
|
||||||
|
TKUnit.waitUntilReady(() => frame.currentPage.isLoaded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
"repository": "<fill-your-repository-here>",
|
"repository": "<fill-your-repository-here>",
|
||||||
"nativescript": {
|
"nativescript": {
|
||||||
"id": "org.nativescript.UnitTestApp",
|
"id": "org.nativescript.UnitTestApp",
|
||||||
"tns-ios": {
|
|
||||||
"version": "5.2.0"
|
|
||||||
},
|
|
||||||
"tns-android": {
|
"tns-android": {
|
||||||
"version": "5.2.1"
|
"version": "5.3.1"
|
||||||
|
},
|
||||||
|
"tns-ios": {
|
||||||
|
"version": "5.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -83,22 +83,20 @@ export function livesync(rootView: View, context?: ModuleContext) {
|
|||||||
events.notify(<EventData>{ eventName: "livesync", object: app });
|
events.notify(<EventData>{ eventName: "livesync", object: app });
|
||||||
const liveSyncCore = global.__onLiveSyncCore;
|
const liveSyncCore = global.__onLiveSyncCore;
|
||||||
let reapplyAppStyles = false;
|
let reapplyAppStyles = false;
|
||||||
let reapplyLocalStyles = false;
|
|
||||||
|
|
||||||
|
// ModuleContext is available only for Hot Module Replacement
|
||||||
if (context && context.path) {
|
if (context && context.path) {
|
||||||
const extensions = ["css", "scss"];
|
const styleExtensions = ["css", "scss"];
|
||||||
const appStylesFullFileName = getCssFileName();
|
const appStylesFullFileName = getCssFileName();
|
||||||
const appStylesFileName = appStylesFullFileName.substring(0, appStylesFullFileName.lastIndexOf(".") + 1);
|
const appStylesFileName = appStylesFullFileName.substring(0, appStylesFullFileName.lastIndexOf(".") + 1);
|
||||||
reapplyAppStyles = extensions.some(ext => context.path === appStylesFileName.concat(ext));
|
reapplyAppStyles = styleExtensions.some(ext => context.path === appStylesFileName.concat(ext));
|
||||||
if (!reapplyAppStyles) {
|
|
||||||
reapplyLocalStyles = extensions.some(ext => context.path.endsWith(ext));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle application styles
|
||||||
if (reapplyAppStyles && rootView) {
|
if (reapplyAppStyles && rootView) {
|
||||||
rootView._onCssStateChange();
|
rootView._onCssStateChange();
|
||||||
} else if (liveSyncCore) {
|
} else if (liveSyncCore) {
|
||||||
reapplyLocalStyles ? liveSyncCore(context) : liveSyncCore();
|
liveSyncCore(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
notify, launchEvent, resumeEvent, suspendEvent, exitEvent, lowMemoryEvent,
|
notify, launchEvent, resumeEvent, suspendEvent, exitEvent, lowMemoryEvent,
|
||||||
orientationChangedEvent, setApplication, livesync, displayedEvent, getCssFileName
|
orientationChangedEvent, setApplication, livesync, displayedEvent, getCssFileName
|
||||||
} from "./application-common";
|
} from "./application-common";
|
||||||
|
import { ModuleType } from "../ui/core/view/view-common";
|
||||||
|
|
||||||
// First reexport so that app module is initialized.
|
// First reexport so that app module is initialized.
|
||||||
export * from "./application-common";
|
export * from "./application-common";
|
||||||
@ -106,6 +107,7 @@ class IOSApplication implements IOSApplicationDefinition {
|
|||||||
get delegate(): typeof UIApplicationDelegate {
|
get delegate(): typeof UIApplicationDelegate {
|
||||||
return this._delegate;
|
return this._delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
set delegate(value: typeof UIApplicationDelegate) {
|
set delegate(value: typeof UIApplicationDelegate) {
|
||||||
if (this._delegate !== value) {
|
if (this._delegate !== value) {
|
||||||
this._delegate = value;
|
this._delegate = value;
|
||||||
@ -228,8 +230,16 @@ class IOSApplication implements IOSApplicationDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _onLivesync(context?: ModuleContext): void {
|
public _onLivesync(context?: ModuleContext): void {
|
||||||
// If view can't handle livesync set window controller.
|
// Handle application root module
|
||||||
if (this._rootView && !this._rootView._onLivesync(context)) {
|
const isAppRootModuleChanged = context && context.path && context.path.includes(getMainEntry().moduleName) && context.type !== ModuleType.style;
|
||||||
|
|
||||||
|
// Set window content when:
|
||||||
|
// + Application root module is changed
|
||||||
|
// + View did not handle the change
|
||||||
|
// Note:
|
||||||
|
// The case when neither app root module is changed, nor livesync is handled on View,
|
||||||
|
// then changes will not apply until navigate forward to the module.
|
||||||
|
if (isAppRootModuleChanged || (this._rootView && !this._rootView._onLivesync(context))) {
|
||||||
this.setWindowContent();
|
this.setWindowContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,7 +268,6 @@ class IOSApplication implements IOSApplicationDefinition {
|
|||||||
this._window.makeKeyAndVisible();
|
this._window.makeKeyAndVisible();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const iosApp = new IOSApplication();
|
const iosApp = new IOSApplication();
|
||||||
|
11
tns-core-modules/trace/trace.d.ts
vendored
11
tns-core-modules/trace/trace.d.ts
vendored
@ -100,10 +100,11 @@ export module categories {
|
|||||||
export const Error: string;
|
export const Error: string;
|
||||||
export const Animation: string;
|
export const Animation: string;
|
||||||
export const Transition: string;
|
export const Transition: string;
|
||||||
|
export const Livesync: string;
|
||||||
export const All: string;
|
|
||||||
|
|
||||||
export const separator: string;
|
export const separator: string;
|
||||||
|
export const All: string;
|
||||||
|
|
||||||
export function concat(...categories: string[]): string;
|
export function concat(...categories: string[]): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +126,7 @@ export interface TraceWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface used to trace information about specific event.
|
* An interface used to trace information about specific event.
|
||||||
*/
|
*/
|
||||||
export interface EventListener {
|
export interface EventListener {
|
||||||
filter: string;
|
filter: string;
|
||||||
@ -133,7 +134,7 @@ export interface EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface used to for handling trace error
|
* An interface used to for handling trace error
|
||||||
*/
|
*/
|
||||||
export interface ErrorHandler {
|
export interface ErrorHandler {
|
||||||
handlerError(error: Error);
|
handlerError(error: Error);
|
||||||
@ -141,4 +142,4 @@ export interface ErrorHandler {
|
|||||||
|
|
||||||
export class DefaultErrorHandler implements ErrorHandler {
|
export class DefaultErrorHandler implements ErrorHandler {
|
||||||
handlerError(error);
|
handlerError(error);
|
||||||
}
|
}
|
||||||
|
@ -129,9 +129,22 @@ export module categories {
|
|||||||
export const Error = "Error";
|
export const Error = "Error";
|
||||||
export const Animation = "Animation";
|
export const Animation = "Animation";
|
||||||
export const Transition = "Transition";
|
export const Transition = "Transition";
|
||||||
export const All = VisualTreeEvents + "," + Layout + "," + Style + "," + ViewHierarchy + "," + NativeLifecycle + "," + Debug + "," + Navigation + "," + Test + "," + Binding + "," + Error + "," + Animation + "," + Transition;
|
export const Livesync = "Livesync";
|
||||||
|
|
||||||
export const separator = ",";
|
export const separator = ",";
|
||||||
|
export const All = VisualTreeEvents + separator
|
||||||
|
+ Layout + separator
|
||||||
|
+ Style + separator
|
||||||
|
+ ViewHierarchy + separator
|
||||||
|
+ NativeLifecycle + separator
|
||||||
|
+ Debug + separator
|
||||||
|
+ Navigation + separator
|
||||||
|
+ Test + separator
|
||||||
|
+ Binding + separator
|
||||||
|
+ Error + separator
|
||||||
|
+ Animation + separator
|
||||||
|
+ Transition + separator
|
||||||
|
+ Livesync;
|
||||||
|
|
||||||
export function concat(): string {
|
export function concat(): string {
|
||||||
let result: string;
|
let result: string;
|
||||||
|
@ -160,7 +160,7 @@ export abstract class ViewBase extends Observable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated use showModal with ShowModalOptions instead
|
* @deprecated use showModal with ShowModalOptions instead
|
||||||
*
|
*
|
||||||
* Shows the View contained in moduleName as a modal view.
|
* 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 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.
|
* @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
|
* @deprecated use showModal with ShowModalOptions instead
|
||||||
*
|
*
|
||||||
* Shows the view passed as parameter as a modal view.
|
* Shows the view passed as parameter as a modal view.
|
||||||
* @param view - View instance to be shown modally.
|
* @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.
|
* @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;
|
public _goToVisualState(state: string): void;
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
*
|
*
|
||||||
* This used to be the way to set attribute values in early {N} versions.
|
* 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.
|
* Now attributes are expected to be set as plain properties on the view instances.
|
||||||
*/
|
*/
|
||||||
|
@ -7,8 +7,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ViewBase, Property, booleanConverter, eachDescendant, EventData, layout,
|
ViewBase, Property, booleanConverter, eachDescendant, EventData, layout,
|
||||||
getEventOrGestureName, traceEnabled, traceWrite, traceCategories,
|
getEventOrGestureName, traceEnabled, traceWrite, traceCategories,
|
||||||
InheritedProperty,
|
InheritedProperty, ShowModalOptions
|
||||||
ShowModalOptions
|
|
||||||
} from "../view-base";
|
} from "../view-base";
|
||||||
|
|
||||||
import { HorizontalAlignment, VerticalAlignment, Visibility, Length, PercentLength } from "../../styling/style-properties";
|
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 {
|
export function CSSType(type: string): ClassDecorator {
|
||||||
return (cls) => {
|
return (cls) => {
|
||||||
cls.prototype.cssType = type;
|
cls.prototype.cssType = type;
|
||||||
@ -138,12 +143,22 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _onLivesync(context?: ModuleContext): boolean {
|
public _onLivesync(context?: ModuleContext): boolean {
|
||||||
|
if (traceEnabled()) {
|
||||||
|
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
|
||||||
|
}
|
||||||
|
|
||||||
_rootModalViews.forEach(v => v.closeModal());
|
_rootModalViews.forEach(v => v.closeModal());
|
||||||
_rootModalViews.length = 0;
|
_rootModalViews.length = 0;
|
||||||
|
|
||||||
// Currently, we pass `context` only for style modules
|
if (context && context.type && context.path) {
|
||||||
if (context && context.path) {
|
// Handle local styles
|
||||||
return this.changeLocalStyles(context.path);
|
if (context.type === ModuleType.style) {
|
||||||
|
return this.changeLocalStyles(context.path);
|
||||||
|
}
|
||||||
|
// Handle module markup and script changes
|
||||||
|
else {
|
||||||
|
return this.changeModule(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -156,11 +171,16 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Do not execute frame navigation for a change in styles
|
|
||||||
|
// Do not reset activity/window content for local styles changes
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private changeStyles(view: ViewBase, contextPath: string): boolean {
|
private changeStyles(view: ViewBase, contextPath: string): boolean {
|
||||||
|
if (traceEnabled()) {
|
||||||
|
traceWrite(`${view}.${view._moduleName}`, traceCategories.Livesync);
|
||||||
|
}
|
||||||
|
|
||||||
if (view._moduleName && contextPath.includes(view._moduleName)) {
|
if (view._moduleName && contextPath.includes(view._moduleName)) {
|
||||||
(<this>view).changeCssFile(contextPath);
|
(<this>view).changeCssFile(contextPath);
|
||||||
return true;
|
return true;
|
||||||
@ -168,6 +188,23 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
|||||||
return false;
|
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 {
|
_setupAsRootView(context: any): void {
|
||||||
super._setupAsRootView(context);
|
super._setupAsRootView(context);
|
||||||
if (!this._styleScope) {
|
if (!this._styleScope) {
|
||||||
|
@ -232,22 +232,22 @@ export function _getAnimatedEntries(frameId: number): Set<BackstackEntry> {
|
|||||||
export function _updateTransitions(entry: ExpandedEntry): void {
|
export function _updateTransitions(entry: ExpandedEntry): void {
|
||||||
const fragment = entry.fragment;
|
const fragment = entry.fragment;
|
||||||
const enterTransitionListener = entry.enterTransitionListener;
|
const enterTransitionListener = entry.enterTransitionListener;
|
||||||
if (enterTransitionListener) {
|
if (enterTransitionListener && fragment) {
|
||||||
fragment.setEnterTransition(enterTransitionListener.transition);
|
fragment.setEnterTransition(enterTransitionListener.transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
const exitTransitionListener = entry.exitTransitionListener;
|
const exitTransitionListener = entry.exitTransitionListener;
|
||||||
if (exitTransitionListener) {
|
if (exitTransitionListener && fragment) {
|
||||||
fragment.setExitTransition(exitTransitionListener.transition);
|
fragment.setExitTransition(exitTransitionListener.transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
const reenterTransitionListener = entry.reenterTransitionListener;
|
const reenterTransitionListener = entry.reenterTransitionListener;
|
||||||
if (reenterTransitionListener) {
|
if (reenterTransitionListener && fragment) {
|
||||||
fragment.setReenterTransition(reenterTransitionListener.transition);
|
fragment.setReenterTransition(reenterTransitionListener.transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
const returnTransitionListener = entry.returnTransitionListener;
|
const returnTransitionListener = entry.returnTransitionListener;
|
||||||
if (returnTransitionListener) {
|
if (returnTransitionListener && fragment) {
|
||||||
fragment.setReturnTransition(returnTransitionListener.transition);
|
fragment.setReturnTransition(returnTransitionListener.transition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,7 +374,7 @@ function getAnimationListener(): android.animation.Animator.AnimatorListener {
|
|||||||
|
|
||||||
return AnimationListener;
|
return AnimationListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToWaitingQueue(entry: ExpandedEntry): void {
|
function addToWaitingQueue(entry: ExpandedEntry): void {
|
||||||
const frameId = entry.frameId;
|
const frameId = entry.frameId;
|
||||||
let entries = waitingQueue.get(frameId);
|
let entries = waitingQueue.get(frameId);
|
||||||
@ -659,7 +659,7 @@ function setupAllAnimation(entry: ExpandedEntry, transition: Transition): void {
|
|||||||
setupExitAndPopEnterAnimation(entry, transition);
|
setupExitAndPopEnterAnimation(entry, transition);
|
||||||
const listener = getAnimationListener();
|
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.
|
// need to clearAnimationListener for enter & popExit animators.
|
||||||
const enterAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.enter);
|
const enterAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.enter);
|
||||||
enterAnimator.transitionType = AndroidTransitionType.enter;
|
enterAnimator.transitionType = AndroidTransitionType.enter;
|
||||||
@ -720,7 +720,7 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry): void {
|
|||||||
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);
|
||||||
completedEntries.delete(frameId);
|
completedEntries.delete(frameId);
|
||||||
waitingQueue.delete(frameId);
|
waitingQueue.delete(frameId);
|
||||||
@ -730,8 +730,8 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry): void {
|
|||||||
// 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) {
|
||||||
const isBack = frame._isBack;
|
const navType = frame.navigationType;
|
||||||
setTimeout(() => frame.setCurrent(current, isBack));
|
setTimeout(() => frame.setCurrent(current, navType));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
completedEntries.set(frameId, entry);
|
completedEntries.set(frameId, entry);
|
||||||
|
@ -11,6 +11,12 @@ import { profile } from "../../profiling";
|
|||||||
import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _popFromFrameStack, _removeFromFrameStack } from "./frame-stack";
|
import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _popFromFrameStack, _removeFromFrameStack } from "./frame-stack";
|
||||||
export * from "../core/view";
|
export * from "../core/view";
|
||||||
|
|
||||||
|
export enum NavigationType {
|
||||||
|
back,
|
||||||
|
forward,
|
||||||
|
replace
|
||||||
|
}
|
||||||
|
|
||||||
function buildEntryFromArgs(arg: any): NavigationEntry {
|
function buildEntryFromArgs(arg: any): NavigationEntry {
|
||||||
let entry: NavigationEntry;
|
let entry: NavigationEntry;
|
||||||
if (typeof arg === "string") {
|
if (typeof arg === "string") {
|
||||||
@ -48,6 +54,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
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.
|
||||||
|
|
||||||
@ -206,7 +213,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
return this._currentEntry === entry;
|
return this._currentEntry === entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setCurrent(entry: BackstackEntry, isBack: boolean): void {
|
public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void {
|
||||||
const newPage = entry.resolvedPage;
|
const newPage = entry.resolvedPage;
|
||||||
// In case we navigated forward to a page that was in the backstack
|
// In case we navigated forward to a page that was in the backstack
|
||||||
// with clearHistory: true
|
// with clearHistory: true
|
||||||
@ -217,6 +224,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
|
|
||||||
this._currentEntry = entry;
|
this._currentEntry = entry;
|
||||||
|
|
||||||
|
const isBack = navigationType === NavigationType.back;
|
||||||
if (isBack) {
|
if (isBack) {
|
||||||
this._pushInFrameStack();
|
this._pushInFrameStack();
|
||||||
}
|
}
|
||||||
@ -229,15 +237,18 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
this._executingEntry = null;
|
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);
|
this.raiseCurrentPageNavigatedEvents(isBack);
|
||||||
const current = this._currentEntry;
|
const current = this._currentEntry;
|
||||||
|
|
||||||
|
// Do nothing for Hot Module Replacement
|
||||||
if (isBack) {
|
if (isBack) {
|
||||||
const index = this._backStack.indexOf(entry);
|
const index = this._backStack.indexOf(entry);
|
||||||
this._backStack.splice(index + 1).forEach(e => this._removeEntry(e));
|
this._backStack.splice(index + 1).forEach(e => this._removeEntry(e));
|
||||||
this._backStack.pop();
|
this._backStack.pop();
|
||||||
} else {
|
} else if (!isReplace) {
|
||||||
if (entry.entry.clearHistory) {
|
if (entry.entry.clearHistory) {
|
||||||
this._backStack.forEach(e => this._removeEntry(e));
|
this._backStack.forEach(e => this._removeEntry(e));
|
||||||
this._backStack.length = 0;
|
this._backStack.length = 0;
|
||||||
@ -345,7 +356,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
private performNavigation(navigationContext: NavigationContext) {
|
public performNavigation(navigationContext: NavigationContext) {
|
||||||
const navContext = navigationContext.entry;
|
const navContext = navigationContext.entry;
|
||||||
this._executingEntry = navContext;
|
this._executingEntry = navContext;
|
||||||
this._onNavigatingTo(navContext, navigationContext.isBackNavigation);
|
this._onNavigatingTo(navContext, navigationContext.isBackNavigation);
|
||||||
@ -563,35 +574,39 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public _onLivesync(context?: ModuleContext): boolean {
|
public _onLivesync(): boolean {
|
||||||
// Execute a navigation if not handled on `View` level
|
// Reset activity/window content when:
|
||||||
if (!super._onLivesync(context)) {
|
// + Changes are not handled on View
|
||||||
if (!this._currentEntry || !this._currentEntry.entry) {
|
// + 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;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
// Definitions.
|
// Definitions.
|
||||||
import {
|
import {
|
||||||
AndroidFrame as AndroidFrameDefinition, BackstackEntry,
|
AndroidFrame as AndroidFrameDefinition, AndroidActivityCallbacks,
|
||||||
NavigationTransition, AndroidFragmentCallbacks, AndroidActivityCallbacks
|
AndroidFragmentCallbacks, BackstackEntry, NavigationTransition
|
||||||
} from ".";
|
} from ".";
|
||||||
|
import { ModuleType } from "../../ui/core/view/view-common";
|
||||||
import { Page } from "../page";
|
import { Page } from "../page";
|
||||||
|
|
||||||
// Types.
|
// Types.
|
||||||
import * as application from "../../application";
|
import * as application from "../../application";
|
||||||
import {
|
import {
|
||||||
FrameBase, stack, goBack, View, Observable,
|
FrameBase, goBack, stack, NavigationContext, NavigationType,
|
||||||
traceEnabled, traceWrite, traceCategories, traceError
|
Observable, View, traceCategories, traceEnabled, traceError, traceWrite
|
||||||
} from "./frame-common";
|
} from "./frame-common";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -21,6 +22,7 @@ import { profile } from "../../profiling";
|
|||||||
|
|
||||||
// TODO: Remove this and get it from global to decouple builder for angular
|
// TODO: Remove this and get it from global to decouple builder for angular
|
||||||
import { createViewFromEntry } from "../builder";
|
import { createViewFromEntry } from "../builder";
|
||||||
|
import { getModuleName } from "../../utils/utils";
|
||||||
|
|
||||||
export * from "./frame-common";
|
export * from "./frame-common";
|
||||||
|
|
||||||
@ -87,8 +89,16 @@ export function reloadPage(context?: ModuleContext): void {
|
|||||||
const callbacks: AndroidActivityCallbacks = activity[CALLBACKS];
|
const callbacks: AndroidActivityCallbacks = activity[CALLBACKS];
|
||||||
if (callbacks) {
|
if (callbacks) {
|
||||||
const rootView: View = callbacks.getRootView();
|
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);
|
callbacks.resetActivityContent(activity);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -104,7 +114,6 @@ export class Frame extends FrameBase {
|
|||||||
private _containerViewId: number = -1;
|
private _containerViewId: number = -1;
|
||||||
private _tearDownPending = false;
|
private _tearDownPending = false;
|
||||||
private _attachedToWindow = false;
|
private _attachedToWindow = false;
|
||||||
public _isBack: boolean = true;
|
|
||||||
private _cachedAnimatorState: AnimatorState;
|
private _cachedAnimatorState: AnimatorState;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -263,11 +272,11 @@ export class Frame extends FrameBase {
|
|||||||
return newFragment;
|
return newFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setCurrent(entry: BackstackEntry, isBack: boolean): void {
|
public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void {
|
||||||
const current = this._currentEntry;
|
const current = this._currentEntry;
|
||||||
const currentEntryChanged = current !== entry;
|
const currentEntryChanged = current !== entry;
|
||||||
if (currentEntryChanged) {
|
if (currentEntryChanged) {
|
||||||
this._updateBackstack(entry, isBack);
|
this._updateBackstack(entry, navigationType);
|
||||||
|
|
||||||
// If activity was destroyed we need to destroy fragment and UI
|
// If activity was destroyed we need to destroy fragment and UI
|
||||||
// of current and new entries.
|
// 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.
|
// If we had real navigation process queue.
|
||||||
this._processNavigationQueue(entry.resolvedPage);
|
this._processNavigationQueue(entry.resolvedPage);
|
||||||
@ -330,10 +339,48 @@ export class Frame extends FrameBase {
|
|||||||
return false;
|
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
|
@profile
|
||||||
public _navigateCore(newEntry: BackstackEntry) {
|
public _navigateCore(newEntry: BackstackEntry) {
|
||||||
super._navigateCore(newEntry);
|
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
|
// set frameId here so that we could use it in fragment.transitions
|
||||||
newEntry.frameId = this._android.frameId;
|
newEntry.frameId = this._android.frameId;
|
||||||
@ -360,7 +407,10 @@ export class Frame extends FrameBase {
|
|||||||
navDepth = -1;
|
navDepth = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
navDepth++;
|
if (!isReplace) {
|
||||||
|
navDepth++;
|
||||||
|
}
|
||||||
|
|
||||||
fragmentId++;
|
fragmentId++;
|
||||||
const newFragmentTag = `fragment${fragmentId}[${navDepth}]`;
|
const newFragmentTag = `fragment${fragmentId}[${navDepth}]`;
|
||||||
const newFragment = this.createFragment(newEntry, newFragmentTag);
|
const newFragment = this.createFragment(newEntry, newFragmentTag);
|
||||||
@ -383,7 +433,7 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _goBackCore(backstackEntry: BackstackEntry) {
|
public _goBackCore(backstackEntry: BackstackEntry) {
|
||||||
this._isBack = true;
|
this.navigationType = NavigationType.back;
|
||||||
super._goBackCore(backstackEntry);
|
super._goBackCore(backstackEntry);
|
||||||
navDepth = backstackEntry.navDepth;
|
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 {
|
export function setFragmentCallbacks(fragment: android.support.v4.app.Fragment): void {
|
||||||
fragment[CALLBACKS] = new FragmentCallbacksImplementation();
|
fragment[CALLBACKS] = new FragmentCallbacksImplementation();
|
||||||
}
|
}
|
||||||
|
28
tns-core-modules/ui/frame/frame.d.ts
vendored
28
tns-core-modules/ui/frame/frame.d.ts
vendored
@ -3,14 +3,14 @@
|
|||||||
* @module "ui/frame"
|
* @module "ui/frame"
|
||||||
*/ /** */
|
*/ /** */
|
||||||
|
|
||||||
|
import { NavigationType } from "./frame-common";
|
||||||
import { Page, View, Observable, EventData } from "../page";
|
import { Page, View, Observable, EventData } from "../page";
|
||||||
import { Transition } from "../transition";
|
import { Transition } from "../transition";
|
||||||
|
|
||||||
export * from "../page";
|
export * from "../page";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the logical View unit that is responsible for navigation withing an application.
|
* Represents the logical View unit that is responsible for navigation within an application.
|
||||||
* Typically an application will have a Frame object at a root level.
|
|
||||||
* Nested frames are supported, enabling hierarchical navigation scenarios.
|
* Nested frames are supported, enabling hierarchical navigation scenarios.
|
||||||
*/
|
*/
|
||||||
export class Frame extends View {
|
export class Frame extends View {
|
||||||
@ -113,12 +113,13 @@ export class Frame extends View {
|
|||||||
* @param entry to check
|
* @param entry to check
|
||||||
*/
|
*/
|
||||||
isCurrent(entry: BackstackEntry): boolean;
|
isCurrent(entry: BackstackEntry): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param entry to set as current
|
* @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
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -143,6 +144,11 @@ export class Frame extends View {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_updateActionBar(page?: Page, disableNavBarAnimation?: boolean);
|
_updateActionBar(page?: Page, disableNavBarAnimation?: boolean);
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param navigationContext
|
||||||
|
*/
|
||||||
|
public performNavigation(navigationContext: NavigationContext): void;
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -154,7 +160,7 @@ export class Frame extends View {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_updateBackstack(entry: BackstackEntry, isBack: boolean): void;
|
_updateBackstack(entry: BackstackEntry, navigationType: NavigationType): void;
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -167,10 +173,12 @@ export class Frame extends View {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_removeFromFrameStack();
|
_removeFromFrameStack();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
|
* Represents the type of navigation.
|
||||||
*/
|
*/
|
||||||
_isBack?: boolean;
|
navigationType: NavigationType;
|
||||||
//@endprivate
|
//@endprivate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -275,6 +283,14 @@ export interface NavigationEntry extends ViewEntry {
|
|||||||
clearHistory?: boolean;
|
clearHistory?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a context passed to navigation methods.
|
||||||
|
*/
|
||||||
|
export interface NavigationContext {
|
||||||
|
entry: BackstackEntry;
|
||||||
|
isBackNavigation: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an object specifying a page navigation transition.
|
* Represents an object specifying a page navigation transition.
|
||||||
*/
|
*/
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
// Definitions.
|
// Definitions.
|
||||||
import { iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition } from ".";
|
import {
|
||||||
|
iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition
|
||||||
|
} from ".";
|
||||||
import { Page } from "../page";
|
import { Page } from "../page";
|
||||||
import { profile } from "../../profiling";
|
import { profile } from "../../profiling";
|
||||||
|
|
||||||
//Types.
|
//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 { _createIOSAnimatedTransitioning } from "./fragment.transitions";
|
||||||
|
|
||||||
|
import { createViewFromEntry } from "../builder";
|
||||||
import * as utils from "../../utils/utils";
|
import * as utils from "../../utils/utils";
|
||||||
|
|
||||||
export * from "./frame-common";
|
export * from "./frame-common";
|
||||||
@ -14,9 +20,10 @@ export * from "./frame-common";
|
|||||||
const majorVersion = utils.ios.MajorVersion;
|
const majorVersion = utils.ios.MajorVersion;
|
||||||
|
|
||||||
const ENTRY = "_entry";
|
const ENTRY = "_entry";
|
||||||
|
const DELEGATE = "_delegate";
|
||||||
const NAV_DEPTH = "_navDepth";
|
const NAV_DEPTH = "_navDepth";
|
||||||
const TRANSITION = "_transition";
|
const TRANSITION = "_transition";
|
||||||
const DELEGATE = "_delegate";
|
const NON_ANIMATED_TRANSITION = "non-animated";
|
||||||
|
|
||||||
let navDepth = -1;
|
let navDepth = -1;
|
||||||
|
|
||||||
@ -46,18 +53,57 @@ export class Frame extends FrameBase {
|
|||||||
return this._ios;
|
return this._ios;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setCurrent(entry: BackstackEntry, isBack: boolean): void {
|
public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void {
|
||||||
const current = this._currentEntry;
|
const current = this._currentEntry;
|
||||||
const currentEntryChanged = current !== entry;
|
const currentEntryChanged = current !== entry;
|
||||||
if (currentEntryChanged) {
|
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
|
@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;
|
||||||
@ -69,7 +115,9 @@ export class Frame extends FrameBase {
|
|||||||
if (clearHistory) {
|
if (clearHistory) {
|
||||||
navDepth = -1;
|
navDepth = -1;
|
||||||
}
|
}
|
||||||
navDepth++;
|
if (!isReplace) {
|
||||||
|
navDepth++;
|
||||||
|
}
|
||||||
|
|
||||||
let navigationTransition: NavigationTransition;
|
let navigationTransition: NavigationTransition;
|
||||||
let animated = this.currentPage ? this._getIsAnimatedNavigation(backstackEntry.entry) : false;
|
let animated = this.currentPage ? this._getIsAnimatedNavigation(backstackEntry.entry) : false;
|
||||||
@ -81,7 +129,7 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//https://github.com/NativeScript/NativeScript/issues/1787
|
//https://github.com/NativeScript/NativeScript/issues/1787
|
||||||
viewController[TRANSITION] = { name: "non-animated" };
|
viewController[TRANSITION] = { name: NON_ANIMATED_TRANSITION };
|
||||||
}
|
}
|
||||||
|
|
||||||
let nativeTransition = _getNativeTransition(navigationTransition, true);
|
let nativeTransition = _getNativeTransition(navigationTransition, true);
|
||||||
@ -136,7 +184,8 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We should hide the current entry from the back stack.
|
// 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);
|
let newControllers = NSMutableArray.alloc<UIViewController>().initWithArray(this._ios.controller.viewControllers);
|
||||||
if (newControllers.count === 0) {
|
if (newControllers.count === 0) {
|
||||||
throw new Error("Wrong controllers count.");
|
throw new Error("Wrong controllers count.");
|
||||||
@ -168,6 +217,7 @@ 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];
|
||||||
|
|
||||||
@ -469,7 +519,7 @@ class UINavigationControllerImpl extends UINavigationController {
|
|||||||
traceWrite(`UINavigationControllerImpl.popViewControllerAnimated(${animated}); transition: ${JSON.stringify(navigationTransition)}`, traceCategories.NativeLifecycle);
|
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
|
//https://github.com/NativeScript/NativeScript/issues/1787
|
||||||
return super.popViewControllerAnimated(false);
|
return super.popViewControllerAnimated(false);
|
||||||
}
|
}
|
||||||
@ -493,7 +543,7 @@ class UINavigationControllerImpl extends UINavigationController {
|
|||||||
traceWrite(`UINavigationControllerImpl.popToViewControllerAnimated(${viewController}, ${animated}); transition: ${JSON.stringify(navigationTransition)}`, traceCategories.NativeLifecycle);
|
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
|
//https://github.com/NativeScript/NativeScript/issues/1787
|
||||||
return super.popToViewControllerAnimated(viewController, false);
|
return super.popToViewControllerAnimated(viewController, false);
|
||||||
}
|
}
|
||||||
|
@ -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
|
@profile
|
||||||
public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) {
|
public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) {
|
||||||
this._navigationContext = context;
|
this._navigationContext = context;
|
||||||
@ -190,4 +194,4 @@ export const androidStatusBarBackgroundProperty = new CssProperty<Style, Color>(
|
|||||||
name: "androidStatusBarBackground", cssName: "android-status-bar-background",
|
name: "androidStatusBarBackground", cssName: "android-status-bar-background",
|
||||||
equalityComparer: Color.equals, valueConverter: (v) => new Color(v)
|
equalityComparer: Color.equals, valueConverter: (v) => new Color(v)
|
||||||
});
|
});
|
||||||
androidStatusBarBackgroundProperty.register(Style);
|
androidStatusBarBackgroundProperty.register(Style);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// Definitions.
|
// Definitions.
|
||||||
import { Frame } from "../frame";
|
import { Frame } from "../frame";
|
||||||
|
import { NavigationType } from "../frame/frame-common";
|
||||||
|
|
||||||
// Types.
|
// Types.
|
||||||
import { ios as iosView } from "../core/view";
|
import { ios as iosView } from "../core/view";
|
||||||
import {
|
import {
|
||||||
PageBase, View, layout,
|
PageBase, View, layout, actionBarHiddenProperty, statusBarStyleProperty, Color
|
||||||
actionBarHiddenProperty, statusBarStyleProperty, Color
|
|
||||||
} from "./page-common";
|
} from "./page-common";
|
||||||
|
|
||||||
import { profile } from "../../profiling";
|
import { profile } from "../../profiling";
|
||||||
@ -20,7 +20,7 @@ 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) {
|
if (!frame || frame.navigationType === NavigationType.replace) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,14 +133,20 @@ class UIViewControllerImpl extends UIViewController {
|
|||||||
const newEntry = this[ENTRY];
|
const newEntry = this[ENTRY];
|
||||||
|
|
||||||
let isBack: boolean;
|
let isBack: boolean;
|
||||||
|
let navType = frame.navigationType;
|
||||||
// We are on the current page which happens when navigation is canceled so isBack should be false.
|
// 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;
|
isBack = false;
|
||||||
|
navType = NavigationType.forward;
|
||||||
} else {
|
} else {
|
||||||
isBack = isBackNavigationTo(owner, newEntry);
|
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.
|
// 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];
|
||||||
@ -182,7 +188,7 @@ class UIViewControllerImpl extends UIViewController {
|
|||||||
|
|
||||||
const frame = owner.frame;
|
const frame = owner.frame;
|
||||||
// Skip navigation events if we are hiding because we are about to show a modal page,
|
// 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.
|
// or because we are in tab and another controller is selected.
|
||||||
const tab = this.tabBarController;
|
const tab = this.tabBarController;
|
||||||
if (owner.onNavigatingFrom && !owner._presentedViewController && !this.presentingViewController && frame && frame.currentPage === owner) {
|
if (owner.onNavigatingFrom && !owner._presentedViewController && !this.presentingViewController && frame && frame.currentPage === owner) {
|
||||||
|
@ -28,6 +28,11 @@ export function convertString(value: any): any {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getModuleName(path: string): string {
|
||||||
|
let moduleName = path.replace("./", "");
|
||||||
|
return moduleName.substring(0, moduleName.lastIndexOf("."));
|
||||||
|
}
|
||||||
|
|
||||||
export module layout {
|
export module layout {
|
||||||
const MODE_SHIFT = 30;
|
const MODE_SHIFT = 30;
|
||||||
const MODE_MASK = 0x3 << MODE_SHIFT;
|
const MODE_MASK = 0x3 << MODE_SHIFT;
|
||||||
@ -148,4 +153,4 @@ export function hasDuplicates(arr: Array<any>): boolean {
|
|||||||
|
|
||||||
export function eliminateDuplicates(arr: Array<any>): Array<any> {
|
export function eliminateDuplicates(arr: Array<any>): Array<any> {
|
||||||
return Array.from(new Set(arr));
|
return Array.from(new Set(arr));
|
||||||
}
|
}
|
||||||
|
12
tns-core-modules/utils/utils.d.ts
vendored
12
tns-core-modules/utils/utils.d.ts
vendored
@ -185,13 +185,13 @@ export module ad {
|
|||||||
export module ios {
|
export module ios {
|
||||||
/**
|
/**
|
||||||
* @deprecated use the respective native property directly
|
* @deprecated use the respective native property directly
|
||||||
*
|
*
|
||||||
* Checks if the property is a function and if it is, calls it on this.
|
* Checks if the property is a function and if it is, calls it on this.
|
||||||
* Designed to support backward compatibility for methods that became properties.
|
* Designed to support backward compatibility for methods that became properties.
|
||||||
* Will not work on delegates since it checks if the propertyValue is a function, and delegates are marshalled as functions.
|
* Will not work on delegates since it checks if the propertyValue is a function, and delegates are marshalled as functions.
|
||||||
* Example: getter(NSRunLoop, NSRunLoop.currentRunLoop).runUntilDate(NSDate.dateWithTimeIntervalSinceNow(waitTime));
|
* Example: getter(NSRunLoop, NSRunLoop.currentRunLoop).runUntilDate(NSDate.dateWithTimeIntervalSinceNow(waitTime));
|
||||||
*/
|
*/
|
||||||
export function getter<T>(_this: any, propertyValue: T | {(): T}): T;
|
export function getter<T>(_this: any, propertyValue: T | { (): T }): T;
|
||||||
|
|
||||||
// Common properties between UILabel, UITextView and UITextField
|
// Common properties between UILabel, UITextView and UITextField
|
||||||
export interface TextUIView {
|
export interface TextUIView {
|
||||||
@ -255,7 +255,7 @@ export module ios {
|
|||||||
* @param rootViewController The root UIViewController instance to start searching from (normally window.rootViewController).
|
* @param rootViewController The root UIViewController instance to start searching from (normally window.rootViewController).
|
||||||
* Returns the visible UIViewController.
|
* Returns the visible UIViewController.
|
||||||
*/
|
*/
|
||||||
export function getVisibleViewController(rootViewController: any/* UIViewController*/ ): any/* UIViewController*/;
|
export function getVisibleViewController(rootViewController: any/* UIViewController*/): any/* UIViewController*/;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -305,6 +305,12 @@ export function escapeRegexSymbols(source: string): string
|
|||||||
*/
|
*/
|
||||||
export function convertString(value: any): any
|
export function convertString(value: any): any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets module name from path.
|
||||||
|
* @param path The module path.
|
||||||
|
*/
|
||||||
|
export function getModuleName(path: string): string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts an array by using merge sort algorithm (which ensures stable sort since the built-in Array.sort() does not promise a stable sort).
|
* Sorts an array by using merge sort algorithm (which ensures stable sort since the built-in Array.sort() does not promise a stable sort).
|
||||||
* @param arr - array to be sorted
|
* @param arr - array to be sorted
|
||||||
|
Reference in New Issue
Block a user