mirror of
https://github.com/Qortal/qortal-mobile.git
synced 2025-06-06 00:16:59 +00:00
added import/export for saved settings
This commit is contained in:
parent
fd907e1c9c
commit
3d4911ecb4
@ -112,6 +112,7 @@ import {
|
|||||||
canSaveSettingToQdnAtom,
|
canSaveSettingToQdnAtom,
|
||||||
fullScreenAtom,
|
fullScreenAtom,
|
||||||
hasSettingsChangedAtom,
|
hasSettingsChangedAtom,
|
||||||
|
isUsingImportExportSettingsAtom,
|
||||||
oldPinnedAppsAtom,
|
oldPinnedAppsAtom,
|
||||||
settingsLocalLastUpdatedAtom,
|
settingsLocalLastUpdatedAtom,
|
||||||
settingsQDNLastUpdatedAtom,
|
settingsQDNLastUpdatedAtom,
|
||||||
@ -509,6 +510,7 @@ function App() {
|
|||||||
settingsLocalLastUpdatedAtom
|
settingsLocalLastUpdatedAtom
|
||||||
);
|
);
|
||||||
const resetAtomOldPinnedAppsAtom = useResetRecoilState(oldPinnedAppsAtom);
|
const resetAtomOldPinnedAppsAtom = useResetRecoilState(oldPinnedAppsAtom);
|
||||||
|
const resetAtomIsUsingImportExportSettingsAtom = useResetRecoilState(isUsingImportExportSettingsAtom)
|
||||||
|
|
||||||
const resetAllRecoil = () => {
|
const resetAllRecoil = () => {
|
||||||
resetAtomSortablePinnedAppsAtom();
|
resetAtomSortablePinnedAppsAtom();
|
||||||
@ -516,6 +518,7 @@ function App() {
|
|||||||
resetAtomSettingsQDNLastUpdatedAtom();
|
resetAtomSettingsQDNLastUpdatedAtom();
|
||||||
resetAtomSettingsLocalLastUpdatedAtom();
|
resetAtomSettingsLocalLastUpdatedAtom();
|
||||||
resetAtomOldPinnedAppsAtom();
|
resetAtomOldPinnedAppsAtom();
|
||||||
|
resetAtomIsUsingImportExportSettingsAtom();
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isMobile) return;
|
if (!isMobile) return;
|
||||||
|
@ -135,4 +135,9 @@ export const blobKeySelector = selectorFamily({
|
|||||||
export const selectedGroupIdAtom = atom({
|
export const selectedGroupIdAtom = atom({
|
||||||
key: 'selectedGroupIdAtom',
|
key: 'selectedGroupIdAtom',
|
||||||
default: null,
|
default: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const isUsingImportExportSettingsAtom = atom({
|
||||||
|
key: 'isUsingImportExportSettingsAtom',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
|
@ -137,6 +137,16 @@ export const useFetchResources = () => {
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
if(res?.status === 'DOWNLOADED'){
|
||||||
|
const url = `${getBaseApiReact()}/arbitrary/resource/status/${service}/${name}/${identifier}?build=true`;
|
||||||
|
const resCall = await fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
res = await resCall.json();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
callFunction()
|
callFunction()
|
||||||
intervalId.current = setInterval(async () => {
|
intervalId.current = setInterval(async () => {
|
||||||
|
@ -33,8 +33,12 @@ import {
|
|||||||
sortablePinnedAppsAtom,
|
sortablePinnedAppsAtom,
|
||||||
} from "../../atoms/global";
|
} from "../../atoms/global";
|
||||||
|
|
||||||
export function saveToLocalStorage(key, subKey, newValue) {
|
export function saveToLocalStorage(key, subKey, newValue, otherRootData = {}, deleteWholeKey) {
|
||||||
try {
|
try {
|
||||||
|
if(deleteWholeKey){
|
||||||
|
localStorage.setItem(key, null);
|
||||||
|
return
|
||||||
|
}
|
||||||
// Fetch existing data
|
// Fetch existing data
|
||||||
const existingData = localStorage.getItem(key);
|
const existingData = localStorage.getItem(key);
|
||||||
let combinedData = {};
|
let combinedData = {};
|
||||||
@ -45,12 +49,14 @@ export function saveToLocalStorage(key, subKey, newValue) {
|
|||||||
// Merge with the new data under the subKey
|
// Merge with the new data under the subKey
|
||||||
combinedData = {
|
combinedData = {
|
||||||
...parsedData,
|
...parsedData,
|
||||||
|
...otherRootData,
|
||||||
timestamp: Date.now(), // Update the root timestamp
|
timestamp: Date.now(), // Update the root timestamp
|
||||||
[subKey]: newValue, // Assuming the data is an array
|
[subKey]: newValue, // Assuming the data is an array
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// If no existing data, just use the new data under the subKey
|
// If no existing data, just use the new data under the subKey
|
||||||
combinedData = {
|
combinedData = {
|
||||||
|
...otherRootData,
|
||||||
timestamp: Date.now(), // Set the initial root timestamp
|
timestamp: Date.now(), // Set the initial root timestamp
|
||||||
[subKey]: newValue,
|
[subKey]: newValue,
|
||||||
};
|
};
|
||||||
|
@ -993,7 +993,8 @@ const sendMessage = async ()=> {
|
|||||||
<AppViewerContainer app={{
|
<AppViewerContainer app={{
|
||||||
tabId: '5558588',
|
tabId: '5558588',
|
||||||
name: 'Q-Manager',
|
name: 'Q-Manager',
|
||||||
service: 'APP'
|
service: 'APP',
|
||||||
|
path: `?groupId=${selectedGroup}`
|
||||||
}} isSelected />
|
}} isSelected />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -266,9 +266,7 @@ export const AttachmentCard = ({
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
{resourceData?.fileName && (
|
{resourceData?.fileName && (
|
||||||
<>
|
<>
|
||||||
<Typography sx={{
|
<Typography>{resourceDetails?.status?.status === 'DOWNLOADED' ? 'BUILDING' : resourceDetails?.status?.status}</Typography>
|
||||||
fontSize: '14px'
|
|
||||||
}}>{resourceData?.fileName}</Typography>
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -4,12 +4,13 @@ import isEqual from "lodash/isEqual"; // Import deep comparison utility
|
|||||||
import {
|
import {
|
||||||
canSaveSettingToQdnAtom,
|
canSaveSettingToQdnAtom,
|
||||||
hasSettingsChangedAtom,
|
hasSettingsChangedAtom,
|
||||||
|
isUsingImportExportSettingsAtom,
|
||||||
oldPinnedAppsAtom,
|
oldPinnedAppsAtom,
|
||||||
settingsLocalLastUpdatedAtom,
|
settingsLocalLastUpdatedAtom,
|
||||||
settingsQDNLastUpdatedAtom,
|
settingsQDNLastUpdatedAtom,
|
||||||
sortablePinnedAppsAtom,
|
sortablePinnedAppsAtom,
|
||||||
} from "../../atoms/global";
|
} from "../../atoms/global";
|
||||||
import { Box, ButtonBase, Popover, Typography } from "@mui/material";
|
import { Box, Button, ButtonBase, Popover, Typography } from "@mui/material";
|
||||||
import { objectToBase64 } from "../../qdn/encryption/group-encryption";
|
import { objectToBase64 } from "../../qdn/encryption/group-encryption";
|
||||||
import { MyContext } from "../../App";
|
import { MyContext } from "../../App";
|
||||||
import { getFee } from "../../background";
|
import { getFee } from "../../background";
|
||||||
@ -19,6 +20,43 @@ import { IconWrapper } from "../Desktop/DesktopFooter";
|
|||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from "../../common/Spacer";
|
||||||
import { LoadingButton } from "@mui/lab";
|
import { LoadingButton } from "@mui/lab";
|
||||||
import { saveToLocalStorage } from "../Apps/AppsNavBar";
|
import { saveToLocalStorage } from "../Apps/AppsNavBar";
|
||||||
|
import { decryptData, encryptData } from "../../qortalRequests/get";
|
||||||
|
import { saveFileToDiskGeneric } from "../../utils/generateWallet/generateWallet";
|
||||||
|
import { base64ToUint8Array, uint8ArrayToObject } from "../../backgroundFunctions/encryption";
|
||||||
|
|
||||||
|
export const handleImportClick = async () => {
|
||||||
|
const fileInput = document.createElement('input');
|
||||||
|
fileInput.type = 'file';
|
||||||
|
fileInput.accept = '.base64,.txt';
|
||||||
|
|
||||||
|
// Create a promise to handle file selection and reading synchronously
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
fileInput.onchange = () => {
|
||||||
|
const file = fileInput.files[0];
|
||||||
|
if (!file) {
|
||||||
|
reject(new Error('No file selected'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
resolve(e.target.result); // Resolve with the file content
|
||||||
|
};
|
||||||
|
reader.onerror = () => {
|
||||||
|
reject(new Error('Error reading file'));
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsText(file); // Read the file as text (Base64 string)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Trigger the file input dialog
|
||||||
|
fileInput.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const Save = ({ isDesktop, disableWidth, myName }) => {
|
export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||||
const [pinnedApps, setPinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
const [pinnedApps, setPinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
||||||
const [settingsQdnLastUpdated, setSettingsQdnLastUpdated] = useRecoilState(
|
const [settingsQdnLastUpdated, setSettingsQdnLastUpdated] = useRecoilState(
|
||||||
@ -28,7 +66,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
settingsLocalLastUpdatedAtom
|
settingsLocalLastUpdatedAtom
|
||||||
);
|
);
|
||||||
const setHasSettingsChangedAtom = useSetRecoilState(hasSettingsChangedAtom);
|
const setHasSettingsChangedAtom = useSetRecoilState(hasSettingsChangedAtom);
|
||||||
|
const [isUsingImportExportSettings, setIsUsingImportExportSettings] = useRecoilState(isUsingImportExportSettingsAtom);
|
||||||
const [canSave] = useRecoilState(canSaveSettingToQdnAtom);
|
const [canSave] = useRecoilState(canSaveSettingToQdnAtom);
|
||||||
const [openSnack, setOpenSnack] = useState(false);
|
const [openSnack, setOpenSnack] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@ -212,14 +250,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
vertical: "top",
|
vertical: "top",
|
||||||
horizontal: "center",
|
horizontal: "center",
|
||||||
}}
|
}}
|
||||||
// sx={{
|
|
||||||
// width: "300px",
|
|
||||||
// maxWidth: "90%",
|
|
||||||
// maxHeight: "80%",
|
|
||||||
// overflow: "auto",
|
|
||||||
// }}
|
|
||||||
>
|
>
|
||||||
<Box
|
{isUsingImportExportSettings && (
|
||||||
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
padding: "15px",
|
padding: "15px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -228,27 +261,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
width: '100%'
|
width: '100%'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!myName ? (
|
<Box
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "14px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
You need a registered Qortal name to save your pinned apps to QDN.
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{hasChanged && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -261,124 +274,45 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
fontSize: "14px",
|
fontSize: "14px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
You have unsaved changes to your pinned apps. Save them to QDN.
|
You are using the export/import way of saving settings.
|
||||||
</Typography>
|
</Typography>
|
||||||
<Spacer height="10px" />
|
<Spacer height="40px" />
|
||||||
<LoadingButton
|
<Button
|
||||||
sx={{
|
size="small"
|
||||||
backgroundColor: "var(--green)",
|
onClick={()=> {
|
||||||
color: "black",
|
saveToLocalStorage("ext_saved_settings_import_export", "sortablePinnedApps", null, true);
|
||||||
opacity: 0.7,
|
setIsUsingImportExportSettings(false)
|
||||||
fontWeight: 'bold',
|
}}
|
||||||
"&:hover": {
|
variant="contained"
|
||||||
backgroundColor: "var(--green)",
|
sx={{
|
||||||
color: "black",
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
size="small"
|
|
||||||
loading={isLoading}
|
|
||||||
onClick={saveToQdn}
|
|
||||||
variant="contained"
|
|
||||||
>
|
|
||||||
Save to QDN
|
|
||||||
</LoadingButton>
|
|
||||||
<Spacer height="20px" />
|
|
||||||
{!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated > 0 && (
|
|
||||||
<>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "14px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Don't like your current local changes? Would you like to
|
|
||||||
reset to your saved QDN pinned apps?
|
|
||||||
</Typography>
|
|
||||||
<Spacer height="10px" />
|
|
||||||
<LoadingButton
|
|
||||||
size="small"
|
|
||||||
loading={isLoading}
|
|
||||||
onClick={revertChanges}
|
|
||||||
variant="contained"
|
|
||||||
sx={{
|
|
||||||
backgroundColor: "var(--danger)",
|
|
||||||
color: "black",
|
|
||||||
fontWeight: 'bold',
|
|
||||||
opacity: 0.7,
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: "var(--danger)",
|
|
||||||
color: "black",
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Revert to QDN
|
|
||||||
</LoadingButton>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated === 0 && (
|
|
||||||
<>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "14px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Don't like your current local changes? Would you like to
|
|
||||||
reset to the default pinned apps?
|
|
||||||
</Typography>
|
|
||||||
<Spacer height="10px" />
|
|
||||||
<LoadingButton
|
|
||||||
loading={isLoading}
|
|
||||||
onClick={revertChanges}
|
|
||||||
variant="contained"
|
|
||||||
>
|
|
||||||
Revert to default
|
|
||||||
</LoadingButton>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated === -100 && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "14px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
The app was unable to download your existing QDN-saved pinned
|
|
||||||
apps. Would you like to overwrite those changes?
|
|
||||||
</Typography>
|
|
||||||
<Spacer height="10px" />
|
|
||||||
<LoadingButton
|
|
||||||
size="small"
|
|
||||||
loading={isLoading}
|
|
||||||
onClick={saveToQdn}
|
|
||||||
variant="contained"
|
|
||||||
sx={{
|
|
||||||
backgroundColor: "var(--danger)",
|
|
||||||
color: "black",
|
|
||||||
fontWeight: 'bold',
|
|
||||||
opacity: 0.7,
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: "var(--danger)",
|
backgroundColor: "var(--danger)",
|
||||||
color: "black",
|
color: "black",
|
||||||
opacity: 1,
|
fontWeight: 'bold',
|
||||||
},
|
opacity: 0.7,
|
||||||
}}
|
"&:hover": {
|
||||||
>
|
backgroundColor: "var(--danger)",
|
||||||
Overwrite to QDN
|
color: "black",
|
||||||
</LoadingButton>
|
opacity: 1,
|
||||||
</Box>
|
},
|
||||||
)}
|
}}
|
||||||
{!hasChanged && (
|
>
|
||||||
|
Use QDN saving
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{!isUsingImportExportSettings && (
|
||||||
<Box
|
<Box
|
||||||
|
sx={{
|
||||||
|
padding: "15px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!myName ? (
|
||||||
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -391,15 +325,237 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
fontSize: "14px",
|
fontSize: "14px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
You currently do not have any changes to your pinned apps
|
You need a registered Qortal name to save your pinned apps to QDN.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
</Box>
|
||||||
</Box>
|
) : (
|
||||||
)}
|
<>
|
||||||
</>
|
{hasChanged && (
|
||||||
)}
|
<Box
|
||||||
|
sx={{
|
||||||
</Box>
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "14px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
You have unsaved changes to your pinned apps. Save them to QDN.
|
||||||
|
</Typography>
|
||||||
|
<Spacer height="10px" />
|
||||||
|
<LoadingButton
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "var(--green)",
|
||||||
|
color: "black",
|
||||||
|
opacity: 0.7,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "var(--green)",
|
||||||
|
color: "black",
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
loading={isLoading}
|
||||||
|
onClick={saveToQdn}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Save to QDN
|
||||||
|
</LoadingButton>
|
||||||
|
<Spacer height="20px" />
|
||||||
|
{!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated > 0 && (
|
||||||
|
<>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "14px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Don't like your current local changes? Would you like to
|
||||||
|
reset to your saved QDN pinned apps?
|
||||||
|
</Typography>
|
||||||
|
<Spacer height="10px" />
|
||||||
|
<LoadingButton
|
||||||
|
size="small"
|
||||||
|
loading={isLoading}
|
||||||
|
onClick={revertChanges}
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "var(--danger)",
|
||||||
|
color: "black",
|
||||||
|
fontWeight: 'bold',
|
||||||
|
opacity: 0.7,
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "var(--danger)",
|
||||||
|
color: "black",
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Revert to QDN
|
||||||
|
</LoadingButton>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated === 0 && (
|
||||||
|
<>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "14px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Don't like your current local changes? Would you like to
|
||||||
|
reset to the default pinned apps?
|
||||||
|
</Typography>
|
||||||
|
<Spacer height="10px" />
|
||||||
|
<LoadingButton
|
||||||
|
loading={isLoading}
|
||||||
|
onClick={revertChanges}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Revert to default
|
||||||
|
</LoadingButton>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated === -100 && isUsingImportExportSettings !== true && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "14px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
The app was unable to download your existing QDN-saved pinned
|
||||||
|
apps. Would you like to overwrite those changes?
|
||||||
|
</Typography>
|
||||||
|
<Spacer height="10px" />
|
||||||
|
<LoadingButton
|
||||||
|
size="small"
|
||||||
|
loading={isLoading}
|
||||||
|
onClick={saveToQdn}
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "var(--danger)",
|
||||||
|
color: "black",
|
||||||
|
fontWeight: 'bold',
|
||||||
|
opacity: 0.7,
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "var(--danger)",
|
||||||
|
color: "black",
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Overwrite to QDN
|
||||||
|
</LoadingButton>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{!hasChanged && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "14px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
You currently do not have any changes to your pinned apps
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
padding: "15px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '10px',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
width: '100%'
|
||||||
|
}}>
|
||||||
|
<ButtonBase onClick={async () => {
|
||||||
|
try {
|
||||||
|
const fileContent = await handleImportClick();
|
||||||
|
const decryptedData = await decryptData({
|
||||||
|
encryptedData: fileContent,
|
||||||
|
});
|
||||||
|
const decryptToUnit8ArraySubject =
|
||||||
|
base64ToUint8Array(decryptedData);
|
||||||
|
const responseData = uint8ArrayToObject(
|
||||||
|
decryptToUnit8ArraySubject
|
||||||
|
);
|
||||||
|
if(Array.isArray(responseData)){
|
||||||
|
saveToLocalStorage("ext_saved_settings_import_export", "sortablePinnedApps", responseData, {
|
||||||
|
isUsingImportExport: true
|
||||||
|
});
|
||||||
|
setPinnedApps(responseData)
|
||||||
|
setOldPinnedApps(responseData)
|
||||||
|
setIsUsingImportExportSettings(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error", error);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
|
||||||
|
Import
|
||||||
|
</ButtonBase>
|
||||||
|
<ButtonBase onClick={async () => {
|
||||||
|
try {
|
||||||
|
const data64 = await objectToBase64(pinnedApps);
|
||||||
|
|
||||||
|
const encryptedData = await encryptData({
|
||||||
|
data64,
|
||||||
|
});
|
||||||
|
const blob = new Blob([encryptedData], {
|
||||||
|
type: "text/plain",
|
||||||
|
});
|
||||||
|
|
||||||
|
const timestamp = new Date()
|
||||||
|
.toISOString()
|
||||||
|
.replace(/:/g, "-"); // Safe timestamp for filenames
|
||||||
|
const filename = `qortal-new-ui-backup-settings-${timestamp}.txt`;
|
||||||
|
await saveFileToDiskGeneric(blob, filename)
|
||||||
|
setInfoSnack({
|
||||||
|
type: "success",
|
||||||
|
message: "saved in INTERNAL storage under DOCUMENTS",
|
||||||
|
});
|
||||||
|
setOpenSnack(true);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log('error', error)
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
Export
|
||||||
|
</ButtonBase>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
</Popover>
|
</Popover>
|
||||||
<CustomizedSnackbars
|
<CustomizedSnackbars
|
||||||
duration={3500}
|
duration={3500}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useEffect } from 'react'
|
import React, { useCallback, useEffect } from 'react'
|
||||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||||
import { canSaveSettingToQdnAtom, oldPinnedAppsAtom, settingsLocalLastUpdatedAtom, settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom } from './atoms/global';
|
import { canSaveSettingToQdnAtom, isUsingImportExportSettingsAtom, oldPinnedAppsAtom, settingsLocalLastUpdatedAtom, settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom } from './atoms/global';
|
||||||
import { getArbitraryEndpointReact, getBaseApiReact } from './App';
|
import { getArbitraryEndpointReact, getBaseApiReact } from './App';
|
||||||
import { decryptResource } from './components/Group/Group';
|
import { decryptResource } from './components/Group/Group';
|
||||||
import { base64ToUint8Array, uint8ArrayToObject } from './backgroundFunctions/encryption';
|
import { base64ToUint8Array, uint8ArrayToObject } from './backgroundFunctions/encryption';
|
||||||
@ -59,7 +59,7 @@ export const useQortalGetSaveSettings = (myName, isAuthenticated) => {
|
|||||||
const setSettingsQDNLastUpdated = useSetRecoilState(settingsQDNLastUpdatedAtom);
|
const setSettingsQDNLastUpdated = useSetRecoilState(settingsQDNLastUpdatedAtom);
|
||||||
const [settingsLocalLastUpdated] = useRecoilState(settingsLocalLastUpdatedAtom);
|
const [settingsLocalLastUpdated] = useRecoilState(settingsLocalLastUpdatedAtom);
|
||||||
const [oldPinnedApps, setOldPinnedApps] = useRecoilState(oldPinnedAppsAtom)
|
const [oldPinnedApps, setOldPinnedApps] = useRecoilState(oldPinnedAppsAtom)
|
||||||
|
const [isUsingImportExportSettings] = useRecoilState(isUsingImportExportSettingsAtom);
|
||||||
const getSavedSettings = useCallback(async (myName, settingsLocalLastUpdated)=> {
|
const getSavedSettings = useCallback(async (myName, settingsLocalLastUpdated)=> {
|
||||||
try {
|
try {
|
||||||
const {hasPublishRecord, timestamp} = await getPublishRecord(myName)
|
const {hasPublishRecord, timestamp} = await getPublishRecord(myName)
|
||||||
@ -87,8 +87,9 @@ export const useQortalGetSaveSettings = (myName, isAuthenticated) => {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
if(!myName || !settingsLocalLastUpdated || !isAuthenticated) return
|
if(!myName || !settingsLocalLastUpdated || !isAuthenticated || isUsingImportExportSettings === null) return
|
||||||
|
if(isUsingImportExportSettings) return
|
||||||
getSavedSettings(myName, settingsLocalLastUpdated)
|
getSavedSettings(myName, settingsLocalLastUpdated)
|
||||||
}, [getSavedSettings, myName, settingsLocalLastUpdated, isAuthenticated])
|
}, [getSavedSettings, myName, settingsLocalLastUpdated, isAuthenticated, isUsingImportExportSettings])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useEffect } from 'react'
|
import React, { useCallback, useEffect } from 'react'
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom } from './atoms/global';
|
import { isUsingImportExportSettingsAtom, oldPinnedAppsAtom, settingsLocalLastUpdatedAtom, settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom } from './atoms/global';
|
||||||
|
|
||||||
function fetchFromLocalStorage(key) {
|
function fetchFromLocalStorage(key) {
|
||||||
try {
|
try {
|
||||||
@ -18,7 +18,10 @@ function fetchFromLocalStorage(key) {
|
|||||||
export const useRetrieveDataLocalStorage = () => {
|
export const useRetrieveDataLocalStorage = () => {
|
||||||
const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom);
|
const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom);
|
||||||
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
|
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
|
||||||
|
const setIsUsingImportExportSettings = useSetRecoilState(isUsingImportExportSettingsAtom)
|
||||||
|
const setSettingsQDNLastUpdated = useSetRecoilState(settingsQDNLastUpdatedAtom);
|
||||||
|
const setOldPinnedApps = useSetRecoilState(oldPinnedAppsAtom)
|
||||||
|
|
||||||
const getSortablePinnedApps = useCallback(()=> {
|
const getSortablePinnedApps = useCallback(()=> {
|
||||||
const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings')
|
const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings')
|
||||||
if(pinnedAppsLocal?.sortablePinnedApps){
|
if(pinnedAppsLocal?.sortablePinnedApps){
|
||||||
@ -28,10 +31,25 @@ export const useRetrieveDataLocalStorage = () => {
|
|||||||
setSettingsLocalLastUpdated(-1)
|
setSettingsLocalLastUpdated(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
const getSortablePinnedAppsImportExport = useCallback(()=> {
|
||||||
|
const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings_import_export')
|
||||||
|
if(pinnedAppsLocal?.sortablePinnedApps){
|
||||||
|
setOldPinnedApps(pinnedAppsLocal?.sortablePinnedApps)
|
||||||
|
|
||||||
|
|
||||||
|
setIsUsingImportExportSettings(true)
|
||||||
|
setSettingsQDNLastUpdated(pinnedAppsLocal?.timestamp || 0)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
setIsUsingImportExportSettings(false)
|
||||||
|
}
|
||||||
|
|
||||||
}, [])
|
}, [])
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
|
|
||||||
getSortablePinnedApps()
|
getSortablePinnedApps()
|
||||||
|
getSortablePinnedAppsImportExport()
|
||||||
}, [getSortablePinnedApps])
|
}, [getSortablePinnedApps])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user