mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-26 11:17:04 +08:00
feat(modals): Enable modal dialog chaining in IOS (#6637)
* feat(modals): fire close callback after close in IOS * chore(tests): Fix some test depending on the order of events
This commit is contained in:

committed by
Dimitar Topuzov

parent
bc68773bd2
commit
64bccb9bbc
@ -1,32 +1,29 @@
|
||||
import * as pages from "tns-core-modules/ui/page";
|
||||
import * as textField from "tns-core-modules/ui/text-field";
|
||||
import * as observable from "tns-core-modules/data/observable";
|
||||
import { Page, ShownModallyData } from "tns-core-modules/ui/page";
|
||||
import { EventData, fromObject } from "tns-core-modules/data/observable";
|
||||
|
||||
var context: any;
|
||||
var closeCallback: Function;
|
||||
export function onShowingModally(args: ShownModallyData) {
|
||||
console.log("login-page.onShowingModally, context: " + args.context);
|
||||
const page = <Page>args.object;
|
||||
|
||||
var page: pages.Page;
|
||||
var usernameTextField: textField.TextField;
|
||||
var passwordTextField: textField.TextField;
|
||||
|
||||
export function onShownModally(args: pages.ShownModallyData) {
|
||||
console.log("login-page.onShownModally, context: " + args.context);
|
||||
context = args.context;
|
||||
closeCallback = args.closeCallback;
|
||||
page.bindingContext = fromObject({
|
||||
username: "username",
|
||||
password: "password",
|
||||
context: args.context,
|
||||
onLoginButtonTap: function() {
|
||||
console.log("login-page.onLoginButtonTap");
|
||||
args.closeCallback(this.username, this.password);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function onLoaded(args: observable.EventData) {
|
||||
export function onShownModally(args: ShownModallyData) {
|
||||
console.log("login-page.onShownModally, context: " + args.context);
|
||||
}
|
||||
|
||||
export function onLoaded(args: EventData) {
|
||||
console.log("login-page.onLoaded");
|
||||
page = <pages.Page>args.object;
|
||||
usernameTextField = page.getViewById<textField.TextField>("username");
|
||||
passwordTextField = page.getViewById<textField.TextField>("password");
|
||||
}
|
||||
|
||||
export function onUnloaded() {
|
||||
console.log("login-page.onUnloaded");
|
||||
}
|
||||
|
||||
export function onLoginButtonTap() {
|
||||
console.log("login-page.onLoginButtonTap");
|
||||
closeCallback(usernameTextField.text, passwordTextField.text);
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
<Page xmlns="http://schemas.nativescript.org/tns.xsd" shownModally="onShownModally"
|
||||
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
|
||||
showingModally="onShowingModally"
|
||||
shownModally="onShownModally"
|
||||
loaded="onLoaded" unloaded="onUnloaded" backgroundColor="Red">
|
||||
<StackLayout backgroundColor="PaleGreen" margin="10">
|
||||
<TextField hint="username" id="username" text="username"/>
|
||||
<TextField hint="password" id="password" text="password" secure="true"/>
|
||||
<Button text="Login" tap="onLoginButtonTap"/>
|
||||
<Label text="{{ context }}"/>
|
||||
<TextField hint="username" text="{{ username }}"/>
|
||||
<TextField hint="password" text="{{ password }}" secure="true"/>
|
||||
<Button text="Login" tap="{{ onLoginButtonTap }}"/>
|
||||
</StackLayout>
|
||||
</Page>
|
@ -22,3 +22,33 @@ export function onTapStretched(args) {
|
||||
label.text = username + "/" + password;
|
||||
}, fullscreen, false, stretched);
|
||||
}
|
||||
|
||||
function openModal(page: Page, label: Label, context: string) {
|
||||
page.showModal("ui-tests-app/modal-view/login-page", context, function (username: string, password: string) {
|
||||
const result = context + "/" + username + "/" + password;
|
||||
console.log(result);
|
||||
label.text = result;
|
||||
}, false);
|
||||
}
|
||||
|
||||
export function onTapSecondModalInCB(args) {
|
||||
const page = <Page>args.object.page;
|
||||
const label = page.getViewById<Label>("label");
|
||||
page.showModal("ui-tests-app/modal-view/login-page", "First", function (username: string, password: string) {
|
||||
const result = "First/" + username + "/" + password;
|
||||
console.log(result);
|
||||
label.text = result;
|
||||
|
||||
// Open second modal in the close callback of the first one.
|
||||
openModal(page, label, "Second");
|
||||
});
|
||||
}
|
||||
|
||||
export function onTapSecondModalInTimer(args) {
|
||||
const page = <Page>args.object.page;
|
||||
const label = page.getViewById<Label>("label");
|
||||
openModal(page, label, "First");
|
||||
|
||||
// Open second modal 1s after the first one.
|
||||
setTimeout(() => openModal(page, label, "Second"), 1000);
|
||||
}
|
||||
|
@ -3,6 +3,10 @@
|
||||
<Button text="Login (pop-up)" tap="onTap" />
|
||||
<Button text="Login (full-screen)" tap="onTap" />
|
||||
<Button text="Login (pop-up-stretched)" tap="onTapStretched" />
|
||||
|
||||
<Button text="Login (second modal in cb)" tap="onTapSecondModalInCB" />
|
||||
<Button text="Login (second modal in timer)" tap="onTapSecondModalInTimer" />
|
||||
|
||||
<Label id="label" text="Anonymous"/>
|
||||
</StackLayout>
|
||||
</Page>
|
@ -738,6 +738,7 @@ export function test_WhenPageIsNavigatedToItCanShowAnotherPageAsModal() {
|
||||
TKUnit.assertTrue(ctx.shownModally, "Modal-page must be shown!");
|
||||
TKUnit.assertEqual(returnValue, "return value", "Modal-page must return value!");
|
||||
modalClosed = true;
|
||||
TKUnit.assertNull(masterPage.modal, "currentPage.modal should be undefined when no modal page is shown!");
|
||||
}
|
||||
|
||||
let modalPage: Page;
|
||||
@ -758,7 +759,6 @@ export function test_WhenPageIsNavigatedToItCanShowAnotherPageAsModal() {
|
||||
const onModalUnloaded = function (args: EventData) {
|
||||
modalUnloaded++;
|
||||
modalPage.off(Page.unloadedEvent, onModalUnloaded);
|
||||
TKUnit.assertNull(masterPage.modal, "currentPage.modal should be undefined when no modal page is shown!");
|
||||
}
|
||||
|
||||
const navigatedToEventHandler = function (args) {
|
||||
|
@ -37,7 +37,6 @@ export function test_WhenShowingModalPageUnloadedIsNotFiredForTheMasterPage() {
|
||||
let onModalUnloaded = function (args: EventData) {
|
||||
modalUnloaded++;
|
||||
modalPage.off(Page.unloadedEvent, onModalUnloaded);
|
||||
TKUnit.assertNull(masterPage.modal, "currentPage.modal should be undefined when no modal page is shown!");
|
||||
}
|
||||
|
||||
var navigatedToEventHandler = function (args) {
|
||||
|
@ -225,10 +225,10 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
const animated = arguments[4];
|
||||
const stretched = arguments[5];
|
||||
|
||||
const view: ViewDefinition = firstAgrument instanceof ViewCommon
|
||||
? firstAgrument : createViewFromEntry({ moduleName: firstAgrument });
|
||||
const view = firstAgrument instanceof ViewCommon
|
||||
? firstAgrument : <ViewCommon>createViewFromEntry({ moduleName: firstAgrument });
|
||||
|
||||
(<ViewCommon>view)._showNativeModalView(this, context, closeCallback, fullscreen, animated, stretched);
|
||||
view._showNativeModalView(this, context, closeCallback, fullscreen, animated, stretched);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
@ -256,27 +256,29 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
this._modalParent = parent;
|
||||
this._modalContext = context;
|
||||
const that = this;
|
||||
this._closeModalCallback = function () {
|
||||
this._closeModalCallback = function (...originalArgs) {
|
||||
if (that._closeModalCallback) {
|
||||
const modalIndex = _rootModalViews.indexOf(that);
|
||||
_rootModalViews.splice(modalIndex);
|
||||
that._hideNativeModalView(parent);
|
||||
that._modalParent = null;
|
||||
that._modalContext = null;
|
||||
that._closeModalCallback = null;
|
||||
that._dialogClosed();
|
||||
parent._modal = null;
|
||||
|
||||
if (typeof closeCallback === "function") {
|
||||
closeCallback.apply(undefined, arguments);
|
||||
const whenClosedCallback = () => {
|
||||
that._modalParent = null;
|
||||
that._modalContext = null;
|
||||
that._closeModalCallback = null;
|
||||
that._dialogClosed();
|
||||
parent._modal = null;
|
||||
|
||||
if (typeof closeCallback === "function") {
|
||||
closeCallback.apply(undefined, originalArgs);
|
||||
}
|
||||
}
|
||||
|
||||
that._hideNativeModalView(parent, whenClosedCallback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected _hideNativeModalView(parent: ViewCommon) {
|
||||
//
|
||||
}
|
||||
protected abstract _hideNativeModalView(parent: ViewCommon, whenClosedCallback: () => void);
|
||||
|
||||
protected _raiseLayoutChangedEvent() {
|
||||
const args: EventData = {
|
||||
|
@ -613,14 +613,14 @@ export class View extends ViewCommon {
|
||||
this._dialogFragment.show(parent._getRootFragmentManager(), this._domId.toString());
|
||||
}
|
||||
|
||||
protected _hideNativeModalView(parent: View) {
|
||||
protected _hideNativeModalView(parent: View, whenClosedCallback: () => void) {
|
||||
const manager = this._dialogFragment.getFragmentManager();
|
||||
if (manager) {
|
||||
this._dialogFragment.dismissAllowingStateLoss();
|
||||
}
|
||||
|
||||
this._dialogFragment = null;
|
||||
super._hideNativeModalView(parent);
|
||||
whenClosedCallback();
|
||||
}
|
||||
|
||||
[isEnabledProperty.setNative](value: boolean) {
|
||||
|
@ -380,8 +380,14 @@ export class View extends ViewCommon {
|
||||
}
|
||||
|
||||
const parentController = parentWithController.viewController;
|
||||
if (parentController.presentedViewController) {
|
||||
traceWrite("Parent is already presenting view controller. Close the current modal page before showing another one!",
|
||||
traceCategories.ViewHierarchy, traceMessageType.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!parentController.view || !parentController.view.window) {
|
||||
traceWrite("Parent page is not part of the window hierarchy. Close the current modal page before showing another one!",
|
||||
traceWrite("Parent page is not part of the window hierarchy.",
|
||||
traceCategories.ViewHierarchy, traceMessageType.error);
|
||||
return;
|
||||
}
|
||||
@ -426,7 +432,7 @@ export class View extends ViewCommon {
|
||||
}
|
||||
}
|
||||
|
||||
protected _hideNativeModalView(parent: View) {
|
||||
protected _hideNativeModalView(parent: View, whenClosedCallback: () => void) {
|
||||
if (!parent || !parent.viewController) {
|
||||
traceError("Trying to hide modal view but no parent with viewController specified.")
|
||||
return;
|
||||
@ -435,8 +441,7 @@ export class View extends ViewCommon {
|
||||
const parentController = parent.viewController;
|
||||
const animated = (<any>this.viewController).animated;
|
||||
|
||||
super._hideNativeModalView(parent);
|
||||
parentController.dismissModalViewControllerAnimated(animated);
|
||||
parentController.dismissViewControllerAnimatedCompletion(animated, whenClosedCallback);
|
||||
}
|
||||
|
||||
[isEnabledProperty.getDefault](): boolean {
|
||||
|
Reference in New Issue
Block a user