diff --git a/src/App.tsx b/src/App.tsx
index 2503d1d..899d0c4 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -112,6 +112,7 @@ import {
canSaveSettingToQdnAtom,
fullScreenAtom,
hasSettingsChangedAtom,
+ isUsingImportExportSettingsAtom,
oldPinnedAppsAtom,
settingsLocalLastUpdatedAtom,
settingsQDNLastUpdatedAtom,
@@ -509,6 +510,7 @@ function App() {
settingsLocalLastUpdatedAtom
);
const resetAtomOldPinnedAppsAtom = useResetRecoilState(oldPinnedAppsAtom);
+ const resetAtomIsUsingImportExportSettingsAtom = useResetRecoilState(isUsingImportExportSettingsAtom)
const resetAllRecoil = () => {
resetAtomSortablePinnedAppsAtom();
@@ -516,6 +518,7 @@ function App() {
resetAtomSettingsQDNLastUpdatedAtom();
resetAtomSettingsLocalLastUpdatedAtom();
resetAtomOldPinnedAppsAtom();
+ resetAtomIsUsingImportExportSettingsAtom();
};
useEffect(() => {
if (!isMobile) return;
diff --git a/src/atoms/global.ts b/src/atoms/global.ts
index 0e0dc42..9d1f7ad 100644
--- a/src/atoms/global.ts
+++ b/src/atoms/global.ts
@@ -135,4 +135,9 @@ export const blobKeySelector = selectorFamily({
export const selectedGroupIdAtom = atom({
key: 'selectedGroupIdAtom',
default: null,
-});
\ No newline at end of file
+});
+
+export const isUsingImportExportSettingsAtom = atom({
+ key: 'isUsingImportExportSettingsAtom',
+ default: null,
+});
diff --git a/src/common/useFetchResources.tsx b/src/common/useFetchResources.tsx
index 0aaec61..9099989 100644
--- a/src/common/useFetchResources.tsx
+++ b/src/common/useFetchResources.tsx
@@ -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()
intervalId.current = setInterval(async () => {
diff --git a/src/components/Apps/AppsNavBar.tsx b/src/components/Apps/AppsNavBar.tsx
index 0007d03..18e28cc 100644
--- a/src/components/Apps/AppsNavBar.tsx
+++ b/src/components/Apps/AppsNavBar.tsx
@@ -33,8 +33,12 @@ import {
sortablePinnedAppsAtom,
} from "../../atoms/global";
-export function saveToLocalStorage(key, subKey, newValue) {
+export function saveToLocalStorage(key, subKey, newValue, otherRootData = {}, deleteWholeKey) {
try {
+ if(deleteWholeKey){
+ localStorage.setItem(key, null);
+ return
+ }
// Fetch existing data
const existingData = localStorage.getItem(key);
let combinedData = {};
@@ -45,12 +49,14 @@ export function saveToLocalStorage(key, subKey, newValue) {
// Merge with the new data under the subKey
combinedData = {
...parsedData,
+ ...otherRootData,
timestamp: Date.now(), // Update the root timestamp
[subKey]: newValue, // Assuming the data is an array
};
} else {
// If no existing data, just use the new data under the subKey
combinedData = {
+ ...otherRootData,
timestamp: Date.now(), // Set the initial root timestamp
[subKey]: newValue,
};
diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx
index 582d4cd..745e0c1 100644
--- a/src/components/Chat/ChatGroup.tsx
+++ b/src/components/Chat/ChatGroup.tsx
@@ -993,7 +993,8 @@ const sendMessage = async ()=> {
diff --git a/src/components/Embeds/AttachmentEmbed.tsx b/src/components/Embeds/AttachmentEmbed.tsx
index 7f4d0f1..347b8f6 100644
--- a/src/components/Embeds/AttachmentEmbed.tsx
+++ b/src/components/Embeds/AttachmentEmbed.tsx
@@ -266,9 +266,7 @@ export const AttachmentCard = ({
{resourceData?.fileName && (
<>
- {resourceData?.fileName}
+ {resourceDetails?.status?.status === 'DOWNLOADED' ? 'BUILDING' : resourceDetails?.status?.status}
>
)}
diff --git a/src/components/Save/Save.tsx b/src/components/Save/Save.tsx
index dd5a222..1a0317f 100644
--- a/src/components/Save/Save.tsx
+++ b/src/components/Save/Save.tsx
@@ -4,12 +4,13 @@ import isEqual from "lodash/isEqual"; // Import deep comparison utility
import {
canSaveSettingToQdnAtom,
hasSettingsChangedAtom,
+ isUsingImportExportSettingsAtom,
oldPinnedAppsAtom,
settingsLocalLastUpdatedAtom,
settingsQDNLastUpdatedAtom,
sortablePinnedAppsAtom,
} 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 { MyContext } from "../../App";
import { getFee } from "../../background";
@@ -19,6 +20,43 @@ import { IconWrapper } from "../Desktop/DesktopFooter";
import { Spacer } from "../../common/Spacer";
import { LoadingButton } from "@mui/lab";
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 }) => {
const [pinnedApps, setPinnedApps] = useRecoilState(sortablePinnedAppsAtom);
const [settingsQdnLastUpdated, setSettingsQdnLastUpdated] = useRecoilState(
@@ -28,7 +66,7 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
settingsLocalLastUpdatedAtom
);
const setHasSettingsChangedAtom = useSetRecoilState(hasSettingsChangedAtom);
-
+ const [isUsingImportExportSettings, setIsUsingImportExportSettings] = useRecoilState(isUsingImportExportSettingsAtom);
const [canSave] = useRecoilState(canSaveSettingToQdnAtom);
const [openSnack, setOpenSnack] = useState(false);
const [isLoading, setIsLoading] = useState(false);
@@ -212,14 +250,9 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
vertical: "top",
horizontal: "center",
}}
- // sx={{
- // width: "300px",
- // maxWidth: "90%",
- // maxHeight: "80%",
- // overflow: "auto",
- // }}
>
- {
width: '100%'
}}
>
- {!myName ? (
-
-
- You need a registered Qortal name to save your pinned apps to QDN.
-
-
- ) : (
- <>
- {hasChanged && (
- {
fontSize: "14px",
}}
>
- You have unsaved changes to your pinned apps. Save them to QDN.
+ You are using the export/import way of saving settings.
-
-
- Save to QDN
-
-
- {!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated > 0 && (
- <>
-
- Don't like your current local changes? Would you like to
- reset to your saved QDN pinned apps?
-
-
-
- Revert to QDN
-
- >
- )}
- {!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated === 0 && (
- <>
-
- Don't like your current local changes? Would you like to
- reset to the default pinned apps?
-
-
-
- Revert to default
-
- >
- )}
-
- )}
- {!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated === -100 && (
-
-
- The app was unable to download your existing QDN-saved pinned
- apps. Would you like to overwrite those changes?
-
-
-
+
- )}
- {!hasChanged && (
+ fontWeight: 'bold',
+ opacity: 0.7,
+ "&:hover": {
+ backgroundColor: "var(--danger)",
+ color: "black",
+ opacity: 1,
+ },
+ }}
+ >
+ Use QDN saving
+
+
+
+ )}
+ {!isUsingImportExportSettings && (
+ {!myName ? (
+ {
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.
-
-
- )}
- >
- )}
-
-
+
+ ) : (
+ <>
+ {hasChanged && (
+
+
+ You have unsaved changes to your pinned apps. Save them to QDN.
+
+
+
+ Save to QDN
+
+
+ {!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated > 0 && (
+ <>
+
+ Don't like your current local changes? Would you like to
+ reset to your saved QDN pinned apps?
+
+
+
+ Revert to QDN
+
+ >
+ )}
+ {!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated === 0 && (
+ <>
+
+ Don't like your current local changes? Would you like to
+ reset to the default pinned apps?
+
+
+
+ Revert to default
+
+ >
+ )}
+
+ )}
+ {!isNaN(settingsQdnLastUpdated) && settingsQdnLastUpdated === -100 && isUsingImportExportSettings !== true && (
+
+
+ The app was unable to download your existing QDN-saved pinned
+ apps. Would you like to overwrite those changes?
+
+
+
+ Overwrite to QDN
+
+
+ )}
+ {!hasChanged && (
+
+
+ You currently do not have any changes to your pinned apps
+
+
+
+ )}
+ >
+ )}
+
+
+ )}
+
+
+ {
+ 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
+
+ {
+ 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
+
+
+
{
const setSettingsQDNLastUpdated = useSetRecoilState(settingsQDNLastUpdatedAtom);
const [settingsLocalLastUpdated] = useRecoilState(settingsLocalLastUpdatedAtom);
const [oldPinnedApps, setOldPinnedApps] = useRecoilState(oldPinnedAppsAtom)
-
+ const [isUsingImportExportSettings] = useRecoilState(isUsingImportExportSettingsAtom);
const getSavedSettings = useCallback(async (myName, settingsLocalLastUpdated)=> {
try {
const {hasPublishRecord, timestamp} = await getPublishRecord(myName)
@@ -87,8 +87,9 @@ export const useQortalGetSaveSettings = (myName, isAuthenticated) => {
}
}, [])
useEffect(()=> {
- if(!myName || !settingsLocalLastUpdated || !isAuthenticated) return
+ if(!myName || !settingsLocalLastUpdated || !isAuthenticated || isUsingImportExportSettings === null) return
+ if(isUsingImportExportSettings) return
getSavedSettings(myName, settingsLocalLastUpdated)
- }, [getSavedSettings, myName, settingsLocalLastUpdated, isAuthenticated])
+ }, [getSavedSettings, myName, settingsLocalLastUpdated, isAuthenticated, isUsingImportExportSettings])
}
diff --git a/src/useRetrieveDataLocalStorage.tsx b/src/useRetrieveDataLocalStorage.tsx
index 8f808ab..2a248bd 100644
--- a/src/useRetrieveDataLocalStorage.tsx
+++ b/src/useRetrieveDataLocalStorage.tsx
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect } from 'react'
import { useSetRecoilState } from 'recoil';
-import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom } from './atoms/global';
+import { isUsingImportExportSettingsAtom, oldPinnedAppsAtom, settingsLocalLastUpdatedAtom, settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom } from './atoms/global';
function fetchFromLocalStorage(key) {
try {
@@ -18,7 +18,10 @@ function fetchFromLocalStorage(key) {
export const useRetrieveDataLocalStorage = () => {
const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom);
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
-
+ const setIsUsingImportExportSettings = useSetRecoilState(isUsingImportExportSettingsAtom)
+ const setSettingsQDNLastUpdated = useSetRecoilState(settingsQDNLastUpdatedAtom);
+ const setOldPinnedApps = useSetRecoilState(oldPinnedAppsAtom)
+
const getSortablePinnedApps = useCallback(()=> {
const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings')
if(pinnedAppsLocal?.sortablePinnedApps){
@@ -28,10 +31,25 @@ export const useRetrieveDataLocalStorage = () => {
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(()=> {
getSortablePinnedApps()
+ getSortablePinnedAppsImportExport()
}, [getSortablePinnedApps])
}