mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(maps): handing a place to a maps app leaves no blank window behind (#2218)
In an installed app, opening a maps target in a new context left an empty window: the platform switches to the maps application before that context paints anything, and coming back lands on a blank page the user has to dismiss before TREK is usable again. The handover now happens from the page the user is already on when TREK runs as an installed app, which is where there is no tab strip to close a stray window from. A browser tab still opens a tab, because there a link should.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { getNavigationTargets, showsAppleMaps } from './placeNavigation'
|
||||
import { getNavigationTargets, openNavigationTarget, showsAppleMaps } from './placeNavigation'
|
||||
import type { Place } from '../../types'
|
||||
|
||||
// FE-PLANNER-NAV-001 to FE-PLANNER-NAV-008
|
||||
@@ -105,3 +105,42 @@ describe('getNavigationTargets', () => {
|
||||
expect(targets.find(t => t.id === 'waze')!.url).toBe('https://waze.com/ul?ll=48.2038,16.3616&navigate=yes')
|
||||
})
|
||||
})
|
||||
|
||||
describe('openNavigationTarget', () => {
|
||||
const target = { id: 'google', label: 'Google Maps', url: 'https://maps.google.com/?q=1,2' } as const
|
||||
|
||||
afterEach(() => { vi.unstubAllGlobals(); vi.restoreAllMocks() })
|
||||
|
||||
function stubDisplayMode(installed: boolean, iosStandalone = false) {
|
||||
const open = vi.fn()
|
||||
const assigned: string[] = []
|
||||
vi.stubGlobal('window', {
|
||||
matchMedia: (q: string) => ({ matches: installed && q.includes('display-mode') }),
|
||||
navigator: { standalone: iosStandalone },
|
||||
open,
|
||||
get location() { return { get href() { return '' }, set href(v: string) { assigned.push(v) } } },
|
||||
})
|
||||
return { open, assigned }
|
||||
}
|
||||
|
||||
it('FE-PLANNER-NAV-101: a browser tab keeps opening a tab', () => {
|
||||
const { open, assigned } = stubDisplayMode(false)
|
||||
openNavigationTarget(target)
|
||||
expect(open).toHaveBeenCalledWith(target.url, '_blank', 'noopener,noreferrer')
|
||||
expect(assigned).toEqual([])
|
||||
})
|
||||
|
||||
it('FE-PLANNER-NAV-102: an installed app hands over from the current context, leaving no blank window (#2218)', () => {
|
||||
const { open, assigned } = stubDisplayMode(true)
|
||||
openNavigationTarget(target)
|
||||
expect(open).not.toHaveBeenCalled()
|
||||
expect(assigned).toEqual([target.url])
|
||||
})
|
||||
|
||||
it('FE-PLANNER-NAV-103: an iOS home-screen app is recognised without the display-mode query', () => {
|
||||
const { open, assigned } = stubDisplayMode(false, true)
|
||||
openNavigationTarget(target)
|
||||
expect(open).not.toHaveBeenCalled()
|
||||
expect(assigned).toEqual([target.url])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -95,7 +95,28 @@ export function getNavigationTargets(
|
||||
return targets
|
||||
}
|
||||
|
||||
/** Opens a target the way every external link in the planner is opened. */
|
||||
/**
|
||||
* Hands the place over to a maps application.
|
||||
*
|
||||
* In a browser tab this opens a tab, which is what a link should do. In an
|
||||
* installed app it must not: the maps application takes over from the new
|
||||
* context before it paints, so what the user comes back to is an empty window
|
||||
* they have to dismiss before TREK is usable again (#2218). Navigating the
|
||||
* current context instead means the handover happens from the page the user is
|
||||
* already on, and returning lands them back where they were, because the app
|
||||
* shell was never replaced by the time the platform switched away.
|
||||
*/
|
||||
export function openNavigationTarget(target: NavigationTarget): void {
|
||||
window.open(target.url, '_blank', 'noopener,noreferrer')
|
||||
if (isInstalledApp()) window.location.href = target.url
|
||||
else window.open(target.url, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
|
||||
/** True in a display mode that has no tab strip to close a stray window from. */
|
||||
function isInstalledApp(): boolean {
|
||||
if (typeof window === 'undefined') return false
|
||||
const standalone = ['standalone', 'fullscreen', 'minimal-ui'].some(
|
||||
mode => window.matchMedia?.(`(display-mode: ${mode})`).matches,
|
||||
)
|
||||
// iOS predates the display-mode query for home-screen apps.
|
||||
return standalone || (window.navigator as { standalone?: boolean }).standalone === true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user