diff --git a/tns-core-modules/application/application.ios.ts b/tns-core-modules/application/application.ios.ts index 3d38f5889..e1d0be781 100644 --- a/tns-core-modules/application/application.ios.ts +++ b/tns-core-modules/application/application.ios.ts @@ -10,7 +10,6 @@ import { notify, launchEvent, resumeEvent, suspendEvent, exitEvent, lowMemoryEvent, orientationChangedEvent, setApplication, livesync, displayedEvent, getCssFileName } from "./application-common"; -import { ModuleType } from "../ui/core/view/view-common"; // First reexport so that app module is initialized. export * from "./application-common"; @@ -231,7 +230,7 @@ class IOSApplication implements IOSApplicationDefinition { public _onLivesync(context?: ModuleContext): void { // Handle application root module - const isAppRootModuleChanged = context && context.path && context.path.includes(getMainEntry().moduleName) && context.type !== ModuleType.style; + const isAppRootModuleChanged = context && context.path && context.path.includes(getMainEntry().moduleName) && context.type !== "style"; // Set window content when: // + Application root module is changed diff --git a/tns-core-modules/module.d.ts b/tns-core-modules/module.d.ts index 573831c1b..f65cd0310 100644 --- a/tns-core-modules/module.d.ts +++ b/tns-core-modules/module.d.ts @@ -66,11 +66,7 @@ declare function clearTimeout(timeoutId: number): void; declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): number; declare function clearInterval(intervalId: number): void; -declare enum ModuleType { - markup = "markup", - script = "script", - style = "style" -} +declare type ModuleType = "markup" | "script" | "style"; /** * Define a module context for Hot Module Replacement. diff --git a/tns-core-modules/ui/core/view/view-common.ts b/tns-core-modules/ui/core/view/view-common.ts index 992f135c0..f97f759b9 100644 --- a/tns-core-modules/ui/core/view/view-common.ts +++ b/tns-core-modules/ui/core/view/view-common.ts @@ -37,18 +37,25 @@ function ensureAnimationModule() { } } -export enum ModuleType { - markup = "markup", - script = "script", - style = "style" -} - export function CSSType(type: string): ClassDecorator { return (cls) => { cls.prototype.cssType = type; }; } +export function viewMatchesModuleContext( + view: ViewDefinition, + context: ModuleContext, + types: ModuleType[]): boolean { + + return context && + view._moduleName && + context.type && + types.some(type => type === context.type) && + context.path && + context.path.includes(view._moduleName); +} + export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator { const stateEventNames = pseudoClasses.map(s => ":" + s); const listeners = Symbol("listeners"); @@ -147,62 +154,36 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition { traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync); } - _rootModalViews.forEach(v => v.closeModal()); - _rootModalViews.length = 0; - - if (context && context.type && context.path) { - // Handle local styles - if (context.type === ModuleType.style) { - return this._changeLocalStyles(context.path); - } - // Handle module markup and script changes - else { - return this.changeModule(context); - } - } - - return false; - } - - public _changeLocalStyles(contextPath: string): boolean { - if (!this.changeStyles(this, contextPath)) { - eachDescendant(this, (child: ViewBase) => { - this.changeStyles(child, contextPath); - return true; - }); - } - - // Do not reset activity/window content for local styles changes - return true; - } - - private changeStyles(view: ViewBase, contextPath: string): boolean { - if (traceEnabled()) { - traceWrite(`${view}.${view._moduleName}`, traceCategories.Livesync); - } - - if (view._moduleName && contextPath.includes(view._moduleName)) { - (view).changeCssFile(contextPath); + if (this._handleLivesync(context)) { return true; } - return false; - } - private changeModule(context: ModuleContext): boolean { - eachDescendant(this, (child: ViewBase) => { - if (traceEnabled()) { - traceWrite(`${child}.${child._moduleName}`, traceCategories.Livesync); + let handled = false; + this.eachChildView((child) => { + if (child._onLivesync(context)) { + handled = true; + return false; } - - // Handle changes in module's Page - if (child._moduleName && context.path.includes(child._moduleName) && child.page) { - child.page._onLivesync(context); - } - return true; }); + return handled; + } - // Do not reset activity/window content for module changes - return true; + public _handleLivesync(context?: ModuleContext): boolean { + if (traceEnabled()) { + traceWrite(`${this}._handleLivesync(${JSON.stringify(context)})`, traceCategories.Livesync); + } + + // Handle local CSS + if (viewMatchesModuleContext(this, context, ["style"])) { + if (traceEnabled()) { + traceWrite(`Change Handled: Changing CSS for ${this}`, traceCategories.Livesync); + } + + this.changeCssFile(context.path); + return true; + } + + return false; } _setupAsRootView(context: any): void { diff --git a/tns-core-modules/ui/core/view/view.d.ts b/tns-core-modules/ui/core/view/view.d.ts index f2be87694..9b9fb84d9 100644 --- a/tns-core-modules/ui/core/view/view.d.ts +++ b/tns-core-modules/ui/core/view/view.d.ts @@ -32,6 +32,17 @@ export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator; */ export function CSSType(type: string): ClassDecorator; +/** + * + * @param view The view + * @param context The ModuleType + * @param type Type of the ModuleType to be matched + */ +export function viewMatchesModuleContext( + view: View, + context: ModuleContext, + type: ModuleType[]): boolean; + /** * Denotes a length number that is in device independent pixel units. */ @@ -686,7 +697,7 @@ export abstract class View extends ViewBase { /** * @private */ - _changeLocalStyles(contextPath: string): boolean; + _handleLivesync(context?: { type: string, path: string }): boolean; /** * @private */ diff --git a/tns-core-modules/ui/frame/frame-common.ts b/tns-core-modules/ui/frame/frame-common.ts index 1346385e0..ef7fbd470 100644 --- a/tns-core-modules/ui/frame/frame-common.ts +++ b/tns-core-modules/ui/frame/frame-common.ts @@ -3,12 +3,13 @@ import { Frame as FrameDefinition, NavigationEntry, BackstackEntry, NavigationTr import { Page } from "../page"; // Types. -import { getAncestor } from "../core/view/view-common"; +import { getAncestor, viewMatchesModuleContext } from "../core/view/view-common"; import { View, CustomLayoutView, isIOS, isAndroid, traceEnabled, traceWrite, traceCategories, Property, CSSType } from "../core/view"; import { createViewFromEntry } from "../builder"; import { profile } from "../../profiling"; import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _popFromFrameStack, _removeFromFrameStack } from "./frame-stack"; +import { getModuleName } from "../../utils/utils"; export * from "../core/view"; export enum NavigationType { @@ -574,7 +575,38 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { return result; } - public _onLivesync(): boolean { + public _onLivesync(context?: ModuleContext): boolean { + if (super._onLivesync(context)) { + return true; + } + + // Fallback + if (!context) { + return this.legacyLivesync(); + } + + return false; + } + + public _handleLivesync(context?: ModuleContext): boolean { + if (super._handleLivesync(context)) { + return true; + } + + // Handle markup/script changes in currentPage + if (this.currentPage && + viewMatchesModuleContext(this.currentPage, context, ["markup", "script"])) { + + traceWrite(`Change Handled: Replacing page ${context.path}`, traceCategories.Livesync); + + this.replacePage(context); + return true; + } + + return false; + } + + private legacyLivesync(): boolean { // Reset activity/window content when: // + Changes are not handled on View // + There is no ModuleContext @@ -609,6 +641,27 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { this.navigate(newEntry); return true; } + + 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); + + const newPage = 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); + } + } 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 3f21d8b9a..7151448e3 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -3,7 +3,6 @@ import { AndroidFrame as AndroidFrameDefinition, AndroidActivityCallbacks, AndroidFragmentCallbacks, BackstackEntry, NavigationTransition } from "."; -import { ModuleType } from "../../ui/core/view/view-common"; import { Page } from "../page"; // Types. @@ -90,7 +89,7 @@ export function reloadPage(context?: ModuleContext): void { if (callbacks) { const rootView: View = callbacks.getRootView(); // Handle application root module - const isAppRootModuleChanged = context && context.path && context.path.includes(application.getMainEntry().moduleName) && context.type !== ModuleType.style; + const isAppRootModuleChanged = context && context.path && context.path.includes(application.getMainEntry().moduleName) && context.type !== "style"; // Reset activity content when: // + Application root module is changed @@ -339,44 +338,6 @@ export class Frame extends FrameBase { return false; } - public _onLivesync(context?: ModuleContext): boolean { - if (traceEnabled()) { - traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync); - } - - if (!this._currentEntry || !this._currentEntry.entry) { - return false; - } - - if (context && context.type && context.path) { - // Handle local styls changes when app root is Frame - if (context.type === ModuleType.style) { - return this._changeLocalStyles(context.path); - } - - // Set NavigationType.replace for HMR. - this.navigationType = NavigationType.replace; - const currentBackstackEntry = this._currentEntry; - const contextModuleName = getModuleName(context.path); - - const newPage = createViewFromEntry({ moduleName: contextModuleName }); - const newBackstackEntry: BackstackEntry = { - entry: currentBackstackEntry.entry, - resolvedPage: newPage, - navDepth: currentBackstackEntry.navDepth, - fragmentTag: currentBackstackEntry.fragmentTag, - frameId: currentBackstackEntry.frameId - }; - - const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false }; - this.performNavigation(navContext); - return true; - } else { - // Fallback - return super._onLivesync(); - } - } - @profile public _navigateCore(newEntry: BackstackEntry) { super._navigateCore(newEntry); diff --git a/tns-core-modules/ui/frame/frame.ios.ts b/tns-core-modules/ui/frame/frame.ios.ts index 6bc3281aa..a9a985b0c 100644 --- a/tns-core-modules/ui/frame/frame.ios.ts +++ b/tns-core-modules/ui/frame/frame.ios.ts @@ -3,7 +3,6 @@ import { iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition } from "."; import { Page } from "../page"; -import { ModuleType } from "../../ui/core/view/view-common"; import { profile } from "../../profiling"; //Types. @@ -64,44 +63,6 @@ export class Frame extends FrameBase { } } - 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) { - // Handle local styls changes when app root is Frame - if (context.type === ModuleType.style) { - return this._changeLocalStyles(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 = createViewFromEntry({ moduleName: contextModuleName }); - const newBackstackEntry: BackstackEntry = { - entry: currentBackstackEntry.entry, - resolvedPage: newPage, - navDepth: currentBackstackEntry.navDepth, - fragmentTag: undefined - } - - const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false }; - this.performNavigation(navContext); - return true; - } else { - // Fallback - return super._onLivesync(); - } - } - @profile public _navigateCore(backstackEntry: BackstackEntry) { // NavigationType.replace for HMR. diff --git a/tns-core-modules/ui/page/page-common.ts b/tns-core-modules/ui/page/page-common.ts index f8c152c71..689781083 100644 --- a/tns-core-modules/ui/page/page-common.ts +++ b/tns-core-modules/ui/page/page-common.ts @@ -99,10 +99,6 @@ export class PageBase extends ContentView implements PageDefinition { }; } - public _onLivesync(context?: ModuleContext): boolean { - return this.frame ? this.frame._onLivesync(context) : false; - } - @profile public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) { this._navigationContext = context;