From 741397df0afd54a66e2c9c018669f433ea825556 Mon Sep 17 00:00:00 2001 From: silverfoxy Date: Sun, 10 Aug 2025 10:06:04 -0400 Subject: [PATCH] Fix json import/export on Android --- web/src/pages/ProfileList/index.jsx | 49 ++++++++++++++++------- web/src/pages/Settings/index.jsx | 24 +++++++---- web/src/pages/ShotHistory/HistoryCard.jsx | 25 ++++++++---- 3 files changed, 69 insertions(+), 29 deletions(-) diff --git a/web/src/pages/ProfileList/index.jsx b/web/src/pages/ProfileList/index.jsx index 781ed2e2c6..1b07ea55da 100644 --- a/web/src/pages/ProfileList/index.jsx +++ b/web/src/pages/ProfileList/index.jsx @@ -64,13 +64,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 ( @@ -292,13 +302,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 ff78fc2892..7e02a3cd53 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 51db0e4a1e..ae70a4ae2f 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 (