Skip to content

Commit af651d6

Browse files
ADjenkovMartoYankov
authored andcommitted
feat(frame): hardware back in parent frame when back states available (NativeScript#6446)
1 parent 9f7df18 commit af651d6

File tree

4 files changed

+94
-6
lines changed

4 files changed

+94
-6
lines changed

tests/app/navigation/navigation-tests.ts

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import * as TKUnit from "../TKUnit";
22
import { EventData, Page, NavigatedData } from "tns-core-modules/ui/page";
33
import { topmost as topmostFrame, NavigationTransition } from "tns-core-modules/ui/frame";
4+
import { StackLayout, } from "tns-core-modules/ui/layouts/stack-layout";
5+
import { GridLayout, } from "tns-core-modules/ui/layouts/grid-layout";
46
import { Color } from "tns-core-modules/color";
57
import * as helper from "../ui/helper";
6-
8+
import * as frame from "tns-core-modules/ui/frame";
79
// Creates a random colorful page full of meaningless stuff.
810
let id = 0;
911
let pageFactory = function (): Page {
@@ -51,6 +53,65 @@ export function test_backstackVisible_WithTransition() {
5153
_test_backstackVisible({ name: "fade", duration: 10 });
5254
}
5355

56+
export function test_backAndForwardParentPage_nestedFrames() {
57+
const topmost = topmostFrame();
58+
const mainTestPage = topmost.currentPage;
59+
let innerFrame;
60+
61+
const page = (title) => {
62+
const p = new Page();
63+
p["tag"] = title;
64+
return p;
65+
};
66+
67+
const parentPage = (title, innerPage) => {
68+
const parentPage = new Page();
69+
parentPage["tag"] = title;
70+
71+
const stack = new StackLayout();
72+
innerFrame = new frame.Frame();
73+
innerFrame.navigate({ create: () => innerPage });
74+
stack.addChild(innerFrame);
75+
parentPage.content = stack;
76+
77+
return parentPage;
78+
}
79+
80+
const back = pages => topmostFrame().goBack(topmostFrame().backStack[topmostFrame().backStack.length - pages]);
81+
const currentPageMustBe = tag => TKUnit.assertEqual(topmostFrame().currentPage["tag"], tag, "Expected current page to be " + tag + " it was " + topmostFrame().currentPage["tag"] + " instead.");
82+
83+
let parentPage1, parentPage2, innerPage1, innerPage2;
84+
innerPage1 = page("InnerPage1");
85+
innerPage2 = page("InnerPage2");
86+
parentPage1 = page("ParentPage1");
87+
parentPage2 = parentPage("ParentPage2", innerPage1);
88+
89+
helper.waitUntilNavigatedTo(parentPage1, () => topmost.navigate({ create: () => parentPage1 }));
90+
currentPageMustBe("ParentPage1");
91+
92+
helper.waitUntilNavigatedTo(parentPage2, () => topmost.navigate({ create: () => parentPage2 }));
93+
currentPageMustBe("ParentPage2");
94+
95+
helper.waitUntilNavigatedTo(innerPage2, () => innerFrame.navigate({ create: () => innerPage2 }));
96+
currentPageMustBe("InnerPage2");
97+
98+
helper.waitUntilNavigatedTo(innerPage1, () => frame.goBack());
99+
currentPageMustBe("InnerPage1");
100+
101+
helper.waitUntilNavigatedTo(parentPage1, () => frame.goBack());
102+
currentPageMustBe("ParentPage1");
103+
104+
helper.waitUntilNavigatedTo(parentPage2, () => topmost.navigate({ create: () => parentPage2 }));
105+
currentPageMustBe("ParentPage2");
106+
107+
back(2);
108+
TKUnit.waitUntilReady(() => topmostFrame().navigationQueueIsEmpty());
109+
110+
const frameStack = frame.stack();
111+
TKUnit.assertEqual(frameStack.length, 1, "There should be only one frame left in the stack");
112+
TKUnit.assertEqual(topmostFrame().currentPage, mainTestPage, "We should be on the main test page at the end of the test.");
113+
}
114+
54115
function _test_backToEntry(transition?: NavigationTransition) {
55116
const topmost = topmostFrame();
56117
const page = (tag) => () => {
@@ -362,10 +423,10 @@ function _test_NavigationEvents_WithClearHistory(transition?: NavigationTransiti
362423
// Go to second page
363424
helper.navigateWithEntry({ create: secondPageFactory, transition: transition, animated: !!transition, clearHistory: true });
364425

365-
const expectedMainPageEvents = [ "main-page navigatingFrom forward", "main-page navigatedFrom forward" ];
426+
const expectedMainPageEvents = ["main-page navigatingFrom forward", "main-page navigatedFrom forward"];
366427
TKUnit.arrayAssert(actualMainPageEvents, expectedMainPageEvents, "Actual main-page events are different from expected.");
367428

368-
const expectedSecondPageEvents = [ "second-page navigatingTo forward", "second-page navigatedTo forward" ];
429+
const expectedSecondPageEvents = ["second-page navigatingTo forward", "second-page navigatedTo forward"];
369430
TKUnit.arrayAssert(actualSecondPageEvents, expectedSecondPageEvents, "Actual main-page events are different from expected.");
370431

371432
TKUnit.assertEqual(topmost.currentPage, secondPage, "We should be on the second page at the end of the test.");
@@ -393,7 +454,7 @@ function _test_Navigate_From_Page_Event_Handler(eventName: string) {
393454
const firstPageFactory = function (): Page {
394455
const firstPage = new Page();
395456
firstPage.id = "first-page";
396-
firstPage.on(eventName, (args: EventData) => {
457+
firstPage.on(eventName, (args: EventData) => {
397458
const page = <Page>args.object;
398459
const frame = page.frame;
399460

tns-core-modules/ui/frame/frame-common.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Frame as FrameDefinition, NavigationEntry, BackstackEntry, NavigationTr
33
import { Page } from "../page";
44

55
// Types.
6+
import { getAncestor } from "../core/view/view-common";
67
import { View, CustomLayoutView, isIOS, isAndroid, traceEnabled, traceWrite, traceCategories, Property, CSSType } from "../core/view";
78
import { createViewFromEntry } from "../builder";
89
import { profile } from "../../profiling";
@@ -215,6 +216,10 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
215216

216217
this._currentEntry = entry;
217218

219+
if (isBack) {
220+
this._pushInFrameStack();
221+
}
222+
218223
newPage.onNavigatedTo(isBack);
219224

220225
// Reset executing entry after NavigatedTo is raised;
@@ -573,6 +578,22 @@ export function goBack(): boolean {
573578
if (top && top.canGoBack()) {
574579
top.goBack();
575580
return true;
581+
} else if (top) {
582+
let parentFrameCanGoBack = false;
583+
let parentFrame = <FrameBase>getAncestor(top, "Frame");
584+
585+
while (parentFrame && !parentFrameCanGoBack) {
586+
if (parentFrame && parentFrame.canGoBack()) {
587+
parentFrameCanGoBack = true;
588+
} else {
589+
parentFrame = <FrameBase>getAncestor(top, "Frame");
590+
}
591+
}
592+
593+
if (parentFrame && parentFrameCanGoBack) {
594+
parentFrame.goBack();
595+
return true;
596+
}
576597
}
577598

578599
if (frameStack.length > 1) {

tns-core-modules/ui/frame/frame.android.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,8 @@ export class Frame extends FrameBase {
204204
// however, we must add a fragment.isAdded() guard as our logic will try to
205205
// explicitly remove the already removed child fragment causing an
206206
// IllegalStateException: Fragment has not been attached yet.
207-
if (!this._currentEntry ||
208-
!this._currentEntry.fragment ||
207+
if (!this._currentEntry ||
208+
!this._currentEntry.fragment ||
209209
!this._currentEntry.fragment.isAdded()) {
210210
return;
211211
}
@@ -423,6 +423,7 @@ export class Frame extends FrameBase {
423423
}
424424

425425
this._android.rootViewGroup = null;
426+
this._removeFromFrameStack();
426427
super.disposeNativeView();
427428
}
428429

tns-core-modules/ui/frame/frame.ios.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ export class Frame extends FrameBase {
3636
return this.viewController.view;
3737
}
3838

39+
public disposeNativeView() {
40+
this._removeFromFrameStack();
41+
super.disposeNativeView();
42+
}
43+
3944
public get ios(): iOSFrame {
4045
return this._ios;
4146
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy