diff --git a/web/src/pages/ProfileList/index.jsx b/web/src/pages/ProfileList/index.jsx index 956815b4e7..4a29ca1071 100644 --- a/web/src/pages/ProfileList/index.jsx +++ b/web/src/pages/ProfileList/index.jsx @@ -65,13 +65,23 @@ function ProfileCard({ delete download.id; delete download.selected; delete download.favorite; - const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(download, undefined, 2))}`; - const downloadAnchorNode = document.createElement('a'); - downloadAnchorNode.setAttribute('href', dataStr); - downloadAnchorNode.setAttribute('download', `${data.id}.json`); - document.body.appendChild(downloadAnchorNode); // required for firefox - downloadAnchorNode.click(); - downloadAnchorNode.remove(); + + const jsonStr = JSON.stringify(download, undefined, 2); + const blob = new Blob([jsonStr], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = 'profile-' + data.id + '.json'; + a.target = '_blank'; + a.rel = 'noopener'; + + document.body.appendChild(a); + setTimeout(() => { + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, 10); }, [data]); return ( @@ -297,13 +307,24 @@ export function ProfileList() { delete ep.favorite; return ep; }); - const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(exportedProfiles, undefined, 2))}`; - const downloadAnchorNode = document.createElement('a'); - downloadAnchorNode.setAttribute('href', dataStr); - downloadAnchorNode.setAttribute('download', 'profiles.json'); - document.body.appendChild(downloadAnchorNode); - downloadAnchorNode.click(); - downloadAnchorNode.remove(); + + const jsonStr = JSON.stringify(exportedProfiles, undefined, 2); + const blob = new Blob([jsonStr], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = 'profiles.json'; + a.target = '_blank'; + a.rel = 'noopener'; + + document.body.appendChild(a); + setTimeout(() => { + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, 10); }, [profiles]); const onUpload = function (evt) { diff --git a/web/src/pages/Settings/index.jsx b/web/src/pages/Settings/index.jsx index be5da35145..3a79e890c0 100644 --- a/web/src/pages/Settings/index.jsx +++ b/web/src/pages/Settings/index.jsx @@ -132,13 +132,23 @@ export function Settings() { ); const onExport = useCallback(() => { - const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(formData, undefined, 2))}`; - const downloadAnchorNode = document.createElement('a'); - downloadAnchorNode.setAttribute('href', dataStr); - downloadAnchorNode.setAttribute('download', 'settings.json'); - document.body.appendChild(downloadAnchorNode); // required for firefox - downloadAnchorNode.click(); - downloadAnchorNode.remove(); + const jsonStr = JSON.stringify(formData, undefined, 2); + const blob = new Blob([jsonStr], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = 'settings.json'; + a.target = '_blank'; + a.rel = 'noopener'; + + document.body.appendChild(a); + setTimeout(() => { + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, 10); }, [formData]); const onUpload = function (evt) { diff --git a/web/src/pages/ShotHistory/HistoryCard.jsx b/web/src/pages/ShotHistory/HistoryCard.jsx index d254b313ee..f8a7cbdff2 100644 --- a/web/src/pages/ShotHistory/HistoryCard.jsx +++ b/web/src/pages/ShotHistory/HistoryCard.jsx @@ -5,14 +5,23 @@ import { HistoryChart } from './HistoryChart.jsx'; export default function HistoryCard({ shot, onDelete }) { const date = new Date(shot.timestamp * 1000); const onExport = useCallback(() => { - const dataStr = - 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(shot, undefined, 2)); - const downloadAnchorNode = document.createElement('a'); - downloadAnchorNode.setAttribute('href', dataStr); - downloadAnchorNode.setAttribute('download', shot.id + '.json'); - document.body.appendChild(downloadAnchorNode); // required for firefox - downloadAnchorNode.click(); - downloadAnchorNode.remove(); + const jsonStr = JSON.stringify(shot, undefined, 2); + const blob = new Blob([jsonStr], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = 'shot-' + shot.id + '.json'; + a.target = '_blank'; + a.rel = 'noopener'; + + document.body.appendChild(a); + setTimeout(() => { + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, 10); }); return (