给小程序添加跳转历史管理,自动辨别跳转是否是标签页,项目地址在
https://gitee.com/ngaiwa/kuak.git
/**
* @author Fa
* @copyright Fa 2017
*/
import { noop } from '../utils/function';
import { isObject } from '../utils/type';
import { toRelRootPathname } from '../utils/url';
import { config } from './config';
export class History implements Kuak.History {
protected pages: Kuak.Page[];
maxLimit: Kuak.History['maxLimit'];
constructor(maxLimit: History['maxLimit']) {
this.maxLimit = maxLimit;
this.pages = [];
}
at(index: number) {
return this.pages[index < 0 ? this.pages.length + index : index];
}
getByIndex(index: number): Kuak.Page | void {
return this.pages[index];
}
getByPath(path: string): Kuak.Page[] {
return this.pages.reduce(
(found, page) => {
if (page.is === found.path) {
found.pages.push(page);
}
return found;
},
{
path: path,
pages: [] as any[],
},
).pages;
}
getById(id: string): Kuak.Page | void {
const found = this.pages.find((page) => page.id === id);
id = null as any;
return found;
}
indexOf(page: Kuak.Page) {
return this.pages.indexOf(page);
}
indexOfById(id: string) {
const index = this.pages.findIndex((page) => page.id === id);
id = null as any;
return index;
}
back() {
return App.router.navigateBack();
}
forward() {
const currentPage = this.currentPage;
const index = this.indexOf(currentPage);
const nextIndex = index + 1;
const page = this.getByIndex(nextIndex);
if (page) {
return navigateTo(App.router as any, { url: page.url }, page) as Promise;
}
return Promise.resolve();
}
/**
* @deprecated
*/
go(index: number): Promise {
return Promise.resolve(index);
}
currentOpened!: Kuak.Page;
currentShowed!: Kuak.Page;
get currentPage() {
const currentPages = this.currentPages;
const currentPage = currentPages[currentPages.length - 1];
if (!this.currentOpened) {
this.currentOpened = currentPage;
}
return currentPage;
}
get currentPages() {
return getCurrentPages();
}
get length() {
return this.pages.length;
}
get currentLength() {
return this.currentPages.length;
}
restoredPage?: Kuak.Page;
onPagesChange(option: { page: Kuak.Page; isNew?: boolean; isOld?: boolean }) {
const currentPage = this.currentPage;
const page = option.page;
if (page !== currentPage) {
this.currentOpened = currentPage;
if (!option.isNew) {
this.currentShowed = currentPage;
}
return;
}
const currentOpened = this.currentOpened;
this.currentOpened = page;
if (option.isNew) {
const restoredPage = this.restoredPage;
const index = this.indexOf(restoredPage!);
this.restoredPage = null as any;
if (restoredPage && restoredPage.__h_redirect_unloaded__) {
unloadPage(restoredPage.__h_redirect_unloaded__);
restoredPage.__h_redirect_unloaded__ = null;
}
if (index !== -1) {
// rejectTo or navigateBack
this.pages[index] = page;
return;
}
if (checkFirstPage(this, page)) {
return;
}
if (this.pages.indexOf(page) === -1) {
// navigateTo
if (currentOpened !== page) {
const index = this.indexOf(currentOpened);
if (index !== -1 && index < this.pages.length - 1) {
const unloadedList = this.pages.splice(index + 1, this.pages.length);
unloadPages(unloadedList);
}
}
this.pages.push(page);
}
return;
}
checkFirstPage(this, page);
this.currentOpened = page;
if (currentPage !== this.currentShowed) {
this.currentShowed = page;
}
}
getInfo() {
return {
openedId: this.currentOpened?.id,
id: this.currentPage?.id,
idList: this.pages.map((page) => page.id),
currentIdList: this.currentPages.map((page) => page.id),
};
}
}
const checkFirstPage = function (history: any, page: Kuak.Page) {
const currentPages = history.currentPages;
if (currentPages.length === 1 && page === currentPages[0]) {
// reLaunch switchTab
unloadPages(history.pages.splice(0));
history.pages.push(page);
return true;
}
return false;
};
const unloadPage = function (page: any) {
if (page && !page.__unloaded__ && page.__h_canUnload__) {
try {
if (page.__h_onUnload__) {
page.__h_onUnload__();
} else if (page.onUnload) {
page.onUnload();
}
} catch (e) {
console.error('onUnload Error', e);
}
page.__unloaded__ = true;
}
};
const unloadPages = function (pages: any[]) {
for (let i = 0; i < pages.length; i++) {
unloadPage(pages[i]);
}
};
interface Router extends Kuak.NavRouter {
__switchTab__: AnyFunction;
__reLaunch__: AnyFunction;
__redirectTo__: AnyFunction;
__navigateTo__: AnyFunction;
__navigateBack__: AnyFunction;
navHistory: Kuak.History;
__nav_fn_bound__: true;
}
const navResultHandler = function (
result: Promise,
option: { success?: AnyFunction; fail?: AnyFunction; complete?: AnyFunction; [n: string]: any },
history?: Kuak.History,
oldPage?: Kuak.Page,
newPage?: Kuak.Page,
) {
const { success, fail, complete } = option;
if (success) {
result.then(success).catch(noop);
}
if (fail) {
result.catch(fail).catch(noop);
}
if (complete) {
result.finally(complete).catch(noop);
}
if (history && newPage) {
result
.catch((res: any) => {
console.warn(res);
if (history!.restoredPage === newPage) {
history!.restoredPage = null as any;
replacePage(history!, newPage!, oldPage!);
}
})
.finally(() => {
history = newPage = oldPage = null as any;
});
} else {
result.catch(console.warn);
}
return result;
};
const replacePage = function (history: Kuak.History, page: Kuak.Page, newPage: Kuak.Page) {
if (!newPage) {
return;
}
const index = history.indexOf(page);
if (index !== -1) {
(history as any).pages[index] = newPage;
}
};
const callNavFunc = function (func: AnyFunction, option: any) {
return new Promise((success, fail) => {
func({ ...option, success, fail, complete: null });
func = option = null as any;
});
};
const isTabPage = function (router: any, option: any) {
return config.tabPagePaths.includes(
option.pathname || toRelRootPathname(option.url, router.dir),
);
};
const redirectPage = function (router: Router, option: any, newPage: any, oldPage?: any) {
if (isTabPage(router, option)) {
return router.switchTab(option);
}
const history = router.navHistory;
oldPage = oldPage || history.currentPage;
replacePage(history, history.currentPage, newPage);
history.restoredPage = newPage;
return navResultHandler(
callNavFunc(router.__redirectTo__, option),
option,
history,
oldPage,
newPage,
);
};
const navigateTo = function (router: Router, option: { url: string }, restoredPage: any) {
if (isTabPage(router, option)) {
return router.switchTab(option);
}
const history = router.navHistory;
const currentPages = history.currentPages;
const currentPage = currentPages[currentPages.length - 1];
const max = history.maxLimit[currentPage?.renderer as 'skyline'] || history.maxLimit.webview;
if (currentPages.length >= max) {
return redirectPage(router, option, restoredPage);
} else {
history.restoredPage = restoredPage;
return navResultHandler(callNavFunc(router.__navigateTo__, option), option, history);
}
};
const toUrlOption = function (option: any) {
return isObject(option) ? option : { url: option };
};
const toDeltaOption = function (option: any) {
return isObject(option) ? option : { delta: option };
};
const routerFn: Router = {
redirectTo(option: any) {
option = toUrlOption(option);
return redirectPage(this, option, {
__h_redirect_unloaded__: this.navHistory.currentPage,
});
},
navigateTo(option: any) {
return navigateTo(this, toUrlOption(option), null);
},
navigateBack(option?: any) {
const _option = toDeltaOption(option);
const delta = Math.abs(+_option.delta || 1);
const history = this.navHistory;
const currentPages = history.currentPages;
const currentPage = currentPages[currentPages.length - 1];
const backPage = history.getByIndex(history.indexOf(currentPage) - delta);
const currentBackPage = currentPages[currentPages.indexOf(currentPage) - delta];
if (!backPage || backPage === currentPage) {
return navResultHandler(Promise.resolve(), _option);
}
if (backPage !== currentBackPage) {
if (isTabPage(this, backPage)) {
return this.switchTab({ url: backPage.url });
}
return redirectPage(this, { ...option, url: backPage.url }, backPage, backPage);
}
return navResultHandler(
callNavFunc(this.__navigateBack__, { ..._option, delta: delta }),
_option,
);
},
reLaunch(option: any) {
return callNavFunc(this.__reLaunch__, toUrlOption(option));
},
switchTab(option: any) {
return callNavFunc(this.__switchTab__, toUrlOption(option));
},
__nav_fn_bound__: true,
} as Router;
export const navFnHandler = function (router: Kuak.NavRouter, history?: Kuak.History) {
if ((router as any).__nav_fn_bound__) {
return router;
}
(router as any).navHistory = history || App.history;
return Object.assign(
router,
{
__navigateBack__: router.navigateBack,
__navigateTo__: router.navigateTo,
__redirectTo__: router.redirectTo,
__reLaunch__: router.reLaunch,
__switchTab__: router.switchTab,
},
routerFn,
);
};
export const boundRouter = function (
page: Kuak.Page,
router: Kuak.Page['router'],
history?: Kuak.History,
) {
router = navFnHandler(router, history);
if (page.router && page.router !== router) {
page.router = router;
}
if (page.pageRouter && page.pageRouter !== router) {
page.pageRouter = router;
}
};