@web-atoms/core
Version:
239 lines (199 loc) • 8.03 kB
text/typescript
import { App } from "../App";
import { AtomComponent } from "../core/AtomComponent";
import { AtomUri } from "../core/AtomUri";
import FormattedString from "../core/FormattedString";
import { ArrayHelper, CancelToken, IDisposable, INameValuePairs } from "../core/types";
import ReferenceService, { ObjectReference } from "./ReferenceService";
// export interface ILocation {
// href?: string;
// hash?: INameValues;
// host?: string;
// hostName?: string;
// port?: string;
// protocol?: string;
// }
export enum NotifyType {
Information = "info",
Warning = "warn",
Error = "error"
}
export type navigateCallback = (
url: AtomUri,
options?: IPageOptions) => Promise<any>;
export type preNavigateCallback = (
url: any,
viewModelParameters: {[key: string]: any},
options?: IPageOptions) => Promise<any>;
export interface IPageOptions {
/**
* target is name of a Frame or AtomTabbedPage component
* where this window/frame should be loaded.
*/
target?: string;
/**
* If set to true, it will clear the history of the frame
*/
clearHistory?: boolean;
/**
* If you want to cancel the window/frame, you can remove the window by calling cancel on given CancelToken
*/
cancelToken?: CancelToken;
/**
* In mobile application, this will not add chrome/navigation headers on the page
*/
frameLess?: boolean;
/**
* Set to true if you want to open modal window blocking entire app.
*/
modal?: boolean;
/** Initializer that will be invoked when page/popup/window is created */
onInit?: (view: any) => void;
}
declare var UMD: any;
const nameSymbol = UMD.nameSymbol;
function hasPageUrl(target: any): boolean {
const url = target[nameSymbol];
if (!url) {
return false;
}
const baseClass = Object.getPrototypeOf(target);
if (!baseClass) {
// this is not possible...
return false;
}
return baseClass[nameSymbol] !== url;
}
export abstract class NavigationService {
private callbacks: navigateCallback[] = [];
private beforeCallbacks: preNavigateCallback[];
constructor(public readonly app: App) {
}
public abstract alert(message: string | FormattedString, title?: string): Promise<any>;
public abstract confirm(message: string | FormattedString, title?: string): Promise<boolean>;
/**
* This method will open the page, it will not wait for the result
* @param pageName node style package url or a class
* @param viewModelParameters key value pair that will be injected on ViewModel when created
* @param options {@link IPageOptions}
*/
public pushPage(
pageName: string | any,
viewModelParameters?: INameValuePairs,
options?: IPageOptions) {
this.app.runAsync(() => this.openPage(pageName, viewModelParameters, options));
}
/**
* This method will open the page and it will wait for result, use pushPage to
* ignore the result
* @param pageName node style package url or a class
* @param viewModelParameters key value pair that will be injected on ViewModel when created
* @param options {@link IPageOptions}
*/
public openPage<T>(
pageName: string | any,
viewModelParameters?: INameValuePairs,
options?: IPageOptions): Promise<T> {
options = options || {};
if (typeof pageName !== "string") {
if (hasPageUrl(pageName)) {
pageName = pageName[nameSymbol] as string;
} else {
const rs = this.app.resolve(ReferenceService) as ReferenceService;
const host = pageName instanceof AtomComponent ? "reference" : "class";
const r = rs.put(pageName);
pageName = `ref://${host}/${r.key}`;
}
}
if (this.beforeCallbacks) {
for (const iterator of this.beforeCallbacks) {
const r = iterator(pageName, viewModelParameters, options);
if (r) {
return r;
}
}
}
const url = new AtomUri(pageName);
if (viewModelParameters) {
for (const key in viewModelParameters) {
if (viewModelParameters.hasOwnProperty(key)) {
const element = viewModelParameters[key];
if (element === undefined) {
continue;
}
if (element === null) {
url.query["json:" + key] = "null";
continue;
}
if (key.startsWith("ref:") || element instanceof FormattedString) {
const r = element instanceof ObjectReference ?
element :
(this.app.resolve(ReferenceService) as ReferenceService).put(element);
url.query[key.startsWith("ref:") ? key : `ref:${key}`] =
r.key;
continue;
}
if (typeof element !== "string" &&
(typeof element === "object" || Array.isArray(element))) {
url.query["json:" + key] = JSON.stringify(element);
} else {
url.query[key] = element;
}
}
}
}
for (const iterator of this.callbacks) {
const r = iterator(url, options);
if (r) {
return r;
}
}
return this.openWindow(url, options);
}
public abstract notify(message: string | FormattedString, title?: string, type?: NotifyType, delay?: number): Promise<void>;
public abstract get title(): string;
public abstract set title(v: string);
public abstract get location(): AtomUri;
public abstract set location(v: AtomUri);
public abstract navigate(url: string): void;
public abstract back(): void;
public abstract refresh(): void;
/**
* Sends signal to remove window/popup/frame, it will not immediately remove, because
* it will identify whether it can remove or not by displaying cancellation warning. Only
* if there is no cancellation warning or user chooses to force close, it will not remove.
* @param id id of an element
* @returns true if view was removed successfully
*/
public async remove(view: { element: any, viewModel: any }, force?: boolean): Promise<boolean> {
if (force) {
this.app.broadcast(`atom-window-cancel:${(view as any).id}`, "cancelled");
return true;
}
const vm = view.viewModel;
if (vm && vm.cancel) {
const a = await vm.cancel();
return a;
}
this.app.broadcast(`atom-window-cancel:${(view as any).id}`, "cancelled");
return true;
}
public registerNavigationHook(callback: navigateCallback): IDisposable {
this.callbacks.push(callback);
return {
dispose: () => {
ArrayHelper.remove(this.callbacks, (a) => a === callback);
}
};
}
public registerPreNavigationHook(callback: preNavigateCallback): IDisposable {
(this.beforeCallbacks ??= []).push(callback);
return {
dispose: () => {
this.beforeCallbacks.remove(callback);
}
};
}
protected abstract openWindow<T>(url: AtomUri, options: IPageOptions): Promise<T>;
}
// Do not mock Navigation unless you want it in design time..
// Mock.mock(NavigationService, "MockNavigationService");