141 lines
4.4 KiB
JavaScript
141 lines
4.4 KiB
JavaScript
|
|
/**
|
||
|
|
* Einheitlicher Rücksprung-Kontext für PWA-Navigation (siehe NAV_RETURN_CONTEXT_SPEC.md).
|
||
|
|
*/
|
||
|
|
import {
|
||
|
|
buildPlanningHubReturnState,
|
||
|
|
planningHubPathFromReturnState,
|
||
|
|
} from './planningUnitRoutes'
|
||
|
|
|
||
|
|
export const NAV_RETURN_STATE_KEY = 'appReturn'
|
||
|
|
export const EXERCISES_LIST_PATH = '/exercises'
|
||
|
|
export const TRAINING_MODULES_LIST_PATH = '/planning/training-modules'
|
||
|
|
|
||
|
|
export function buildNavReturnContext(opts) {
|
||
|
|
const path = String(opts?.path || '').trim()
|
||
|
|
const label = String(opts?.label || '').trim()
|
||
|
|
if (!path || !label) return null
|
||
|
|
const ctx = { v: 1, path, label }
|
||
|
|
if (opts?.kind) ctx.kind = opts.kind
|
||
|
|
if (opts?.payload && typeof opts.payload === 'object') {
|
||
|
|
ctx.payload = opts.payload
|
||
|
|
}
|
||
|
|
return ctx
|
||
|
|
}
|
||
|
|
|
||
|
|
export function buildExercisesListReturnContext() {
|
||
|
|
return buildNavReturnContext({
|
||
|
|
path: EXERCISES_LIST_PATH,
|
||
|
|
label: 'Zurück zur Übungsliste',
|
||
|
|
kind: 'exerciseList',
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
export function buildTrainingModulesListReturnContext() {
|
||
|
|
return buildNavReturnContext({
|
||
|
|
path: TRAINING_MODULES_LIST_PATH,
|
||
|
|
label: 'Zurück zur Modul-Bibliothek',
|
||
|
|
kind: 'trainingModulesList',
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Rücksprung zur aktuellen Route (z. B. Einheiten-Editor).
|
||
|
|
*/
|
||
|
|
export function buildCurrentLocationReturnContext(location, label) {
|
||
|
|
const path = `${location?.pathname || ''}${location?.search || ''}`.trim()
|
||
|
|
if (!path) return null
|
||
|
|
return buildNavReturnContext({
|
||
|
|
path,
|
||
|
|
label: String(label || 'Zurück').trim(),
|
||
|
|
kind: 'currentLocation',
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @param {ReturnType<typeof buildPlanningHubReturnState>|object} hubState */
|
||
|
|
export function buildPlanningHubReturnContext(hubState = {}) {
|
||
|
|
const payload = buildPlanningHubReturnState(hubState)
|
||
|
|
return buildNavReturnContext({
|
||
|
|
path: planningHubPathFromReturnState(payload),
|
||
|
|
label: 'Zurück zur Planung',
|
||
|
|
kind: 'planningHub',
|
||
|
|
payload,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Legacy planningReturn → appReturn */
|
||
|
|
export function appReturnFromPlanningReturn(planningReturn) {
|
||
|
|
if (!planningReturn || planningReturn.v !== 1) return null
|
||
|
|
return buildPlanningHubReturnContext(planningReturn)
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {import('react-router-dom').Location|{ state?: object }|null|undefined} location
|
||
|
|
*/
|
||
|
|
export function readNavReturnFromLocation(location) {
|
||
|
|
const state = location?.state
|
||
|
|
if (!state || typeof state !== 'object') return null
|
||
|
|
const raw = state[NAV_RETURN_STATE_KEY]
|
||
|
|
if (raw?.v === 1 && raw.path && raw.label) return raw
|
||
|
|
if (state.planningReturn) return appReturnFromPlanningReturn(state.planningReturn)
|
||
|
|
return null
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {import('react-router-dom').Location|{ state?: object }|null|undefined} location
|
||
|
|
* @param {{ path: string, label: string }|null|undefined} fallback
|
||
|
|
*/
|
||
|
|
export function resolveNavReturnTarget(location, fallback) {
|
||
|
|
const ctx = readNavReturnFromLocation(location)
|
||
|
|
if (ctx?.path && ctx?.label) {
|
||
|
|
return { path: ctx.path, label: ctx.label, fromContext: true }
|
||
|
|
}
|
||
|
|
if (fallback?.path && fallback?.label) {
|
||
|
|
return { path: fallback.path, label: fallback.label, fromContext: false }
|
||
|
|
}
|
||
|
|
return null
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {import('react-router-dom').NavigateFunction} navigate
|
||
|
|
* @param {import('react-router-dom').Location|{ state?: object }|null|undefined} location
|
||
|
|
* @param {{ path?: string, label?: string }|null|undefined} fallback
|
||
|
|
*/
|
||
|
|
export function goNavReturn(navigate, location, fallback) {
|
||
|
|
const target = resolveNavReturnTarget(location, fallback)
|
||
|
|
if (target?.fromContext && target.path) {
|
||
|
|
navigate(target.path)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if (typeof window !== 'undefined' && window.history.length > 1) {
|
||
|
|
navigate(-1)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if (fallback?.path) {
|
||
|
|
navigate(fallback.path)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
navigate('/')
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param {import('react-router-dom').NavigateFunction} navigate
|
||
|
|
* @param {string} to
|
||
|
|
* @param {ReturnType<typeof buildNavReturnContext>|null|undefined} returnContext
|
||
|
|
* @param {object} [options]
|
||
|
|
*/
|
||
|
|
export function navigateWithAppReturn(navigate, to, returnContext, options = {}) {
|
||
|
|
const state = { ...(options.state || {}) }
|
||
|
|
if (returnContext) state[NAV_RETURN_STATE_KEY] = returnContext
|
||
|
|
navigate(to, { ...options, state })
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Bestehenden appReturn (oder Legacy planningReturn) beim Weiterleiten erhalten.
|
||
|
|
*/
|
||
|
|
export function preserveAppReturnOnNavigate(navigate, location, to, options = {}) {
|
||
|
|
const existing = readNavReturnFromLocation(location)
|
||
|
|
const state = { ...(options.state || {}) }
|
||
|
|
if (existing) state[NAV_RETURN_STATE_KEY] = existing
|
||
|
|
navigate(to, { ...options, state })
|
||
|
|
}
|