Skip to content

Commit b5b8d51

Browse files
author
Alexander Vakrilov
authored
fix(modal): parent page invalid hierarchy handling [extended] (#5966)
* fix(modal): parent page invalid hierarchy handling * refactor(modals): Refactor safe guard in show/hide modal
1 parent 4b5754a commit b5b8d51

5 files changed

Lines changed: 51 additions & 19 deletions

File tree

‎tns-core-modules/ui/core/bindable/bindable.d.ts‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,19 @@
44

55
import { ViewBase } from "../view-base";
66
import { Observable, WrappedValue, PropertyChangeData, EventData } from "../../../data/observable";
7-
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories, notifyEvent as traceNotifyEvent, messageType as traceMessageType, isCategorySet } from "../../../trace";
7+
import {
8+
isEnabled as traceEnabled,
9+
write as traceWrite,
10+
error as traceError,
11+
categories as traceCategories,
12+
notifyEvent as traceNotifyEvent,
13+
messageType as traceMessageType,
14+
isCategorySet } from "../../../trace";
815

916
export {
1017
Observable, WrappedValue, PropertyChangeData, EventData,
11-
traceEnabled, traceWrite, traceCategories, traceNotifyEvent, traceMessageType, isCategorySet
18+
traceEnabled, traceWrite, traceError, traceCategories, traceNotifyEvent,
19+
traceMessageType, isCategorySet
1220
};
1321

1422
/**

‎tns-core-modules/ui/core/bindable/bindable.ts‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,23 @@ import { Observable, WrappedValue, PropertyChangeData, EventData } from "../../.
66
import { addWeakEventListener, removeWeakEventListener } from "../weak-event-listener";
77
import { bindingConstants, parentsRegex } from "../../builder/binding-builder";
88
import { escapeRegexSymbols } from "../../../utils/utils";
9-
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories, notifyEvent as traceNotifyEvent, isCategorySet, messageType as traceMessageType } from "../../../trace";
9+
import {
10+
isEnabled as traceEnabled,
11+
write as traceWrite,
12+
error as traceError,
13+
categories as traceCategories,
14+
notifyEvent as traceNotifyEvent,
15+
isCategorySet,
16+
messageType as traceMessageType
17+
} from "../../../trace";
1018
import * as types from "../../../utils/types";
1119

1220
import * as applicationCommon from "../../../application/application-common";
1321
import * as polymerExpressions from "../../../js-libs/polymer-expressions";
1422

1523
export {
1624
Observable, WrappedValue, PropertyChangeData, EventData,
17-
traceEnabled, traceWrite, traceCategories, traceNotifyEvent, traceMessageType, isCategorySet
25+
traceEnabled, traceWrite, traceError, traceCategories, traceNotifyEvent, traceMessageType, isCategorySet
1826
};
1927

2028
const contextKey = "context";

‎tns-core-modules/ui/core/view/view.d.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,11 @@ export const isEnabledProperty: Property<View, boolean>;
775775
export const isUserInteractionEnabledProperty: Property<View, boolean>;
776776

777777
export namespace ios {
778-
export function getParentWithViewController(parent: View): View
778+
/**
779+
* Returns a view with viewController or undefined if no such found along the view's parent chain.
780+
* @param view The view form which to start the search.
781+
*/
782+
export function getParentWithViewController(view: View): View
779783
export function isContentScrollable(controller: any /* UIViewController */, owner: View): boolean
780784
export function updateAutoAdjustScrollInsets(controller: any /* UIViewController */, owner: View): void
781785
export function updateConstraints(controller: any /* UIViewController */, owner: View): void;

‎tns-core-modules/ui/core/view/view.ios.ts‎

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ViewBase } from "../view-base";
44

55
import {
66
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
7-
traceEnabled, traceWrite, traceCategories, traceMessageType
7+
traceEnabled, traceWrite, traceCategories, traceError, traceMessageType
88
} from "./view-common";
99

