import axios from 'axios'; import * as log from 'loglevel'; import { useContext, useState } from 'react'; import { toast } from 'react-toastify'; import { MiniAppContext } from '../context/miniAppContext'; import { MiniAppAPI, MiniAppCBType } from '../model/miniAppApi'; import { MiniAppView } from '../model/miniAppView'; import msgHandler from '../utils/iframe/messageHandler'; import { isUrl } from '../utils/helperFunctions'; import { MiniAppErrorType } from '../model/miniAppError'; function useRouter() { const { state, dispatch } = useContext(MiniAppContext); const [displayBackButton, setDisplayBackButton] = useState(false); let initialStack: MiniAppView[][] = [ [ new MiniAppView( '/workspace/' + (state?.pages?.length ? state.pages[0] : '') ), ], ]; if ( state.tabBar && state.tabBar.list && state.tabBar.list?.length > 1 && state.tabBar.list?.length < 6 ) { initialStack = state.tabBar.list.slice().map((tab) => { return [new MiniAppView(`/workspace/${tab.pagePath}`)]; }); } const [navigationStack, setNavigationStack] = useState(initialStack); const [activeIndex, setActiveIndex] = useState(0); const navigateTo = (params: any) => { if (!params?.url?.startsWith('/')) { params.url = '/' + params?.url; } if (params?.url?.startsWith('/pages/')) { params.url = '/workspace' + params.url; axios .get(params.url) .then(function (response) { navigationHelper.push(new MiniAppView(params.url)); }) .catch(function (error) { log.error(error); }); } }; const backButtonTap = () => { if ( navigationHelper .activeView() .handleEvent(MiniAppAPI.backButtonDidTap, MiniAppCBType.success) === false ) { navigateBack(); } }; const backButtonDidTap = (miniAppView: MiniAppView, data: any) => { if (data.originId === miniAppView.id) { miniAppView.callbacks.push({ name: MiniAppAPI.backButtonDidTap, type: MiniAppCBType.success, id: data.successCB, }); } }; const navigateBack = () => { navigationHelper.pop(); }; const popAll = () => { if ( !navigationStack[activeIndex] || navigationStack[activeIndex].length <= 1 ) { return; } const firstUrl = navigationHelper.activeStack()[0]; navigationStack[activeIndex] = [firstUrl]; setNavigationStack([...navigationStack]); }; const redirectTo = (params: any) => { if (!params?.url?.startsWith('/')) { params.url = '/' + params?.url; } if (params?.url?.startsWith('/pages/')) { params.url = '/workspace' + params.url; axios .get(params.url) .then(function () { navigationHelper.showView(new MiniAppView(params.url)); }) .catch(function (error) { log.error(error); }); } }; const navigateToRemote = (params: any) => { try { if ( state?.whiteListDomains.indexOf(new URL(params.url ?? '').host) > -1 ) { navigationHelper.showView(new MiniAppView(params.url)); msgHandler(params?.originId, params?.successCB); setDisplayBackButton(params?.displayBackButton); } else { msgHandler(params?.originId, params?.failCB, { errorCode: MiniAppErrorType.whiteListError, }); } } catch (e) { msgHandler(params?.originId, params?.failCB, { errorCode: MiniAppErrorType.invalidUrl, }); } }; const switchTab = (params: any) => { if (params.url?.length > 0) { try { if (!params?.url?.startsWith('/')) { params.url = '/' + params?.url; } let theIndex = -1; for (let index = 0; index < navigationStack.length; index++) { if ( navigationStack[index][0].url.startsWith('/workspace' + params.url) ) { theIndex = index; break; } } if (theIndex === -1) { return; } navigationHelper.setSelectedTab(theIndex); } catch (error) { log.error(JSON.stringify(error)); } } }; const [webviewProps, setWebviewProps] = useState({ visible: false, toggleVisible: (v: boolean) => {}, view: {} as MiniAppView, }); const openWebview = (data: any) => { /* style: could be modal, slide, or external modal: present from bottom slide: present by sliding in from right side, stay as top screen in screen stack external: open external browser. url: target endpoint to open in webview */ if (!isUrl(data.url)) { return msgHandler(data?.originId, data?.failCB, { errorCode: MiniAppErrorType.invalidUrl, }); } if (state.whiteListDomains.indexOf(new URL(data.url ?? '').host) === -1) { return msgHandler(data?.originId, data?.failCB, { errorCode: MiniAppErrorType.whiteListError, }); } switch (data.style) { case 'slide': navigationHelper.push(new MiniAppView(data.url)); break; case 'external': if (data.url && data.url.length > 0) { const win = window.open(data.url, '_blank'); if (win) { win.focus(); } else { //Browser has blocked it toast.error('Please allow popups for this website'); } } break; default: case 'modal': setWebviewProps({ visible: true, toggleVisible: (v) => { setWebviewProps({ ...webviewProps, visible: v, view: new MiniAppView(data.url), }); }, view: new MiniAppView(data.url), }); break; } }; const navigationHelper = { activeStack() { return navigationStack[activeIndex]; }, activeView() { const aStack = this.activeStack(); return aStack[aStack.length - 1]; }, getSelectedTab() { return activeIndex; }, setSelectedTab(tabIndex: number) { setActiveIndex(tabIndex); }, // showView(view: MiniAppView) { this.activeViewWillDisappear(); navigationStack[activeIndex] = [view]; setNavigationStack([...navigationStack]); this.activeViewWillAppear(); }, push(view: MiniAppView) { this.activeViewWillDisappear(); this.activeStack().push(view); setNavigationStack([...navigationStack]); this.activeViewWillAppear(); }, pop() { if (!this.activeStack() || this.activeStack().length <= 1) { return; } this.activeViewWillDisappear(); this.activeStack().pop(); setNavigationStack([...navigationStack]); this.activeViewWillAppear(); }, // activeViewWillDisappear() { this.activeView().handleEvent( MiniAppAPI.pageWillDisappear, MiniAppCBType.success ); }, activeViewWillAppear() { if (this.activeView().url.startsWith('/workspace')) { const segmentArray = this.activeView().url.split('/'); const lastSegment = segmentArray[segmentArray.length - 1] ?.split('?')[0] ?.split('#')[0] ?.split('.html')[0]; const pageJson = segmentArray.slice(0, -1).join('/'); const pageJsonUrl = pageJson + '/' + lastSegment + '.json'; axios.get(pageJsonUrl).then((res) => { if (res?.data?.window) { dispatch({ type: 'UPDATE_WINDOW', payload: res.data.window, }); } }); } this.activeView().handleEvent( MiniAppAPI.pageWillAppear, MiniAppCBType.success ); }, }; return { navigationStack, navigateTo, navigateBack, backButtonTap, backButtonDidTap, popAll, redirectTo, navigateToRemote, switchTab, navigationHelper, activeIndex, setActiveIndex, displayBackButton, openWebview, webviewProps, }; } export default useRouter;