From 7c22ffed4591cdd74ddbb3d88b60212298143b96 Mon Sep 17 00:00:00 2001 From: Manol Donev Date: Fri, 31 May 2019 16:17:55 +0300 Subject: [PATCH 1/5] fix(hmr): support for multi module replacement --- tests/app/livesync/livesync-button-page.scss | 3 + tests/app/livesync/livesync-tests.ts | 56 ++++++++++++++++++- .../ui/frame/fragment.transitions.android.ts | 3 +- tns-core-modules/ui/frame/frame-common.ts | 54 +++++++++++------- tns-core-modules/ui/frame/frame.android.ts | 53 ++++++++---------- tns-core-modules/ui/frame/frame.d.ts | 20 ++++--- tns-core-modules/ui/frame/frame.ios.ts | 17 +++--- tns-core-modules/ui/page/page.ios.ts | 48 +++++++--------- 8 files changed, 152 insertions(+), 102 deletions(-) create mode 100644 tests/app/livesync/livesync-button-page.scss diff --git a/tests/app/livesync/livesync-button-page.scss b/tests/app/livesync/livesync-button-page.scss new file mode 100644 index 000000000..3b012936e --- /dev/null +++ b/tests/app/livesync/livesync-button-page.scss @@ -0,0 +1,3 @@ +Button { + color: cyan; +} \ No newline at end of file diff --git a/tests/app/livesync/livesync-tests.ts b/tests/app/livesync/livesync-tests.ts index 79de7ad2c..87e3c67d6 100644 --- a/tests/app/livesync/livesync-tests.ts +++ b/tests/app/livesync/livesync-tests.ts @@ -5,7 +5,6 @@ import * as app from "tns-core-modules/application/application"; import * as frame from "tns-core-modules/ui/frame"; import { Color } from "tns-core-modules/color"; -import { isAndroid } from "tns-core-modules/platform"; import { createViewFromEntry } from "tns-core-modules/ui/builder"; import { Page } from "tns-core-modules/ui/page"; import { Frame } from "tns-core-modules/ui/frame"; @@ -20,6 +19,7 @@ const buttonHtmlPageFileName = "./livesync/livesync-button-page.html"; const buttonXmlPageFileName = "./livesync/livesync-button-page.xml"; const buttonJsPageFileName = "./livesync/livesync-button-page.js"; const buttonTsPageFileName = "./livesync/livesync-button-page.ts"; +const buttonScssPageFileName = "./livesync/livesync-button-page.scss"; const labelPageModuleName = "livesync/livesync-label-page"; const green = new Color("green"); @@ -60,6 +60,36 @@ export function test_onLiveSync_ModuleContext_Markup_XmlFile() { _test_onLiveSync_ModuleReplace({ type: "markup", path: buttonXmlPageFileName }); } +export function test_onLiveSync_ModuleContext_Markup_Script_XmlFile() { + _test_onLiveSync_ModuleReplace_Multiple([ + { type: "script", path: buttonTsPageFileName }, + { type: "markup", path: buttonXmlPageFileName } + ]); +} + +export function test_onLiveSync_ModuleContext_Markup_Script_Style_XmlFile() { + _test_onLiveSync_ModuleReplace_Multiple([ + { type: "script", path: buttonTsPageFileName }, + { type: "markup", path: buttonXmlPageFileName }, + { type: "style", path: buttonScssPageFileName } + ]); +} + +export function test_onLiveSync_ModuleContext_Markup_Script_HtmlFile() { + _test_onLiveSync_ModuleReplace_Multiple([ + { type: "script", path: buttonTsPageFileName }, + { type: "markup", path: buttonHtmlPageFileName } + ]); +} + +export function test_onLiveSync_ModuleContext_Markup_Script_Style_HtmlFile() { + _test_onLiveSync_ModuleReplace_Multiple([ + { type: "script", path: buttonTsPageFileName }, + { type: "markup", path: buttonHtmlPageFileName }, + { type: "style", path: buttonScssPageFileName } + ]); +} + export function setUp() { const labelPage = createViewFromEntry(({ moduleName: labelPageModuleName })); helper.navigate(() => labelPage); @@ -121,6 +151,28 @@ function _test_onLiveSync_ModuleReplace(context: { type, path }) { TKUnit.assertEqual(pageBeforeNavigation, pageAfterBackNavigation, "Pages are different!"); } +function _test_onLiveSync_ModuleReplace_Multiple(context: { type: string, path: string }[]) { + const pageBeforeNavigation = helper.getCurrentPage(); + const buttonPage = createViewFromEntry(({ moduleName: buttonPageModuleName })); + helper.navigateWithHistory(() => buttonPage); + + context.forEach(item => { + global.__onLiveSync(item); + }); + + const topmostFrame = frame.topmost(); + waitUntilLivesyncComplete(topmostFrame); + TKUnit.assertTrue(topmostFrame.currentPage.getViewById("button").isLoaded, "Button page is NOT loaded!"); + TKUnit.assertEqual(topmostFrame.backStack.length, 1, "Backstack is clean!"); + TKUnit.assertTrue(topmostFrame.canGoBack(), "Can NOT go back!"); + + helper.goBack(); + const pageAfterBackNavigation = helper.getCurrentPage(); + TKUnit.assertTrue(topmostFrame.currentPage.getViewById("label").isLoaded, "Label page is NOT loaded!"); + TKUnit.assertEqual(topmostFrame.backStack.length, 0, "Backstack is NOT clean!"); + TKUnit.assertEqual(pageBeforeNavigation, pageAfterBackNavigation, "Pages are different!"); +} + function _test_onLiveSync_ModuleContext_TypeStyle(context: { type, path }) { const pageBeforeNavigation = helper.getCurrentPage(); const buttonPage = createViewFromEntry(({ moduleName: buttonPageModuleName })); @@ -146,5 +198,5 @@ function _test_onLiveSync_ModuleContext_TypeStyle(context: { type, path }) { } function waitUntilLivesyncComplete(frame: Frame) { - TKUnit.waitUntilReady(() => frame._executingEntry === null); + TKUnit.waitUntilReady(() => frame.navigationQueueIsEmpty()); } diff --git a/tns-core-modules/ui/frame/fragment.transitions.android.ts b/tns-core-modules/ui/frame/fragment.transitions.android.ts index f391fcc0f..151066f1c 100644 --- a/tns-core-modules/ui/frame/fragment.transitions.android.ts +++ b/tns-core-modules/ui/frame/fragment.transitions.android.ts @@ -735,8 +735,7 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry): void { // Will be null if Frame is shown modally... // transitionOrAnimationCompleted fires again (probably bug in android). if (current) { - const navType = frame.navigationType; - setTimeout(() => frame.setCurrent(current, navType)); + setTimeout(() => frame.setCurrent(current)); } } else { completedEntries.set(frameId, entry); diff --git a/tns-core-modules/ui/frame/frame-common.ts b/tns-core-modules/ui/frame/frame-common.ts index ef7fbd470..93c12b08d 100644 --- a/tns-core-modules/ui/frame/frame-common.ts +++ b/tns-core-modules/ui/frame/frame-common.ts @@ -13,6 +13,7 @@ import { getModuleName } from "../../utils/utils"; export * from "../core/view"; export enum NavigationType { + unset, back, forward, replace @@ -37,7 +38,9 @@ function buildEntryFromArgs(arg: any): NavigationEntry { export interface NavigationContext { entry: BackstackEntry; + // TODO: remove isBackNavigation for NativeScript 6.0 isBackNavigation: boolean; + navigationType: NavigationType } @CSSType("Frame") @@ -51,11 +54,10 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { public actionBarVisibility: "auto" | "never" | "always"; public _currentEntry: BackstackEntry; - public _executingEntry: BackstackEntry; + public _executingContext: NavigationContext; public _isInFrameStack = false; public static defaultAnimatedNavigation = true; public static defaultTransition: NavigationTransition; - public navigationType: NavigationType; // TODO: Currently our navigation will not be synchronized in case users directly call native navigation methods like Activity.startActivity. @@ -75,7 +77,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { let previousForwardNotInBackstack = false; this._navigationQueue.forEach(item => { const entry = item.entry; - if (item.isBackNavigation) { + const isBackNavigation = item.navigationType === NavigationType.back; + if (isBackNavigation) { previousForwardNotInBackstack = false; if (!entry) { backstack--; @@ -135,7 +138,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { const navigationContext: NavigationContext = { entry: backstackEntry, - isBackNavigation: true + isBackNavigation: true, + navigationType: NavigationType.back } this._navigationQueue.push(navigationContext); @@ -203,7 +207,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { const navigationContext: NavigationContext = { entry: backstackEntry, - isBackNavigation: false + isBackNavigation: false, + navigationType: NavigationType.forward } this._navigationQueue.push(navigationContext); @@ -214,7 +219,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { return this._currentEntry === entry; } - public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void { + public setCurrent(entry: BackstackEntry): void { const newPage = entry.resolvedPage; // In case we navigated forward to a page that was in the backstack // with clearHistory: true @@ -225,6 +230,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { this._currentEntry = entry; + const navigationContext = this._executingContext || { navigationType: NavigationType.unset }; + const navigationType = navigationContext.navigationType; const isBack = navigationType === NavigationType.back; if (isBack) { this._pushInFrameStack(); @@ -232,10 +239,10 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { newPage.onNavigatedTo(isBack); - // Reset executing entry after NavigatedTo is raised; + // Reset executing context after NavigatedTo is raised; // we do not want to execute two navigations in parallel in case // additional navigation is triggered from the NavigatedTo handler. - this._executingEntry = null; + this._executingContext = null; } public _updateBackstack(entry: BackstackEntry, navigationType: NavigationType): void { @@ -342,13 +349,14 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { } protected _processNextNavigationEntry() { - if (!this.isLoaded || this._executingEntry) { + if (!this.isLoaded || this._executingContext) { return; } if (this._navigationQueue.length > 0) { const navigationContext = this._navigationQueue[0]; - if (navigationContext.isBackNavigation) { + const isBackNavigation = navigationContext.navigationType === NavigationType.back; + if (isBackNavigation) { this.performGoBack(navigationContext); } else { this.performNavigation(navigationContext); @@ -358,10 +366,12 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { @profile public performNavigation(navigationContext: NavigationContext) { - const navContext = navigationContext.entry; - this._executingEntry = navContext; - this._onNavigatingTo(navContext, navigationContext.isBackNavigation); - this._navigateCore(navContext); + this._executingContext = navigationContext; + + const backstackEntry = navigationContext.entry; + const isBackNavigation = navigationContext.navigationType === NavigationType.back; + this._onNavigatingTo(backstackEntry, isBackNavigation); + this._navigateCore(backstackEntry); } @profile @@ -373,7 +383,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { navigationContext.entry = backstackEntry; } - this._executingEntry = backstackEntry; + this._executingContext = navigationContext; this._onNavigatingTo(backstackEntry, true); this._goBackCore(backstackEntry); } @@ -643,9 +653,6 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { } protected replacePage(context: ModuleContext): void { - // Set NavigationType.replace for HMR. - // In IOS on `viewDidAppear()` this will be set to NavigationType.forward. - this.navigationType = NavigationType.replace; const currentBackstackEntry = this._currentEntry; const contextModuleName = getModuleName(context.path); @@ -658,10 +665,15 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { frameId: currentBackstackEntry.frameId }; - const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false }; - this.performNavigation(navContext); - } + const navigationContext: NavigationContext = { + entry: newBackstackEntry, + isBackNavigation: false, + navigationType: NavigationType.replace + }; + this._navigationQueue.push(navigationContext); + this._processNextNavigationEntry(); + } } export function getFrameById(id: string): FrameBase { diff --git a/tns-core-modules/ui/frame/frame.android.ts b/tns-core-modules/ui/frame/frame.android.ts index 194771031..8d4ac385d 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -163,24 +163,12 @@ export class Frame extends FrameBase { // In this case call _navigateCore in order to recreate the current fragment. // Don't call navigate because it will fire navigation events. // As JS instances are alive it is already done for the current page. - if (!this.isLoaded || this._executingEntry || !this._attachedToWindow) { + if (!this.isLoaded || this._executingContext || !this._attachedToWindow) { return; } const animatedEntries = _getAnimatedEntries(this._android.frameId); if (animatedEntries) { - // // recreate UI on the animated fragments because we have new context. - // // We need to recreate the UI because it Frame will do it only for currentPage. - // // Once currentPage is changed due to transition end we will have no UI on the - // // new Page. - // animatedEntries.forEach(entry => { - // const page = entry.resolvedPage; - // if (page._context !== this._context) { - // page._tearDownUI(true); - // page._setupUI(this._context); - // } - // }); - // Wait until animations are completed. if (animatedEntries.size > 0) { return; @@ -212,7 +200,13 @@ export class Frame extends FrameBase { } public _getChildFragmentManager() { - const backstackEntry = this._executingEntry || this._currentEntry; + let backstackEntry; + if (this._executingContext && this._executingContext.entry) { + backstackEntry = this._executingContext.entry; + } else { + backstackEntry = this._currentEntry; + } + if (backstackEntry && backstackEntry.fragment && backstackEntry.fragment.isAdded()) { return backstackEntry.fragment.getChildFragmentManager(); } @@ -271,9 +265,11 @@ export class Frame extends FrameBase { return newFragment; } - public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void { + public setCurrent(entry: BackstackEntry): void { const current = this._currentEntry; const currentEntryChanged = current !== entry; + const navigationContext = this._executingContext || { navigationType: NavigationType.unset }; + const navigationType = navigationContext.navigationType; if (currentEntryChanged) { this._updateBackstack(entry, navigationType); @@ -304,7 +300,7 @@ export class Frame extends FrameBase { } } - super.setCurrent(entry, navigationType); + super.setCurrent(entry); // If we had real navigation process queue. this._processNavigationQueue(entry.resolvedPage); @@ -324,7 +320,7 @@ export class Frame extends FrameBase { if (navigationType === NavigationType.replace) { _clearEntry(entry); - const animated = entry.entry.animated; + const animated = this._getIsAnimatedNavigation(entry.entry); const navigationTransition = this._getNavigationTransition(entry.entry); const currentEntry = null; const newEntry = entry; @@ -353,12 +349,6 @@ export class Frame extends FrameBase { @profile public _navigateCore(newEntry: BackstackEntry) { super._navigateCore(newEntry); - // NavigationType.replace for HMR. - // Otherwise, default to NavigationType.forward. - const isReplace = this.navigationType === NavigationType.replace; - if (!isReplace) { - this.navigationType = NavigationType.forward; - } // set frameId here so that we could use it in fragment.transitions newEntry.frameId = this._android.frameId; @@ -385,6 +375,8 @@ export class Frame extends FrameBase { navDepth = -1; } + const navigationContext = this._executingContext || { navigationType: NavigationType.unset }; + const isReplace = navigationContext.navigationType === NavigationType.replace; if (!isReplace) { navDepth++; } @@ -419,7 +411,6 @@ export class Frame extends FrameBase { } public _goBackCore(backstackEntry: BackstackEntry) { - this.navigationType = NavigationType.back; super._goBackCore(backstackEntry); navDepth = backstackEntry.navDepth; @@ -477,18 +468,18 @@ export class Frame extends FrameBase { const listener = getAttachListener(); this.nativeViewProtected.removeOnAttachStateChangeListener(listener); this.nativeViewProtected[ownerSymbol] = null; - this._tearDownPending = !!this._executingEntry; + this._tearDownPending = !!this._executingContext; const current = this._currentEntry; - + const executingContext = this._executingContext || { entry: null }; this.backStack.forEach(entry => { // Don't destroy current and executing entries or UI will look blank. // We will do it in setCurrent. - if (entry !== this._executingEntry) { + if (entry !== executingContext.entry) { clearEntry(entry); } }); - if (current && !this._executingEntry) { + if (current && !executingContext.entry) { clearEntry(current); } @@ -711,11 +702,11 @@ function findPageForFragment(fragment: android.support.v4.app.Fragment, frame: F let entry: BackstackEntry; const current = frame._currentEntry; - const navigating = frame._executingEntry; + const executingContext = frame._executingContext; if (current && current.fragmentTag === fragmentTag) { entry = current; - } else if (navigating && navigating.fragmentTag === fragmentTag) { - entry = navigating; + } else if (executingContext && executingContext.entry && executingContext.entry.fragmentTag === fragmentTag) { + entry = executingContext.entry; } let page: Page; diff --git a/tns-core-modules/ui/frame/frame.d.ts b/tns-core-modules/ui/frame/frame.d.ts index 36fdc6b95..aa358d2cf 100644 --- a/tns-core-modules/ui/frame/frame.d.ts +++ b/tns-core-modules/ui/frame/frame.d.ts @@ -117,9 +117,8 @@ export class Frame extends View { /** * @private * @param entry to set as current - * @param navigationType */ - setCurrent(entry: BackstackEntry, navigationType: NavigationType): void; + setCurrent(entry: BackstackEntry): void; /** * @private */ @@ -135,11 +134,19 @@ export class Frame extends View { /** * @private */ - _executingEntry: BackstackEntry; + _executingContext: NavigationContext; /** * @private */ _processNavigationQueue(page: Page); + /** + * @private + */ + _getIsAnimatedNavigation(entry: NavigationEntry): boolean; + /** + * @private + */ + _getNavigationTransition(entry: NavigationEntry): NavigationTransition; /** * @private */ @@ -173,12 +180,6 @@ export class Frame extends View { * @private */ _removeFromFrameStack(); - - /** - * @private - * Represents the type of navigation. - */ - navigationType: NavigationType; //@endprivate /** @@ -289,6 +290,7 @@ export interface NavigationEntry extends ViewEntry { export interface NavigationContext { entry: BackstackEntry; isBackNavigation: boolean; + navigationType: NavigationType; } /** diff --git a/tns-core-modules/ui/frame/frame.ios.ts b/tns-core-modules/ui/frame/frame.ios.ts index 765571f1e..03f553fc8 100644 --- a/tns-core-modules/ui/frame/frame.ios.ts +++ b/tns-core-modules/ui/frame/frame.ios.ts @@ -53,24 +53,19 @@ export class Frame extends FrameBase { return this._ios; } - public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void { + public setCurrent(entry: BackstackEntry): void { const current = this._currentEntry; const currentEntryChanged = current !== entry; + const navigationContext = this._executingContext || { navigationType: NavigationType.unset }; if (currentEntryChanged) { - this._updateBackstack(entry, navigationType); + this._updateBackstack(entry, navigationContext.navigationType); - super.setCurrent(entry, navigationType); + super.setCurrent(entry); } } @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; @@ -82,6 +77,9 @@ export class Frame extends FrameBase { if (clearHistory) { navDepth = -1; } + + const navigationContext = this._executingContext || { navigationType: NavigationType.unset }; + const isReplace = navigationContext.navigationType === NavigationType.replace; if (!isReplace) { navDepth++; } @@ -187,7 +185,6 @@ export class Frame extends FrameBase { } public _goBackCore(backstackEntry: BackstackEntry) { - this.navigationType = NavigationType.back; super._goBackCore(backstackEntry); navDepth = backstackEntry[NAV_DEPTH]; diff --git a/tns-core-modules/ui/page/page.ios.ts b/tns-core-modules/ui/page/page.ios.ts index 1650fc04b..2c94dc37e 100644 --- a/tns-core-modules/ui/page/page.ios.ts +++ b/tns-core-modules/ui/page/page.ios.ts @@ -22,18 +22,24 @@ const majorVersion = iosUtils.MajorVersion; function isBackNavigationTo(page: Page, entry): boolean { const frame = page.frame; - if (!frame || frame.navigationType === NavigationType.replace) { + if (!frame) { + return false; + } + + const navigationContext = frame._executingContext || { navigationType: NavigationType.unset }; + const isReplace = navigationContext.navigationType === NavigationType.replace; + if (isReplace) { return false; } if (frame.navigationQueueIsEmpty()) { return true; - } else { - const navigationQueue = (frame)._navigationQueue; - for (let i = 0; i < navigationQueue.length; i++) { - if (navigationQueue[i].entry === entry) { - return navigationQueue[i].isBackNavigation; - } + } + + const navigationQueue = (frame)._navigationQueue; + for (let i = 0; i < navigationQueue.length; i++) { + if (navigationQueue[i].entry === entry) { + return navigationQueue[i].navigationType === NavigationType.back; } } @@ -129,30 +135,20 @@ class UIViewControllerImpl extends UIViewController { } const navigationController = this.navigationController; - const frame = navigationController ? (navigationController).owner : null; + const frame: Frame = navigationController ? (navigationController).owner : null; // Skip navigation events if modal page is shown. if (!owner._presentedViewController && frame) { const newEntry: BackstackEntry = 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 (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, navType); + frame.setCurrent(newEntry); - if (frame.navigationType === NavigationType.replace) { + const navigationContext = frame._executingContext || { navigationType: NavigationType.unset }; + const isReplace = navigationContext.navigationType === NavigationType.replace; + if (isReplace) { let controller = newEntry.resolvedPage.ios; if (controller) { - if (newEntry.entry.animated) { + const animated = frame._getIsAnimatedNavigation(newEntry.entry); + if (animated) { controller[TRANSITION] = frame._getNavigationTransition(newEntry.entry); } else { controller[TRANSITION] = { name: NON_ANIMATED_TRANSITION }; @@ -160,8 +156,6 @@ class UIViewControllerImpl extends UIViewController { } } - frame.navigationType = isBack ? NavigationType.back : NavigationType.forward; - // If page was shown with custom animation - we need to set the navigationController.delegate to the animatedDelegate. frame.ios.controller.delegate = this[DELEGATE]; @@ -209,7 +203,7 @@ class UIViewControllerImpl extends UIViewController { const willSelectViewController = tab && (tab)._willSelectViewController; if (!willSelectViewController || willSelectViewController === tab.selectedViewController) { - let isBack = isBackNavigationFrom(this, owner); + const isBack = isBackNavigationFrom(this, owner); owner.onNavigatingFrom(isBack); } } From 30d06f2d7ed23d36305657e461d349f4292a997e Mon Sep 17 00:00:00 2001 From: Manol Donev Date: Fri, 31 May 2019 19:16:33 +0300 Subject: [PATCH 2/5] fix iOS back transition after hmr --- tns-core-modules/ui/page/page.ios.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tns-core-modules/ui/page/page.ios.ts b/tns-core-modules/ui/page/page.ios.ts index 2c94dc37e..4e03a8ef9 100644 --- a/tns-core-modules/ui/page/page.ios.ts +++ b/tns-core-modules/ui/page/page.ios.ts @@ -140,10 +140,12 @@ class UIViewControllerImpl extends UIViewController { if (!owner._presentedViewController && frame) { const newEntry: BackstackEntry = this[ENTRY]; - frame.setCurrent(newEntry); - + // frame.setCurrent(...) will reset executing context so retrieve it here const navigationContext = frame._executingContext || { navigationType: NavigationType.unset }; const isReplace = navigationContext.navigationType === NavigationType.replace; + + frame.setCurrent(newEntry); + if (isReplace) { let controller = newEntry.resolvedPage.ios; if (controller) { From 6d360418234638b5247b5faa50208909d3eb30bf Mon Sep 17 00:00:00 2001 From: Manol Donev Date: Tue, 4 Jun 2019 17:40:26 +0300 Subject: [PATCH 3/5] fix back navigation --- .../ui/frame/fragment.transitions.android.ts | 5 +++-- tns-core-modules/ui/frame/frame-common.ts | 5 +---- tns-core-modules/ui/frame/frame.android.ts | 15 ++++++--------- tns-core-modules/ui/frame/frame.d.ts | 3 ++- tns-core-modules/ui/frame/frame.ios.ts | 10 ++++------ tns-core-modules/ui/page/page.ios.ts | 8 +++++--- 6 files changed, 21 insertions(+), 25 deletions(-) diff --git a/tns-core-modules/ui/frame/fragment.transitions.android.ts b/tns-core-modules/ui/frame/fragment.transitions.android.ts index 151066f1c..437698d8e 100644 --- a/tns-core-modules/ui/frame/fragment.transitions.android.ts +++ b/tns-core-modules/ui/frame/fragment.transitions.android.ts @@ -724,6 +724,7 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry): void { entries.delete(entry); 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 const previousCompletedAnimationEntry = completedEntries.get(frameId); @@ -734,8 +735,8 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry): void { current = current || entry; // Will be null if Frame is shown modally... // transitionOrAnimationCompleted fires again (probably bug in android). - if (current) { - setTimeout(() => frame.setCurrent(current)); + if (current && frame._executingContext) { + setTimeout(() => frame.setCurrent(current, frame._executingContext.navigationType)); } } else { completedEntries.set(frameId, entry); diff --git a/tns-core-modules/ui/frame/frame-common.ts b/tns-core-modules/ui/frame/frame-common.ts index 93c12b08d..b88d79c56 100644 --- a/tns-core-modules/ui/frame/frame-common.ts +++ b/tns-core-modules/ui/frame/frame-common.ts @@ -13,7 +13,6 @@ import { getModuleName } from "../../utils/utils"; export * from "../core/view"; export enum NavigationType { - unset, back, forward, replace @@ -219,7 +218,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { return this._currentEntry === entry; } - public setCurrent(entry: BackstackEntry): 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 @@ -230,8 +229,6 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { this._currentEntry = entry; - const navigationContext = this._executingContext || { navigationType: NavigationType.unset }; - const navigationType = navigationContext.navigationType; const isBack = navigationType === NavigationType.back; if (isBack) { this._pushInFrameStack(); diff --git a/tns-core-modules/ui/frame/frame.android.ts b/tns-core-modules/ui/frame/frame.android.ts index 8d4ac385d..bdf640b8c 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -265,11 +265,9 @@ export class Frame extends FrameBase { return newFragment; } - public setCurrent(entry: BackstackEntry): void { + public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void { const current = this._currentEntry; const currentEntryChanged = current !== entry; - const navigationContext = this._executingContext || { navigationType: NavigationType.unset }; - const navigationType = navigationContext.navigationType; if (currentEntryChanged) { this._updateBackstack(entry, navigationType); @@ -300,7 +298,7 @@ export class Frame extends FrameBase { } } - super.setCurrent(entry); + super.setCurrent(entry, navigationType); // If we had real navigation process queue. this._processNavigationQueue(entry.resolvedPage); @@ -375,8 +373,7 @@ export class Frame extends FrameBase { navDepth = -1; } - const navigationContext = this._executingContext || { navigationType: NavigationType.unset }; - const isReplace = navigationContext.navigationType === NavigationType.replace; + const isReplace = this._executingContext && this._executingContext.navigationType === NavigationType.replace; if (!isReplace) { navDepth++; } @@ -470,16 +467,16 @@ export class Frame extends FrameBase { this.nativeViewProtected[ownerSymbol] = null; this._tearDownPending = !!this._executingContext; const current = this._currentEntry; - const executingContext = this._executingContext || { entry: null }; + const executingEntry = this._executingContext ? this._executingContext.entry : null; this.backStack.forEach(entry => { // Don't destroy current and executing entries or UI will look blank. // We will do it in setCurrent. - if (entry !== executingContext.entry) { + if (entry !== executingEntry) { clearEntry(entry); } }); - if (current && !executingContext.entry) { + if (current && !executingEntry) { clearEntry(current); } diff --git a/tns-core-modules/ui/frame/frame.d.ts b/tns-core-modules/ui/frame/frame.d.ts index aa358d2cf..78ac20725 100644 --- a/tns-core-modules/ui/frame/frame.d.ts +++ b/tns-core-modules/ui/frame/frame.d.ts @@ -117,8 +117,9 @@ export class Frame extends View { /** * @private * @param entry to set as current + * @param navigationType */ - setCurrent(entry: BackstackEntry): void; + setCurrent(entry: BackstackEntry, navigationType: NavigationType): void; /** * @private */ diff --git a/tns-core-modules/ui/frame/frame.ios.ts b/tns-core-modules/ui/frame/frame.ios.ts index 03f553fc8..d06548dcb 100644 --- a/tns-core-modules/ui/frame/frame.ios.ts +++ b/tns-core-modules/ui/frame/frame.ios.ts @@ -53,14 +53,13 @@ export class Frame extends FrameBase { return this._ios; } - public setCurrent(entry: BackstackEntry): void { + public setCurrent(entry: BackstackEntry, navigationType: NavigationType): void { const current = this._currentEntry; const currentEntryChanged = current !== entry; - const navigationContext = this._executingContext || { navigationType: NavigationType.unset }; if (currentEntryChanged) { - this._updateBackstack(entry, navigationContext.navigationType); + this._updateBackstack(entry, navigationType); - super.setCurrent(entry); + super.setCurrent(entry, navigationType); } } @@ -78,8 +77,7 @@ export class Frame extends FrameBase { navDepth = -1; } - const navigationContext = this._executingContext || { navigationType: NavigationType.unset }; - const isReplace = navigationContext.navigationType === NavigationType.replace; + const isReplace = this._executingContext && this._executingContext.navigationType === NavigationType.replace; if (!isReplace) { navDepth++; } diff --git a/tns-core-modules/ui/page/page.ios.ts b/tns-core-modules/ui/page/page.ios.ts index 4e03a8ef9..c80a6becd 100644 --- a/tns-core-modules/ui/page/page.ios.ts +++ b/tns-core-modules/ui/page/page.ios.ts @@ -26,7 +26,8 @@ function isBackNavigationTo(page: Page, entry): boolean { return false; } - const navigationContext = frame._executingContext || { navigationType: NavigationType.unset }; + // if executing context is null here this most probably means back navigation through iOS back button + const navigationContext = frame._executingContext || { navigationType: NavigationType.back }; const isReplace = navigationContext.navigationType === NavigationType.replace; if (isReplace) { return false; @@ -141,10 +142,11 @@ class UIViewControllerImpl extends UIViewController { const newEntry: BackstackEntry = this[ENTRY]; // frame.setCurrent(...) will reset executing context so retrieve it here - const navigationContext = frame._executingContext || { navigationType: NavigationType.unset }; + // if executing context is null here this most probably means back navigation through iOS back button + const navigationContext = frame._executingContext || { navigationType: NavigationType.back }; const isReplace = navigationContext.navigationType === NavigationType.replace; - frame.setCurrent(newEntry); + frame.setCurrent(newEntry, navigationContext.navigationType); if (isReplace) { let controller = newEntry.resolvedPage.ios; From 6720139ec0a700cc194a62a34eb50c04d518fd9c Mon Sep 17 00:00:00 2001 From: Martin Bektchiev Date: Wed, 5 Jun 2019 17:41:58 +0300 Subject: [PATCH 4/5] fix(platform-declarations-ios): Change 2^64-1 enum values to -1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `18446744073709551615` (`0xFFFFFFFFFFFFFFFF`) is an overflow to the JS numerical system and therefore incorrect. Integer values from `-(2^53-1)` to `(2^53-1)` only can be safely represented in JS (`Number​.MIN_SAFE_INTEGER` to `Number​.MAX_SAFE_INTEGER`) There's a fix in iOS Runtime's metadata generator which now automatically converts such values to signed. (https://github.com/NativeScript/ios-runtime/pull/1151) refs https://github.com/NativeScript/ios-runtime/issues/1150 --- .../ios/objc-x86_64/objc!MediaPlayer.d.ts | 2 +- .../ios/objc-x86_64/objc!PassKit.d.ts | 2 +- .../ios/objc-x86_64/objc!SceneKit.d.ts | 2 +- tns-platform-declarations/ios/objc-x86_64/objc!UIKit.d.ts | 8 ++++---- .../ios/objc-x86_64/objc!WebKit.d.ts | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tns-platform-declarations/ios/objc-x86_64/objc!MediaPlayer.d.ts b/tns-platform-declarations/ios/objc-x86_64/objc!MediaPlayer.d.ts index 4cf0ab8e4..436cc8f02 100644 --- a/tns-platform-declarations/ios/objc-x86_64/objc!MediaPlayer.d.ts +++ b/tns-platform-declarations/ios/objc-x86_64/objc!MediaPlayer.d.ts @@ -743,7 +743,7 @@ declare const enum MPMediaType { AnyVideo = 65280, - Any = 18446744073709551615 + Any = -1 } declare class MPMovieAccessLog extends NSObject implements NSCopying { diff --git a/tns-platform-declarations/ios/objc-x86_64/objc!PassKit.d.ts b/tns-platform-declarations/ios/objc-x86_64/objc!PassKit.d.ts index 9b763aa5e..b005348a8 100644 --- a/tns-platform-declarations/ios/objc-x86_64/objc!PassKit.d.ts +++ b/tns-platform-declarations/ios/objc-x86_64/objc!PassKit.d.ts @@ -375,7 +375,7 @@ declare const enum PKPassType { Payment = 1, - Any = 18446744073709551615 + Any = -1 } declare class PKPayment extends NSObject { diff --git a/tns-platform-declarations/ios/objc-x86_64/objc!SceneKit.d.ts b/tns-platform-declarations/ios/objc-x86_64/objc!SceneKit.d.ts index 1c99e60d7..2868dce8d 100644 --- a/tns-platform-declarations/ios/objc-x86_64/objc!SceneKit.d.ts +++ b/tns-platform-declarations/ios/objc-x86_64/objc!SceneKit.d.ts @@ -2939,7 +2939,7 @@ declare const enum SCNPhysicsCollisionCategory { Static = 2, - All = 18446744073709551615 + All = -1 } declare class SCNPhysicsConeTwistJoint extends SCNPhysicsBehavior { diff --git a/tns-platform-declarations/ios/objc-x86_64/objc!UIKit.d.ts b/tns-platform-declarations/ios/objc-x86_64/objc!UIKit.d.ts index 3f838f5c8..0b35c8ac4 100644 --- a/tns-platform-declarations/ios/objc-x86_64/objc!UIKit.d.ts +++ b/tns-platform-declarations/ios/objc-x86_64/objc!UIKit.d.ts @@ -4354,7 +4354,7 @@ declare const enum UICollisionBehaviorMode { Boundaries = 2, - Everything = 18446744073709551615 + Everything = -1 } declare class UIColor extends NSObject implements NSCopying, NSItemProviderReading, NSItemProviderWriting, NSSecureCoding { @@ -4831,7 +4831,7 @@ declare const enum UIDataDetectorTypes { None = 0, - All = 18446744073709551615 + All = -1 } interface UIDataSourceModelAssociation { @@ -9348,7 +9348,7 @@ declare const enum UIPopoverArrowDirection { Any = 15, - Unknown = 18446744073709551615 + Unknown = -1 } declare class UIPopoverBackgroundView extends UIView implements UIPopoverBackgroundViewMethods { @@ -10286,7 +10286,7 @@ declare const enum UIRectCorner { BottomRight = 8, - AllCorners = 18446744073709551615 + AllCorners = -1 } declare const enum UIRectEdge { diff --git a/tns-platform-declarations/ios/objc-x86_64/objc!WebKit.d.ts b/tns-platform-declarations/ios/objc-x86_64/objc!WebKit.d.ts index 5f94e389a..374743a55 100644 --- a/tns-platform-declarations/ios/objc-x86_64/objc!WebKit.d.ts +++ b/tns-platform-declarations/ios/objc-x86_64/objc!WebKit.d.ts @@ -7,7 +7,7 @@ declare const enum WKAudiovisualMediaTypes { Video = 2, - All = 18446744073709551615 + All = -1 } declare class WKBackForwardList extends NSObject { @@ -88,7 +88,7 @@ declare const enum WKDataDetectorTypes { LookupSuggestion = 64, - All = 18446744073709551615, + All = -1, SpotlightSuggestion = 64 } From f5fd979eff9b1eef089f37fe82f1a1435791bac0 Mon Sep 17 00:00:00 2001 From: Martin Bektchiev Date: Thu, 6 Jun 2019 11:39:44 +0300 Subject: [PATCH 5/5] chore(widgets): Build TNSWidgets.framework.dSYM package (#7315) We should provide debug symbols so that users can upload it to their crashlytics service provider. --- tns-core-modules-widgets/build.ios.sh | 3 ++- tns-core-modules-widgets/ios/build.sh | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tns-core-modules-widgets/build.ios.sh b/tns-core-modules-widgets/build.ios.sh index 48ee95c95..509cd6ad3 100755 --- a/tns-core-modules-widgets/build.ios.sh +++ b/tns-core-modules-widgets/build.ios.sh @@ -17,7 +17,8 @@ mkdir dist/package/platforms/ios cd ios ./build.sh cd .. -cp -r ios/TNSWidgets/build/TNSWidgets.framework dist/package/platforms/ios/TNSWidgets.framework +echo "Copy TNSWidgets.framework and TNSWidgets.framework.dSYM.zip to dist/package/platforms/ios" +cp -r ios/TNSWidgets/build/TNSWidgets.framework* dist/package/platforms/ios echo "Copy NPM artefacts" cp LICENSE dist/package/LICENSE diff --git a/tns-core-modules-widgets/ios/build.sh b/tns-core-modules-widgets/ios/build.sh index e287a8e8f..2230374c3 100755 --- a/tns-core-modules-widgets/ios/build.sh +++ b/tns-core-modules-widgets/ios/build.sh @@ -19,3 +19,17 @@ cp -r TNSWidgets/build/Release-iphoneos/TNSWidgets.framework/Info.plist TNSWidge lipo -create TNSWidgets/build/Release-iphoneos/TNSWidgets.framework/TNSWidgets TNSWidgets/build/Release-iphonesimulator/TNSWidgets.framework/TNSWidgets -o TNSWidgets/build/TNSWidgets.framework/TNSWidgets file TNSWidgets/build/TNSWidgets.framework/TNSWidgets + +echo "Build fat dSYM at TNSWidgets/build/TNSWidgets.framework.dSYM" +cp -r TNSWidgets/build/Release-iphoneos/TNSWidgets.framework.dSYM TNSWidgets/build +rm "TNSWidgets/build/TNSWidgets.framework.dSYM/Contents/Resources/DWARF/TNSWidgets" +lipo -create -output "TNSWidgets/build/TNSWidgets.framework.dSYM/Contents/Resources/DWARF/TNSWidgets" \ + "TNSWidgets/build/Release-iphonesimulator/TNSWidgets.framework.dSYM/Contents/Resources/DWARF/TNSWidgets" \ + "TNSWidgets/build/Release-iphoneos/TNSWidgets.framework.dSYM/Contents/Resources/DWARF/TNSWidgets" +file TNSWidgets/build/TNSWidgets.framework.dSYM/Contents/Resources/DWARF/TNSWidgets + +echo "Archiving dSYM at TNSWidgets/build/TNSWidgets.framework.dSYM.zip" +(cd TNSWidgets/build && zip -qr TNSWidgets.framework.dSYM.zip TNSWidgets.framework.dSYM) + +echo "Removing TNSWidgets/build/TNSWidgets.framework.dSYM" +rm -rf TNSWidgets/build/TNSWidgets.framework.dSYM