Deep-link public topic handoffs into protected routes
This commit is contained in:
parent
4a5c0f7a7c
commit
556ad2edf2
6 changed files with 49 additions and 8 deletions
|
|
@ -268,6 +268,12 @@ Latest same-day browser/distribution continuity follow-up still on `2026-06-24`:
|
|||
- operator access
|
||||
- studio rollout
|
||||
- each topic now carries explicit next steps, manual/route focus, and quick actions so the public web surface reflects the same browser/package/runtime/compliance separation the hardened routes already enforce
|
||||
- those topic actions now also preserve exact protected destinations through auth:
|
||||
- protected downloads
|
||||
- protected notices
|
||||
- protected browser-access
|
||||
- protected dashboard
|
||||
- the public topic cards therefore no longer dead-end on broad public guidance when the real next step is signed-in protected follow-through
|
||||
- the new shared callout posture explicitly separates:
|
||||
- what still works
|
||||
- what stays intentionally withheld
|
||||
|
|
|
|||
|
|
@ -884,8 +884,9 @@ describe('public marketing pages', () => {
|
|||
const selectedLaneSection = selectedLaneHeading.closest('section')
|
||||
expect(selectedLaneSection).toBeTruthy()
|
||||
expect(within(selectedLaneSection as HTMLElement).getByText('Confirm public launch status, operator/studio checkout posture, and support contact first.')).toBeTruthy()
|
||||
expect(within(selectedLaneSection as HTMLElement).getByRole('link', { name: 'Sign in for protected release lane' }).getAttribute('href')).toBe('/login?next=%2Fapp%2Fdownloads%3Fplatform%3Dwindows')
|
||||
expect(within(selectedLaneSection as HTMLElement).getByRole('link', { name: 'Sign in for protected notices' }).getAttribute('href')).toBe('/login?next=%2Fapp%2Fnotices')
|
||||
expect(within(selectedLaneSection as HTMLElement).getByRole('link', { name: 'Open pricing' }).getAttribute('href')).toBe('/pricing')
|
||||
expect(within(selectedLaneSection as HTMLElement).getByRole('link', { name: 'Open download center' }).getAttribute('href')).toBe('/download')
|
||||
expect(screen.getByText('If the desktop app is primary, why keep the web version?')).toBeTruthy()
|
||||
expect(screen.getByText('Is VR/controller support already fully finished?')).toBeTruthy()
|
||||
expect(screen.getByText('Do higher-dimensional selector choices persist between sessions?')).toBeTruthy()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
buildLoginPath,
|
||||
buildProtectedDownloadPath,
|
||||
buildRegisterPath,
|
||||
buildSupportPath,
|
||||
isExternalHref,
|
||||
normalizeDownloadPlatform,
|
||||
|
|
@ -25,4 +27,10 @@ describe('site route helpers', () => {
|
|||
expect(isExternalHref('https://checkout.paddle.com')).toBe(true)
|
||||
expect(isExternalHref('/support?topic=operator-access')).toBe(false)
|
||||
})
|
||||
|
||||
it('builds auth-preserving login and register links for protected destinations', () => {
|
||||
expect(buildLoginPath('/app/downloads?platform=windows')).toBe('/login?next=%2Fapp%2Fdownloads%3Fplatform%3Dwindows')
|
||||
expect(buildRegisterPath('/app/notices')).toBe('/register?next=%2Fapp%2Fnotices')
|
||||
expect(buildLoginPath('//evil.example')).toBe('/login?next=%2Fapp')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,7 +16,12 @@ import {
|
|||
releaseNotesUrl,
|
||||
} from '../site-config'
|
||||
import { deliverySurfaceCards } from '../site-data'
|
||||
import { isExternalHref } from '../site-routes'
|
||||
import {
|
||||
buildLoginPath,
|
||||
buildProtectedDownloadPath,
|
||||
buildRegisterPath,
|
||||
isExternalHref,
|
||||
} from '../site-routes'
|
||||
|
||||
export type SupportTopicGuide = {
|
||||
title: string
|
||||
|
|
@ -45,9 +50,9 @@ export const supportTopicGuidance: Record<string, SupportTopicGuide> = {
|
|||
'The public manual explains the browser-versus-desktop product split that launch copy must preserve.',
|
||||
],
|
||||
actions: [
|
||||
{ label: 'Sign in for protected release lane', to: buildLoginPath(buildProtectedDownloadPath('windows')) },
|
||||
{ label: 'Sign in for protected notices', to: buildLoginPath('/app/notices') },
|
||||
{ label: 'Open pricing', to: '/pricing' },
|
||||
{ label: 'Open download center', to: '/download' },
|
||||
{ label: 'Review notices', to: '/open-source-notices' },
|
||||
],
|
||||
},
|
||||
'operator-access': {
|
||||
|
|
@ -65,9 +70,9 @@ export const supportTopicGuidance: Record<string, SupportTopicGuide> = {
|
|||
'The public manual explains when the browser dashboard owns the next step and when the native runtime takes over.',
|
||||
],
|
||||
actions: [
|
||||
{ label: 'Open sign-in', to: '/login' },
|
||||
{ label: 'Open download posture', to: '/download' },
|
||||
{ label: 'Open public manual', to: '/docs' },
|
||||
{ label: 'Sign in for protected downloads', to: buildLoginPath(buildProtectedDownloadPath('windows')) },
|
||||
{ label: 'Create account for desktop access', to: buildRegisterPath(buildProtectedDownloadPath('windows')) },
|
||||
{ label: 'Open protected dashboard', to: buildLoginPath('/app') },
|
||||
],
|
||||
},
|
||||
'studio-rollout': {
|
||||
|
|
@ -85,9 +90,10 @@ export const supportTopicGuidance: Record<string, SupportTopicGuide> = {
|
|||
'Resources summarize higher-dimensional runtime lanes and operator playbooks for wider team review.',
|
||||
],
|
||||
actions: [
|
||||
{ label: 'Sign in for protected notices', to: buildLoginPath('/app/notices') },
|
||||
{ label: 'Sign in for protected browser access', to: buildLoginPath('/app/browser-access') },
|
||||
{ label: 'Open release notes', to: '/changelog' },
|
||||
{ label: 'Open resources', to: '/resources' },
|
||||
{ label: 'Review notices', to: '/open-source-notices' },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -762,6 +762,11 @@ export const resourceCollections = [
|
|||
] as const
|
||||
|
||||
export const changelogEntries = [
|
||||
{
|
||||
date: 'June 24, 2026',
|
||||
title: 'Public support topics now preserve exact protected destinations through auth',
|
||||
details: 'Support-topic quick routes no longer stop at generic public guidance. Launch-readiness, operator-access, and studio-rollout actions now deep-link into the correct protected dashboard, download, browser-access, or notices destination through auth-preserving next-target links.',
|
||||
},
|
||||
{
|
||||
date: 'June 24, 2026',
|
||||
title: 'Support-topic routing now spans support, docs, and resources with the same authority split',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { normalizeNextPath } from './auth/next-path'
|
||||
|
||||
export type DownloadPlatformKey = 'windows' | 'macos' | 'linux'
|
||||
|
||||
const DOWNLOAD_PLATFORM_LABELS: Record<DownloadPlatformKey, string> = {
|
||||
|
|
@ -43,6 +45,19 @@ export function buildSupportPath(topic?: string): string {
|
|||
return `/support?${params.toString()}`
|
||||
}
|
||||
|
||||
function buildAuthPath(mode: 'login' | 'register', nextPath: string | null | undefined, fallback = '/app') {
|
||||
const normalizedNextPath = normalizeNextPath(nextPath, fallback)
|
||||
return `/${mode}?next=${encodeURIComponent(normalizedNextPath)}`
|
||||
}
|
||||
|
||||
export function buildLoginPath(nextPath?: string | null, fallback?: string): string {
|
||||
return buildAuthPath('login', nextPath, fallback)
|
||||
}
|
||||
|
||||
export function buildRegisterPath(nextPath?: string | null, fallback?: string): string {
|
||||
return buildAuthPath('register', nextPath, fallback)
|
||||
}
|
||||
|
||||
export function isExternalHref(value: string): boolean {
|
||||
return /^[a-z][a-z0-9+.-]*:/i.test(value)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue