web-atoms-core
Version:
186 lines (155 loc) • 6.07 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 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;
}
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[] = [];
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>;
/**
*
* @param pageName url
* @param p parameters
* @param options {@link IPageOptions}
*/
public openPage<T>(
pageName: string | any,
p?: 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}`;
}
}
const url = new AtomUri(pageName);
if (p) {
for (const key in p) {
if (p.hasOwnProperty(key)) {
const element = p[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): 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);
}
};
}
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");