shinkan-jinkendo/frontend/src/utils/navReturnContext.js

141 lines
4.4 KiB
JavaScript
Raw Normal View History

/**
* 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 })
}