111 lines
2.9 KiB
TypeScript
Executable File
111 lines
2.9 KiB
TypeScript
Executable File
import {
|
|
inject,
|
|
onBeforeUnmount,
|
|
onMounted,
|
|
onScopeDispose,
|
|
watch,
|
|
type InjectionKey,
|
|
} from "vue";
|
|
import { nativeRouterKey } from "../runtime";
|
|
import type { NativeRouterRuntime, NativeViewLifecycle } from "../types";
|
|
|
|
export const nativeViewLifecycleKey: InjectionKey<NativeViewLifecycle> = Symbol(
|
|
"native-view-lifecycle",
|
|
);
|
|
|
|
export function useNativeRouter() {
|
|
const runtime = inject<NativeRouterRuntime>(nativeRouterKey);
|
|
if (!runtime)
|
|
throw new Error(
|
|
"Native Vue Router is not installed. Call app.use(nativeRouter).",
|
|
);
|
|
return runtime;
|
|
}
|
|
|
|
export function useNativeViewLifecycle() {
|
|
const lifecycle = inject<NativeViewLifecycle>(nativeViewLifecycleKey);
|
|
if (!lifecycle)
|
|
throw new Error(
|
|
"Native view lifecycle APIs must be used inside NativeRouterView.",
|
|
);
|
|
return lifecycle;
|
|
}
|
|
|
|
type NativeViewHook = () => void;
|
|
|
|
function onNativeViewState(
|
|
source: Readonly<{ value: boolean }>,
|
|
entering: boolean,
|
|
hook: NativeViewHook,
|
|
) {
|
|
onMounted(() => {
|
|
if (entering && source.value) hook();
|
|
});
|
|
watch(
|
|
() => source.value,
|
|
(value, previous) => {
|
|
if (value === entering && previous !== entering) hook();
|
|
},
|
|
{ flush: "sync" },
|
|
);
|
|
}
|
|
|
|
export function onNativeViewActivate(hook: NativeViewHook) {
|
|
onNativeViewState(useNativeViewLifecycle().isActive, true, hook);
|
|
}
|
|
|
|
export function onNativeViewDeactivate(hook: NativeViewHook) {
|
|
onNativeViewState(useNativeViewLifecycle().isActive, false, hook);
|
|
}
|
|
|
|
export function onNativeViewShow(hook: NativeViewHook) {
|
|
onNativeViewState(useNativeViewLifecycle().isVisible, true, hook);
|
|
}
|
|
|
|
export function onNativeViewHide(hook: NativeViewHook) {
|
|
onNativeViewState(useNativeViewLifecycle().isVisible, false, hook);
|
|
}
|
|
|
|
export function onNativeViewEvict(
|
|
hook: (reason: NativeViewLifecycle["evictionReason"]["value"]) => void,
|
|
) {
|
|
const lifecycle = useNativeViewLifecycle();
|
|
onBeforeUnmount(() => {
|
|
if (lifecycle.status.value === "evicted")
|
|
hook(lifecycle.evictionReason.value);
|
|
});
|
|
}
|
|
|
|
function useNativeViewEffect(
|
|
source: Readonly<{ value: boolean }>,
|
|
effect: () => void | (() => void),
|
|
) {
|
|
let cleanup: void | (() => void);
|
|
const stopEffect = () => {
|
|
cleanup?.();
|
|
cleanup = undefined;
|
|
};
|
|
const stopWatch = watch(
|
|
() => source.value,
|
|
(enabled) => {
|
|
stopEffect();
|
|
if (enabled) cleanup = effect();
|
|
},
|
|
{ immediate: true, flush: "sync" },
|
|
);
|
|
onScopeDispose(() => {
|
|
stopWatch();
|
|
stopEffect();
|
|
});
|
|
}
|
|
|
|
/** Runs an effect only while this route is the semantically active route. */
|
|
export function useNativeViewActiveEffect(effect: () => void | (() => void)) {
|
|
useNativeViewEffect(useNativeViewLifecycle().isActive, effect);
|
|
}
|
|
|
|
/** Runs an effect while this route is active or participating in a transition. */
|
|
export function useNativeViewVisibleEffect(effect: () => void | (() => void)) {
|
|
useNativeViewEffect(useNativeViewLifecycle().isVisible, effect);
|
|
}
|