1- import { onMounted , onUnmounted } from "vue" ;
1+ import { onMounted , onUnmounted , getCurrentInstance } from "vue" ;
22import { RouteAction , RouteDirection } from "@ionic/vue-router/dist/types/types" ;
33import { useIonRouter } from "@ionic/vue" ;
44import {
55 createTypedCustomEventClass ,
66 TypedCustomEventData ,
77} from "@/models/utils" ;
8- import { useRouter } from "vue-router" ;
98import { goBackOrNavigateTo } from "@/router" ;
9+ import { getCurrentComponentInstancePath } from "@/views/vue-utils" ;
10+ import { match , P } from "ts-pattern" ;
1011
1112
1213/**
@@ -52,6 +53,28 @@ const NavigationEvent = createTypedCustomEventClass<{
5253} > ( NavigationEventName )
5354
5455
56+ /**
57+ * This map allows to declare multiple navigation callback per component 'path'
58+ * because, for some (unknown) reasons, when we refresh tabbed page (like, event schedule)
59+ * we have 2 instances of event-tabs component after a tab navigation:
60+ * - first <event-tabs> instance when loading page
61+ * - second <event-tabs> instance on first tab switch
62+ *
63+ * Note that this behaviour only happens on page load occuring on a tabbed page: when we navigate
64+ * from event-selector screen, only a single <event-tabs> instance is created
65+ *
66+ * I guess this has something to do with _BaseEventPages "parent" route which is instantiated twice
67+ * instead of once, in case of a parent-child route refresh
68+ *
69+ * Map below allows to track callback registrations on a per-component path basis (in duplicated <event-tabs> case
70+ * described above, we will have a single entry in PER_COMPONENT_PATH_CALLBACKS, with an array of 2 callbacks declared)
71+ * And when a navigation will be triggered, every callbacks in array will be triggered)
72+ */
73+ type TabbedPageNavigationCallbacks = {
74+ navCallback : ( event : Event ) => Promise < void > ,
75+ tabExitOrNavigateCallback : ( event : Event ) => Promise < void > ,
76+ } ;
77+ const PER_COMPONENT_PATH_CALLBACKS = new Map < string , TabbedPageNavigationCallbacks [ ] > ( ) ;
5578
5679export function useTabbedPageNav ( ) {
5780 return {
@@ -76,44 +99,110 @@ export function useTabbedPageNav() {
7699 // Please, call this listeners registration whenever you open a page containing tabs, so that
77100 // tabbed views are able to communicate root-level navigation calls through triggerXXX hooks
78101 registerTabbedPageNavListeners : function ( opts ?: { skipNavRegistration : boolean , skipExitOrNavRegistration : boolean } ) {
102+ const { path : currentComponentInstancePath } = getCurrentComponentInstancePath ( )
103+
79104 const ionRouter = useIonRouter ( ) ;
80105 const startingHistoryPosition = history . state . position ;
81106
82107 const navCallback = async ( event : Event ) => {
108+ const perComponentPathCallbacks = PER_COMPONENT_PATH_CALLBACKS . get ( currentComponentInstancePath ) ;
109+ if ( ! perComponentPathCallbacks ) {
110+ return ;
111+ }
112+ if ( perComponentPathCallbacks [ perComponentPathCallbacks . length - 1 ] . navCallback !== navCallback ) {
113+ return ;
114+ }
115+
83116 if ( isNavigationEvent ( event ) ) {
84117 if ( event . detail . onEventCaught ) {
85118 await event . detail . onEventCaught ( ) ;
86119 }
87120
88121 // This navigate() call will happen inside tabbed page context
89122 ionRouter . navigate ( event . detail . url , event . detail . routerDirection , event . detail . routerAction ) ;
123+
124+ perComponentPathCallbacks . pop ( ) ;
125+ if ( perComponentPathCallbacks . length ) {
126+ await new Promise ( ( resolve ) => {
127+ setTimeout ( async ( ) => {
128+ await perComponentPathCallbacks [ perComponentPathCallbacks . length - 1 ] . navCallback ( event ) ;
129+ resolve ( null ) ;
130+ } , 0 ) ;
131+ } ) ;
132+ } else {
133+ PER_COMPONENT_PATH_CALLBACKS . delete ( currentComponentInstancePath ) ;
134+ }
90135 } else {
91136 throw new Error ( `Unexpected event type ${ event . type } in tabbed-page-navigation callback registration !` )
92137 }
93138 }
94139 const tabExitOrNavigateCallback = async ( event : Event ) => {
140+ const perComponentPathCallbacks = PER_COMPONENT_PATH_CALLBACKS . get ( currentComponentInstancePath ) ;
141+ if ( ! perComponentPathCallbacks ) {
142+ return ;
143+ }
144+ if ( perComponentPathCallbacks [ perComponentPathCallbacks . length - 1 ] . tabExitOrNavigateCallback !== tabExitOrNavigateCallback ) {
145+ return ;
146+ }
147+
95148 if ( isTabExitOrNavigateEvent ( event ) ) {
96149 const routerGoBacks = startingHistoryPosition - history . state . position ;
97150 await goBackOrNavigateTo ( ionRouter , event . detail . url , routerGoBacks , event . detail . routerDirection , event . detail . onEventCaught ) ;
151+
152+ perComponentPathCallbacks . pop ( ) ;
153+ if ( perComponentPathCallbacks . length ) {
154+ await new Promise ( async ( resolve ) => {
155+ await perComponentPathCallbacks [ perComponentPathCallbacks . length - 1 ] . tabExitOrNavigateCallback ( event ) ;
156+ setTimeout ( ( ) => resolve ( null ) , 0 ) ;
157+ } ) ;
158+ } else {
159+ PER_COMPONENT_PATH_CALLBACKS . delete ( currentComponentInstancePath ) ;
160+ }
98161 } else {
99162 throw new Error ( `Unexpected event type ${ event . type } in tabbed-page-navigation callback registration !` )
100163 }
101164 }
102165
166+ const componentCallbacks : TabbedPageNavigationCallbacks = { navCallback, tabExitOrNavigateCallback }
167+
103168 onMounted ( ( ) => {
169+ const perComponentPathCallbacks = match ( PER_COMPONENT_PATH_CALLBACKS . get ( currentComponentInstancePath ) )
170+ . with ( P . nullish , ( ) => {
171+ const callbacks : TabbedPageNavigationCallbacks [ ] = [ ] ;
172+ PER_COMPONENT_PATH_CALLBACKS . set ( currentComponentInstancePath , callbacks ) ;
173+ return callbacks ;
174+ } ) . otherwise ( callbacks => callbacks ) ;
175+
104176 if ( ! opts ?. skipNavRegistration ) {
105- window . addEventListener ( NavigationEventName , navCallback ) ;
177+ window . addEventListener ( NavigationEventName , componentCallbacks . navCallback ) ;
106178 }
107179 if ( ! opts ?. skipExitOrNavRegistration ) {
108- window . addEventListener ( TabExitOrNavigateEventName , tabExitOrNavigateCallback ) ;
180+ window . addEventListener ( TabExitOrNavigateEventName , componentCallbacks . tabExitOrNavigateCallback ) ;
109181 }
182+
183+ perComponentPathCallbacks . push ( componentCallbacks ) ;
110184 } )
111185 onUnmounted ( ( ) => {
112- if ( ! opts ?. skipNavRegistration ) {
113- window . removeEventListener ( NavigationEventName , navCallback ) ;
186+ const maybePerComponentPathCallbacks = PER_COMPONENT_PATH_CALLBACKS . get ( currentComponentInstancePath ) ;
187+ if ( ! maybePerComponentPathCallbacks ) {
188+ return ;
114189 }
115- if ( ! opts ?. skipExitOrNavRegistration ) {
116- window . removeEventListener ( TabExitOrNavigateEventName , tabExitOrNavigateCallback ) ;
190+
191+ const componentCallbacksIndex = maybePerComponentPathCallbacks . findIndex ( callbacks => callbacks === componentCallbacks ) ;
192+ if ( componentCallbacksIndex !== - 1 ) {
193+ maybePerComponentPathCallbacks . splice ( componentCallbacksIndex , 1 ) ;
194+
195+ if ( ! opts ?. skipNavRegistration ) {
196+ window . removeEventListener ( NavigationEventName , componentCallbacks . navCallback ) ;
197+ }
198+ if ( ! opts ?. skipExitOrNavRegistration ) {
199+ window . removeEventListener ( TabExitOrNavigateEventName , componentCallbacks . tabExitOrNavigateCallback ) ;
200+ }
201+ }
202+ if ( maybePerComponentPathCallbacks . length === 0 ) {
203+ PER_COMPONENT_PATH_CALLBACKS . delete ( currentComponentInstancePath ) ;
204+ } else {
205+ console . log ( `remaining callbacks in component ${ currentComponentInstancePath } ` )
117206 }
118207 } )
119208 }
0 commit comments