feat(frame): rework frame retrieval api (#5527)

Rework the frame api to support working with multiple Frames.
* frameModule.topmost() - now returns the last navigated Frame or
the currently selected tab item's Frame if the tab item's view is a
Frame.
* frameModule.getFrameById(id: string) - returns a navigated Frame by id.
* args.object.page.frame - can be used in page elements event handlers.
Returns the Frame of the current element's page.
* chore: Update madge-android npm script path
This commit is contained in:
Martin Yankov
2018-03-13 18:10:45 +02:00
committed by GitHub
parent 45bf5b8f57
commit dfa70dd384
7 changed files with 81 additions and 8 deletions

View File

@ -84,7 +84,7 @@
"tslint": "tslint --config build/tslint.json 'tns-core-modules/**/*.ts' 'tests/**/*.ts' 'apps/**/*.ts' 'e2e/**/*.ts' -e '**/node_modules/**' -e '**/platforms/**'", "tslint": "tslint --config build/tslint.json 'tns-core-modules/**/*.ts' 'tests/**/*.ts' 'apps/**/*.ts' 'e2e/**/*.ts' -e '**/node_modules/**' -e '**/platforms/**'",
"madge-ios": "tsc --skipLibCheck && tns prepare ios --path tests && madge --circular tests/platforms/ios/tests/app/tns_modules/tns-core-modules", "madge-ios": "tsc --skipLibCheck && tns prepare ios --path tests && madge --circular tests/platforms/ios/tests/app/tns_modules/tns-core-modules",
"madge-ios-image": "tsc --skipLibCheck && tns prepare ios --path tests && madge --image graph-tests-ios.svg tests/platforms/ios/tests/app/tns_modules/tns-core-modules", "madge-ios-image": "tsc --skipLibCheck && tns prepare ios --path tests && madge --image graph-tests-ios.svg tests/platforms/ios/tests/app/tns_modules/tns-core-modules",
"madge-android": "tsc --skipLibCheck && tns prepare android --path tests && madge --circular tests/platforms/android/src/main/assets/app/tns_modules/tns-core-modules", "madge-android": "tsc --skipLibCheck && tns prepare android --path tests && madge --circular tests/platforms/android/app/src/main/assets/app/tns_modules/tns-core-modules",
"madge-android-image": "tsc --skipLibCheck && tns prepare android --path tests && madge --image graph-tests-android.svg tests/platforms/android/src/main/assets/app/tns_modules/tns-core-modules" "madge-android-image": "tsc --skipLibCheck && tns prepare android --path tests && madge --image graph-tests-android.svg tests/platforms/android/app/src/main/assets/app/tns_modules/tns-core-modules"
} }
} }

View File

@ -1,7 +1,8 @@
// >> frame-require // >> frame-require
import { topmost, NavigationEntry } from "tns-core-modules/ui/frame"; import { Frame, getFrameById, topmost, NavigationEntry } from "tns-core-modules/ui/frame";
// << frame-require // << frame-require
import { getRootView } from "tns-core-modules/application";
import { Label } from "tns-core-modules/ui/label"; import { Label } from "tns-core-modules/ui/label";
import { Page } from "tns-core-modules/ui/page"; import { Page } from "tns-core-modules/ui/page";
import * as helper from "../helper"; import * as helper from "../helper";
@ -223,4 +224,40 @@ export function test_page_parent_when_navigate_back() {
}); });
pages.length = 0; pages.length = 0;
}
export function test_frame_retrieval_API_when_navigating() {
const rootView = getRootView();
const initialFrame = new Frame();
initialFrame.id = "initialFrame";
initialFrame.navigate(() => new Page());
const initialTopmost = topmost();
const initialFrameById = getFrameById("initialFrame");
TKUnit.assertEqual(initialTopmost, initialFrame);
TKUnit.assertEqual(initialFrameById, initialFrame);
const newFrame = new Frame();
newFrame.id = "newFrame";
newFrame.navigate(() => new Page());
const newTopmost = topmost();
const newFrameById = getFrameById("newFrame");
TKUnit.assertEqual(newTopmost, newFrame);
TKUnit.assertEqual(newFrameById, newFrame);
initialFrame.navigate(() => new Page());
const previousTopmost = topmost();
const previousFrameById = getFrameById("initialFrame");
TKUnit.assertEqual(previousTopmost, initialFrame);
TKUnit.assertEqual(previousFrameById, initialFrame);
// clean up the frame stack
initialFrame._removeFromFrameStack();
newFrame._removeFromFrameStack();
} }

View File

