| | import type { Response, NextFunction } from 'express' |
| |
|
| | import type { ExtendedRequest, LearningTracks } from '@/types' |
| | import type { Context, CurrentLearningTrack, TrackGuide } from '../lib/types' |
| | import { getPathWithoutLanguage, getPathWithoutVersion } from '@/frame/lib/path-utils' |
| | import getLinkData from '../lib/get-link-data' |
| | import { renderContent } from '@/content-render/index' |
| | import { executeWithFallback } from '@/languages/lib/render-with-fallback' |
| | import { getDeepDataByLanguage } from '@/data-directory/lib/get-data' |
| |
|
| | export default async function learningTrack( |
| | req: ExtendedRequest & { context: Context }, |
| | res: Response, |
| | next: NextFunction, |
| | ) { |
| | if (!req.context) throw new Error('request is not contextualized') |
| |
|
| | const noTrack = () => { |
| | req.context.currentLearningTrack = null |
| | return next() |
| | } |
| |
|
| | if (!req.context.page) return next() |
| |
|
| | if (!req.query.learn) return noTrack() |
| | if (Array.isArray(req.query.learn)) return noTrack() |
| | const trackName = req.query.learn as string |
| |
|
| | let trackProduct = req.context.currentProduct as string |
| | const allLearningTracks = getDeepDataByLanguage( |
| | 'learning-tracks', |
| | req.language!, |
| | ) as LearningTracks |
| |
|
| | if (req.language !== 'en') { |
| | |
| | |
| | const allEnglishLearningTracks = getDeepDataByLanguage( |
| | 'learning-tracks', |
| | 'en', |
| | ) as LearningTracks |
| | for (const [key, tracks] of Object.entries(allLearningTracks)) { |
| | if (!(key in allEnglishLearningTracks)) { |
| | |
| | |
| | |
| | delete allLearningTracks[key] |
| | console.warn('No English learning track for %s', key) |
| | continue |
| | } |
| | for (const [name, track] of Object.entries(tracks)) { |
| | |
| | |
| | if (!(name in allEnglishLearningTracks[key])) { |
| | delete tracks[name] |
| | continue |
| | } |
| | track.guides = allEnglishLearningTracks[key][name].guides |
| | } |
| | } |
| | } |
| | let tracksPerProduct = allLearningTracks[trackProduct] |
| |
|
| | |
| | |
| | |
| | |
| | if (!tracksPerProduct) { |
| | if (!req.query.learnProduct) return noTrack() |
| | if (Array.isArray(req.query.learnProduct)) { |
| | trackProduct = req.query.learnProduct[0] as string |
| | } else { |
| | trackProduct = req.query.learnProduct as string |
| | } |
| | tracksPerProduct = allLearningTracks[trackProduct] |
| | } |
| | if (!tracksPerProduct) return noTrack() |
| |
|
| | const track = allLearningTracks[trackProduct][trackName] |
| | if (!track) return noTrack() |
| |
|
| | |
| | const renderOpts = { textOnly: true } |
| | |
| | |
| | const trackTitle = await executeWithFallback( |
| | req.context, |
| | () => renderContent(track.title, req.context, renderOpts), |
| | () => '', |
| | ) |
| |
|
| | const currentLearningTrack: CurrentLearningTrack = { trackName, trackProduct, trackTitle } |
| | const guidePath = getPathWithoutLanguage(getPathWithoutVersion(req.pagePath)) |
| |
|
| | |
| | |
| | const trackGuides = ((await getLinkData(track.guides, req.context)) || []) as TrackGuide[] |
| |
|
| | const trackGuidePaths = trackGuides.map((guide) => { |
| | return getPathWithoutLanguage(getPathWithoutVersion(guide.href)) |
| | }) |
| |
|
| | let guideIndex = trackGuidePaths.findIndex((path) => path === guidePath) |
| |
|
| | |
| | |
| | |
| | if (guideIndex < 0) { |
| | guideIndex = await indexOfLearningTrackGuide(trackGuidePaths, guidePath, req.context) |
| | } |
| |
|
| | |
| | |
| | |
| | if (guideIndex < 0) { |
| | for (const redirect of req.context.page.redirect_from || []) { |
| | if (guideIndex >= 0) break |
| |
|
| | guideIndex = await indexOfLearningTrackGuide(trackGuidePaths, redirect, req.context) |
| | } |
| | } |
| |
|
| | if (guideIndex < 0) return noTrack() |
| |
|
| | currentLearningTrack.numberOfGuides = trackGuidePaths.length |
| | currentLearningTrack.currentGuideIndex = guideIndex |
| |
|
| | if (guideIndex > 0) { |
| | const prevGuidePath = trackGuidePaths[guideIndex - 1] |
| | const resultData = await getLinkData(prevGuidePath, req.context, { |
| | title: true, |
| | intro: false, |
| | fullTitle: false, |
| | }) |
| | if (!resultData || !resultData.length) return noTrack() |
| | const result = resultData[0] |
| |
|
| | const href = result.href |
| | const title = result.title |
| | currentLearningTrack.prevGuide = { href, title } |
| | } |
| |
|
| | if (guideIndex < trackGuidePaths.length - 1) { |
| | const nextGuidePath = trackGuidePaths[guideIndex + 1] |
| | const resultData = await getLinkData(nextGuidePath, req.context, { |
| | title: true, |
| | intro: false, |
| | fullTitle: false, |
| | }) |
| | if (!resultData || !resultData.length) return noTrack() |
| | const result = resultData[0] |
| |
|
| | const href = result.href |
| | const title = result.title |
| |
|
| | currentLearningTrack.nextGuide = { href, title } |
| | } |
| |
|
| | req.context.currentLearningTrack = currentLearningTrack |
| |
|
| | return next() |
| | } |
| |
|
| | |
| | |
| | async function indexOfLearningTrackGuide( |
| | trackGuidePaths: string[], |
| | guidePath: string, |
| | context: Context, |
| | ) { |
| | let guideIndex = -1 |
| |
|
| | const renderOpts = { textOnly: true } |
| | for (let i = 0; i < trackGuidePaths.length; i++) { |
| | |
| | const renderedGuidePath = await executeWithFallback( |
| | context, |
| | () => renderContent(trackGuidePaths[i], context, renderOpts), |
| | () => '', |
| | ) |
| |
|
| | if (!renderedGuidePath) continue |
| |
|
| | if (renderedGuidePath === guidePath) { |
| | guideIndex = i |
| | break |
| | } |
| | } |
| |
|
| | return guideIndex |
| | } |
| |
|