From 0aca08755aaaf49ea138ad89ea88bce0ce1aa64d Mon Sep 17 00:00:00 2001 From: Manol Donev Date: Wed, 22 May 2019 11:45:55 +0300 Subject: [PATCH] fix(hmr): quick fade upon replace navigation (#7251) --- tests/app/livesync/livesync-tests.ts | 6 +--- tns-core-modules/ui/core/view/view-common.ts | 2 +- .../ui/frame/fragment.transitions.android.ts | 9 ++++-- tns-core-modules/ui/frame/frame.android.ts | 30 +++++++++++++++---- tns-core-modules/ui/frame/frame.ios.ts | 13 ++++---- tns-core-modules/ui/page/page.ios.ts | 18 +++++++++-- 6 files changed, 58 insertions(+), 20 deletions(-) diff --git a/tests/app/livesync/livesync-tests.ts b/tests/app/livesync/livesync-tests.ts index 7eafac365..79de7ad2c 100644 --- a/tests/app/livesync/livesync-tests.ts +++ b/tests/app/livesync/livesync-tests.ts @@ -146,9 +146,5 @@ function _test_onLiveSync_ModuleContext_TypeStyle(context: { type, path }) { } function waitUntilLivesyncComplete(frame: Frame) { - if (isAndroid) { - TKUnit.waitUntilReady(() => frame._executingEntry === null); - } else { - TKUnit.waitUntilReady(() => frame.currentPage.isLoaded); - } + TKUnit.waitUntilReady(() => frame._executingEntry === null); } diff --git a/tns-core-modules/ui/core/view/view-common.ts b/tns-core-modules/ui/core/view/view-common.ts index ac3ef9f50..d392dcfed 100644 --- a/tns-core-modules/ui/core/view/view-common.ts +++ b/tns-core-modules/ui/core/view/view-common.ts @@ -5,7 +5,7 @@ import { } from "."; import { - ViewBase, Property, booleanConverter, eachDescendant, EventData, layout, + ViewBase, Property, booleanConverter, EventData, layout, getEventOrGestureName, traceEnabled, traceWrite, traceCategories, InheritedProperty, ShowModalOptions } from "../view-base"; diff --git a/tns-core-modules/ui/frame/fragment.transitions.android.ts b/tns-core-modules/ui/frame/fragment.transitions.android.ts index 70f1fba7b..f391fcc0f 100644 --- a/tns-core-modules/ui/frame/fragment.transitions.android.ts +++ b/tns-core-modules/ui/frame/fragment.transitions.android.ts @@ -169,8 +169,11 @@ export function _setAndroidFragmentTransitions( // Having transition means we have custom animation if (transition) { - // we do not use Android backstack so setting popEnter / popExit is meaningless (3rd and 4th optional args) - fragmentTransaction.setCustomAnimations(AnimationType.enterFakeResourceId, AnimationType.exitFakeResourceId); + if (fragmentTransaction) { + // we do not use Android backstack so setting popEnter / popExit is meaningless (3rd and 4th optional args) + fragmentTransaction.setCustomAnimations(AnimationType.enterFakeResourceId, AnimationType.exitFakeResourceId); + } + setupAllAnimation(newEntry, transition); if (currentFragmentNeedsDifferentAnimation) { setupExitAndPopEnterAnimation(currentEntry, transition); @@ -502,6 +505,8 @@ function clearEntry(entry: ExpandedEntry, removeListener: boolean): void { clearAnimationListener(entry.exitAnimator, listener); clearAnimationListener(entry.popEnterAnimator, listener); clearAnimationListener(entry.popExitAnimator, listener); + clearAnimationListener(entry.defaultEnterAnimator, listener); + clearAnimationListener(entry.defaultExitAnimator, listener); } } diff --git a/tns-core-modules/ui/frame/frame.android.ts b/tns-core-modules/ui/frame/frame.android.ts index 7151448e3..194771031 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -1,14 +1,14 @@ // Definitions. import { AndroidFrame as AndroidFrameDefinition, AndroidActivityCallbacks, - AndroidFragmentCallbacks, BackstackEntry, NavigationTransition + AndroidFragmentCallbacks, BackstackEntry, NavigationTransition, NavigationEntry } from "."; import { Page } from "../page"; // Types. import * as application from "../../application"; import { - FrameBase, goBack, stack, NavigationContext, NavigationType, + FrameBase, goBack, stack, NavigationType, Observable, View, traceCategories, traceEnabled, traceError, traceWrite } from "./frame-common"; @@ -21,7 +21,6 @@ import { profile } from "../../profiling"; // TODO: Remove this and get it from global to decouple builder for angular import { createViewFromEntry } from "../builder"; -import { getModuleName } from "../../utils/utils"; export * from "./frame-common"; @@ -37,6 +36,7 @@ const INTENT_EXTRA = "com.tns.activity"; const ROOT_VIEW_ID_EXTRA = "com.tns.activity.rootViewId"; const FRAMEID = "_frameId"; const CALLBACKS = "_callbacks"; +const HMR_REPLACE_TRANSITION = "fade"; const ownerSymbol = Symbol("_owner"); const activityRootViewsMap = new Map>(); @@ -319,6 +319,18 @@ export class Frame extends FrameBase { restoreAnimatorState(this._currentEntry, this._cachedAnimatorState); this._cachedAnimatorState = null; } + + // restore original fragment transitions if we just completed replace navigation (hmr) + if (navigationType === NavigationType.replace) { + _clearEntry(entry); + + const animated = entry.entry.animated; + const navigationTransition = this._getNavigationTransition(entry.entry); + const currentEntry = null; + const newEntry = entry; + const transaction = null; + _setAndroidFragmentTransitions(animated, navigationTransition, currentEntry, newEntry, transaction, this._android.frameId); + } } public onBackPressed(): boolean { @@ -381,12 +393,20 @@ export class Frame extends FrameBase { const newFragmentTag = `fragment${fragmentId}[${navDepth}]`; const newFragment = this.createFragment(newEntry, newFragmentTag); const transaction = manager.beginTransaction(); - const animated = currentEntry ? this._getIsAnimatedNavigation(newEntry.entry) : false; + let animated = currentEntry ? this._getIsAnimatedNavigation(newEntry.entry) : false; // NOTE: Don't use transition for the initial navigation (same as on iOS) // On API 21+ transition won't be triggered unless there was at least one // layout pass so we will wait forever for transitionCompleted handler... // https://github.com/NativeScript/NativeScript/issues/4895 - const navigationTransition = this._currentEntry ? this._getNavigationTransition(newEntry.entry) : null; + let navigationTransition: NavigationTransition; + if (isReplace) { + animated = true; + navigationTransition = { name: HMR_REPLACE_TRANSITION, duration: 100 }; + } else if (this._currentEntry) { + navigationTransition = this._getNavigationTransition(newEntry.entry); + } else { + navigationTransition = null; + } _setAndroidFragmentTransitions(animated, navigationTransition, currentEntry, newEntry, transaction, this._android.frameId); diff --git a/tns-core-modules/ui/frame/frame.ios.ts b/tns-core-modules/ui/frame/frame.ios.ts index a9a985b0c..765571f1e 100644 --- a/tns-core-modules/ui/frame/frame.ios.ts +++ b/tns-core-modules/ui/frame/frame.ios.ts @@ -7,12 +7,11 @@ import { profile } from "../../profiling"; //Types. import { - FrameBase, View, isCategorySet, layout, NavigationContext, + FrameBase, View, isCategorySet, layout, NavigationType, traceCategories, traceEnabled, traceWrite } from "./frame-common"; import { _createIOSAnimatedTransitioning } from "./fragment.transitions"; -import { createViewFromEntry } from "../builder"; import * as utils from "../../utils/utils"; export * from "./frame-common"; @@ -24,6 +23,7 @@ const DELEGATE = "_delegate"; const NAV_DEPTH = "_navDepth"; const TRANSITION = "_transition"; const NON_ANIMATED_TRANSITION = "non-animated"; +const HMR_REPLACE_TRANSITION = "fade"; let navDepth = -1; @@ -88,13 +88,16 @@ export class Frame extends FrameBase { let navigationTransition: NavigationTransition; let animated = this.currentPage ? this._getIsAnimatedNavigation(backstackEntry.entry) : false; - if (animated) { + if (isReplace) { + animated = true; + navigationTransition = { name: HMR_REPLACE_TRANSITION, duration: 100 } + viewController[TRANSITION] = navigationTransition; + } else if (animated) { navigationTransition = this._getNavigationTransition(backstackEntry.entry); if (navigationTransition) { viewController[TRANSITION] = navigationTransition; } - } - else { + } else { //https://github.com/NativeScript/NativeScript/issues/1787 viewController[TRANSITION] = { name: NON_ANIMATED_TRANSITION }; } diff --git a/tns-core-modules/ui/page/page.ios.ts b/tns-core-modules/ui/page/page.ios.ts index 508e4c392..1650fc04b 100644 --- a/tns-core-modules/ui/page/page.ios.ts +++ b/tns-core-modules/ui/page/page.ios.ts @@ -1,5 +1,5 @@ // Definitions. -import { Frame } from "../frame"; +import { Frame, BackstackEntry } from "../frame"; import { NavigationType } from "../frame/frame-common"; // Types. @@ -15,6 +15,8 @@ export * from "./page-common"; const ENTRY = "_entry"; const DELEGATE = "_delegate"; +const TRANSITION = "_transition"; +const NON_ANIMATED_TRANSITION = "non-animated"; const majorVersion = iosUtils.MajorVersion; @@ -130,7 +132,7 @@ class UIViewControllerImpl extends UIViewController { const frame = navigationController ? (navigationController).owner : null; // Skip navigation events if modal page is shown. if (!owner._presentedViewController && frame) { - const newEntry = this[ENTRY]; + const newEntry: BackstackEntry = this[ENTRY]; let isBack: boolean; let navType = frame.navigationType; @@ -146,6 +148,18 @@ class UIViewControllerImpl extends UIViewController { } frame.setCurrent(newEntry, navType); + + if (frame.navigationType === NavigationType.replace) { + let controller = newEntry.resolvedPage.ios; + if (controller) { + if (newEntry.entry.animated) { + controller[TRANSITION] = frame._getNavigationTransition(newEntry.entry); + } else { + controller[TRANSITION] = { name: NON_ANIMATED_TRANSITION }; + } + } + } + frame.navigationType = isBack ? NavigationType.back : NavigationType.forward; // If page was shown with custom animation - we need to set the navigationController.delegate to the animatedDelegate.