1010
import { ios as iosBackground, Background } from "../../styling/background";
@@ -309,7 +309,19 @@ export class View extends ViewCommon {
309309
}
310310

311311
protected _showNativeModalView(parent: View, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean) {
312-
let parentWithController = ios.getParentWithViewController(parent);
312+
const parentWithController = ios.getParentWithViewController(parent);
313+
if (!parentWithController) {
314+
traceWrite(`Could not find parent with viewController for ${parent} while showing modal view.`,
315+
traceCategories.ViewHierarchy, traceMessageType.error)
316+
return;
317+
}
318+
319+
const parentController = parentWithController.viewController;
320+
if (!parentController.view || !parentController.view.window) {
321+
traceWrite("Parent page is not part of the window hierarchy. Close the current modal page before showing another one!",
322+
traceCategories.ViewHierarchy, traceMessageType.error);
323+
return;
324+
}
313325

314326
super._showNativeModalView(parentWithController, context, closeCallback, fullscreen, stretched);
315327
let controller = this.viewController;
@@ -326,11 +338,6 @@ export class View extends ViewCommon {
326338

327339
this._setupAsRootView({});
328340

329-
const parentController = parentWithController.viewController;
330-
if (!parentController.view.window) {
331-
throw new Error("Parent page is not part of the window hierarchy. Close the current modal page before showing another one!");
332-
}
333-
334341
if (fullscreen) {
335342
controller.modalPresentationStyle = UIModalPresentationStyle.FullScreen;
336343
} else {
@@ -346,7 +353,8 @@ export class View extends ViewCommon {
346353
parentController.presentViewControllerAnimatedCompletion(controller, animated, null);
347354
const transitionCoordinator = iosUtils.getter(parentController, parentController.transitionCoordinator);
348355
if (transitionCoordinator) {
349-
UIViewControllerTransitionCoordinator.prototype.animateAlongsideTransitionCompletion.call(transitionCoordinator, null, () => this._raiseShownModallyEvent());
356+
UIViewControllerTransitionCoordinator.prototype.animateAlongsideTransitionCompletion
357+
.call(transitionCoordinator, null, () => this._raiseShownModallyEvent());
350358
} else {
351359
// Apparently iOS 9+ stops all transitions and animations upon application suspend and transitionCoordinator becomes null here in this case.
352360
// Since we are not waiting for any transition to complete, i.e. transitionCoordinator is null, we can directly raise our shownModally event.
@@ -356,6 +364,11 @@ export class View extends ViewCommon {
356364
}
357365

358366
protected _hideNativeModalView(parent: View) {
367+
if (!parent || !parent.viewController) {
368+
traceError("Trying to hide modal view but no parent with viewController specified.")
369+
return;
370+
}
371+
359372
const parentController = parent.viewController;
360373
const animated = (<any>this.viewController).animated;
361374

@@ -582,14 +595,12 @@ export class CustomLayoutView extends View {
582595
}
583596

584597
export namespace ios {
585-
export function getParentWithViewController(parent: View): View {
586-
let view = parent;
587-
let controller = view.viewController;
588-
while (!controller) {
598+
export function getParentWithViewController(view: View): View {
599+
while (view && !view.viewController) {
589600
view = view.parent as View;
590-
controller = view.viewController;
591601
}
592602

603+
// Note: Might return undefined if no parent with viewController is found
593604
return view;
594605
}
595606

‎tns-core-modules/ui/dialogs/dialogs.ios.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ function showUIAlertController(alertController: UIAlertController) {
209209
if (view.ios instanceof UIViewController) {
210210
viewController = view.ios;
211211
} else {
212-
viewController = iosView.getParentWithViewController(view).viewController;
212+
const parentWithController = iosView.getParentWithViewController(view);
213+
viewController = parentWithController ? parentWithController.viewController : undefined;
213214
}
214215
}
215216

0 commit comments

Comments
 (0)