@ -403,10 +403,15 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
} }
public _pushInFrameStack() { public _pushInFrameStack() {
if (this._isInFrameStack) { if (this._isInFrameStack && frameStack[frameStack.length - 1] === this) {
return; return;
} }
if (this._isInFrameStack) {
const indexOfFrame = frameStack.indexOf(this);
frameStack.splice(indexOfFrame, 1);
}
frameStack.push(this); frameStack.push(this);
this._isInFrameStack = true; this._isInFrameStack = true;
} }
@ -425,7 +430,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
this._isInFrameStack = false; this._isInFrameStack = false;
} }
private _removeFromFrameStack() { public _removeFromFrameStack() {
if (!this._isInFrameStack) { if (!this._isInFrameStack) {
return; return;
} }
@ -575,6 +580,10 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
} }
} }
export function getFrameById(id: string): FrameBase {
return frameStack.find((frame) => frame.id && frame.id === id);
}
export function topmost(): FrameBase { export function topmost(): FrameBase {
if (frameStack.length > 0) { if (frameStack.length > 0) {
return frameStack[frameStack.length - 1]; return frameStack[frameStack.length - 1];

View File

@ -597,7 +597,7 @@ function startActivity(activity: android.app.Activity, frameId: number) {
activity.startActivity(intent); activity.startActivity(intent);
} }
function getFrameById(frameId: number): Frame { function getFrameByNumberId(frameId: number): Frame {
// Find the frame for this activity. // Find the frame for this activity.
for (let i = 0; i < framesCache.length; i++) { for (let i = 0; i < framesCache.length; i++) {
let aliveFrame = framesCache[i].get(); let aliveFrame = framesCache[i].get();
@ -676,7 +676,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
if (!this.entry) { if (!this.entry) {
const args = fragment.getArguments(); const args = fragment.getArguments();
const frameId = args.getInt(FRAMEID); const frameId = args.getInt(FRAMEID);
const frame = getFrameById(frameId); const frame = getFrameByNumberId(frameId);
if (!frame) { if (!frame) {
throw new Error(`Cannot find Frame for ${fragment}`); throw new Error(`Cannot find Frame for ${fragment}`);
} }
@ -999,7 +999,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
if (!rootView) { if (!rootView) {
// If we have frameId from extras - we are starting a new activity from navigation (e.g. new Frame().navigate())) // If we have frameId from extras - we are starting a new activity from navigation (e.g. new Frame().navigate()))
// Then we check if we have frameId from savedInstanceState - this happens when Activity is destroyed but app was not (e.g. suspend) // Then we check if we have frameId from savedInstanceState - this happens when Activity is destroyed but app was not (e.g. suspend)
rootView = getFrameById(frameId) || new Frame(); rootView = getFrameByNumberId(frameId) || new Frame();
} }
if (rootView instanceof Frame) { if (rootView instanceof Frame) {

View File

@ -145,6 +145,14 @@ export class Frame extends View {
* @private * @private
*/ */
_updateBackstack(entry: BackstackEntry, isBack: boolean): void; _updateBackstack(entry: BackstackEntry, isBack: boolean): void;
/**
* @private
*/
_pushInFrameStack();
/**
* @private
*/
_removeFromFrameStack();
/** /**
* @private * @private
*/ */
@ -170,6 +178,11 @@ export class Frame extends View {
*/ */
export function setFragmentClass(clazz: any): void; export function setFragmentClass(clazz: any): void;
/**
* Gets a frame by id.
*/
export function getFrameById(id: string): Frame;
/** /**
* Gets the topmost frame in the frames stack. An application will typically has one frame instance. Multiple frames handle nested (hierarchical) navigation scenarios. * Gets the topmost frame in the frames stack. An application will typically has one frame instance. Multiple frames handle nested (hierarchical) navigation scenarios.
*/ */
@ -182,6 +195,7 @@ export function topmost(): Frame;
export function goBack(); export function goBack();
/** /**
* Deprecated. Use getFrameById() if you want to retrieve a frame different than the topmost one.
* Gets the frames stack. * Gets the frames stack.
*/ */
export function stack(): Array<Frame>; export function stack(): Array<Frame>;

View File

@ -11,6 +11,7 @@ import {
import { textTransformProperty, TextTransform, getTransformedText } from "../text-base"; import { textTransformProperty, TextTransform, getTransformedText } from "../text-base";
import { fromFileOrResource } from "../../image-source"; import { fromFileOrResource } from "../../image-source";
import { RESOURCE_PREFIX, ad } from "../../utils/utils"; import { RESOURCE_PREFIX, ad } from "../../utils/utils";
import { Frame } from "../frame";
export * from "./tab-view-common"; export * from "./tab-view-common";
@ -473,6 +474,12 @@ export class TabView extends TabViewBase {
} }
}); });
const newItem = items[newIndex];
const selectedView = newItem && newItem.view;
if (selectedView instanceof Frame) {
selectedView._pushInFrameStack();
}
toLoad.forEach(index => { toLoad.forEach(index => {
const item = items[index]; const item = items[index];
if (this.isLoaded && items[index]) { if (this.isLoaded && items[index]) {

View File

@ -13,6 +13,7 @@ import { Page } from "../page";
import { profile } from "../../profiling"; import { profile } from "../../profiling";
import * as uiUtils from "../utils"; import * as uiUtils from "../utils";
import * as utils from "../../utils/utils"; import * as utils from "../../utils/utils";
import { Frame } from "../frame";
export * from "./tab-view-common"; export * from "./tab-view-common";
@ -253,6 +254,11 @@ export class TabView extends TabViewBase {
const newItem = items[newIndex]; const newItem = items[newIndex];
if (newItem && this.isLoaded) { if (newItem && this.isLoaded) {
const selectedView = items[newIndex].view;
if (selectedView instanceof Frame) {
selectedView._pushInFrameStack();
}
newItem.loadView(newItem.view); newItem.loadView(newItem.view);
} }