Skip to content

Commit fc0f400

Browse files
committed
bring back copy link; also allows gpx download (fixes #28)
1 parent 2316813 commit fc0f400

6 files changed

Lines changed: 67 additions & 7 deletions

File tree

src/custom.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ declare module '*.svg'
33
declare module '*.png'
44
declare module 'custom-model-editor/src/index'
55

6+
// Optional global injected by the Capacitor wrapper (src/app.js in graphhopper-maps-capacitor)
7+
// to route file downloads through native Filesystem.writeFile + Share. Absent in browser builds.
8+
interface Window {
9+
ghSaveFile?: (args: { fileName: string; mimeType: string; fileContents: string }) => Promise<unknown>
10+
}
11+
612
declare module 'config' {
713
interface ProfileGroup {
814
readonly options: { profile: string }[]

src/sidebar/RoutingResults.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -745,14 +745,22 @@ function downloadGPX(path: Path, settings: Settings) {
745745

746746
xmlString += '</gpx>'
747747

748-
const tmpElement = document.createElement('a')
749-
const file = new Blob([xmlString], { type: 'application/gpx+xml' })
750-
tmpElement.href = URL.createObjectURL(file)
751748
const date = new Date()
752-
tmpElement.download = `GraphHopper-Track-${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(
749+
const fileName = `GraphHopper-Track-${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(
753750
date.getUTCDate(),
754751
)}-${metersToTextForFile(path.distance, settings.showDistanceInMiles)}.gpx`
755-
tmpElement.click()
752+
const mimeType = 'application/gpx+xml'
753+
754+
if (window.ghSaveFile) {
755+
// Capacitor wrapper handles native file save + share.
756+
window.ghSaveFile({ fileName, mimeType, fileContents: xmlString })
757+
} else {
758+
const tmpElement = document.createElement('a')
759+
const file = new Blob([xmlString], { type: mimeType })
760+
tmpElement.href = URL.createObjectURL(file)
761+
tmpElement.download = fileName
762+
tmpElement.click()
763+
}
756764
}
757765

758766
function pad(value: number) {

src/sidebar/SettingsBox.module.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,30 @@
55
border-bottom: 1px lightgray solid;
66
}
77

8+
.copyLinkRow {
9+
cursor: pointer;
10+
display: grid;
11+
grid-template-columns: 50px auto;
12+
align-items: center;
13+
padding-bottom: 20px;
14+
font-size: 14px;
15+
color: gray;
16+
}
17+
18+
.copyLinkRow div {
19+
color: #5b616a;
20+
}
21+
22+
.copyLinkRow:hover div,
23+
.copyLinkRow:hover svg {
24+
color: black;
25+
}
26+
27+
.copyLinkRow svg {
28+
width: 30px;
29+
height: 30px;
30+
}
31+
832
.settingsTable {
933
display: flex;
1034
flex-direction: column;

src/sidebar/SettingsBox.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { tr } from '@/translation/Translation'
55
import PlainButton from '@/PlainButton'
66
import OnIcon from '@/sidebar/toggle_on.svg'
77
import OffIcon from '@/sidebar/toggle_off.svg'
8-
import { useContext } from 'react'
8+
import LinkIcon from '@/sidebar/link.svg'
9+
import { useContext, useState } from 'react'
910
import { SettingsContext } from '@/contexts/SettingsContext'
1011
import { RoutingProfile } from '@/api/graphhopper'
1112
import * as config from 'config'
@@ -14,6 +15,7 @@ import { clearRecentLocations } from '@/sidebar/search/RecentLocations'
1415

1516
export default function SettingsBox({ profile }: { profile: RoutingProfile }) {
1617
const settings = useContext(SettingsContext)
18+
const [showCopiedInfo, setShowCopiedInfo] = useState(false)
1719

1820
function setProfile(n: string) {
1921
Dispatcher.dispatch(new SetVehicleProfile({ name: profile.name === n ? 'car' : n }))
@@ -23,6 +25,25 @@ export default function SettingsBox({ profile }: { profile: RoutingProfile }) {
2325
const group = config.profile_group_mapping[groupName]
2426
return (
2527
<div className={styles.parent}>
28+
<div
29+
className={styles.copyLinkRow}
30+
onClick={() => {
31+
let url = window.location.href
32+
// Capacitor serves the app from https://localhost or http://localhost — rewrite to the
33+
// public URL so the shared link is openable on other devices.
34+
if (window.location.hostname === 'localhost')
35+
url = 'https://navi.graphhopper.org' + window.location.pathname + window.location.search
36+
navigator.clipboard.writeText(url)
37+
if (navigator.share) navigator.share({ url })
38+
setShowCopiedInfo(true)
39+
setTimeout(() => setShowCopiedInfo(false), 2500)
40+
}}
41+
>
42+
<PlainButton>
43+
<LinkIcon />
44+
</PlainButton>
45+
<div>{showCopiedInfo ? tr('Copied!') : tr('Copy Link')}</div>
46+
</div>
2647
{groupName && <span className={styles.groupProfileOptionsHeader}>{tr(groupName + '_settings')}</span>}
2748
{groupName && (
2849
<div className={styles.settingsTable}>

src/sidebar/link.svg

Lines changed: 1 addition & 0 deletions
Loading

src/sidebar/navigation.svg

Lines changed: 1 addition & 1 deletion
Loading

0 commit comments

Comments
 (0)