diff --git a/src/App.tsx b/src/App.tsx index 48aaf6a..5200bec 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -99,6 +99,7 @@ import { AddressQRCode } from "./components/AddressQRCode"; import { Settings } from "./components/Group/Settings"; import { MainAvatar } from "./components/MainAvatar"; import { useRetrieveDataLocalStorage } from "./useRetrieveDataLocalStorage"; +import { useQortalGetSaveSettings } from "./useQortalGetSaveSettings"; type extStates = | "not-authenticated" @@ -324,6 +325,7 @@ function App() { const [isSettingsOpen, setIsSettingsOpen] = useState(false); const qortalRequestCheckbox1Ref = useRef(null); useRetrieveDataLocalStorage() + useQortalGetSaveSettings(userInfo?.name) useEffect(() => { if (!isMobile) return; // Function to set the height of the app to the viewport height diff --git a/src/assets/svgs/SaveIcon.tsx b/src/assets/svgs/SaveIcon.tsx new file mode 100644 index 0000000..12c4999 --- /dev/null +++ b/src/assets/svgs/SaveIcon.tsx @@ -0,0 +1,10 @@ +import React from 'react' + +export const SaveIcon = ({color = '#8F8F91'}) => { + return ( + + + + + ) +} diff --git a/src/atoms/global.ts b/src/atoms/global.ts index 54d4186..2ad90d4 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -4,4 +4,9 @@ import { atom } from 'recoil'; export const sortablePinnedAppsAtom = atom({ key: 'sortablePinnedAppsFromAtom', default: [], +}); + +export const canSaveSettingToQdnAtom = atom({ + key: 'canSaveSettingToQdnAtom', + default: false, }); \ No newline at end of file diff --git a/src/components/Apps/AppViewerContainer.tsx b/src/components/Apps/AppViewerContainer.tsx index 98303ad..7f01c15 100644 --- a/src/components/Apps/AppViewerContainer.tsx +++ b/src/components/Apps/AppViewerContainer.tsx @@ -42,7 +42,7 @@ const AppViewerContainer = ({app, isSelected, hide}) => { } style={{ - height: `calc(${rootHeight} - 60px - 45px)`, + height: `calc(${rootHeight} - 60px - 45px - 20px)`, border: 'none', width: '100%', display: (!isSelected || hide) && 'none' diff --git a/src/components/Apps/Apps.tsx b/src/components/Apps/Apps.tsx index 0a6ea90..bc1af29 100644 --- a/src/components/Apps/Apps.tsx +++ b/src/components/Apps/Apps.tsx @@ -236,6 +236,7 @@ export const Apps = ({ mode, setMode, show , myName}) => { const setNewTabWindowFunc = (e) => { setIsNewTabWindow(true); + setSelectedTab(null) }; useEffect(() => { @@ -252,18 +253,19 @@ export const Apps = ({ mode, setMode, show , myName}) => { display: !show && "none", }} > - {mode !== "viewer" && } + {mode !== "viewer" && !selectedTab && } {mode === "home" && ( )} - {mode === "library" && ( + - )} + {mode === "appInfo" && } {mode === "publish" && } @@ -283,7 +285,7 @@ export const Apps = ({ mode, setMode, show , myName}) => { )} - {mode !== "viewer" && } + {mode !== "viewer" && !selectedTab && } ); }; diff --git a/src/components/Apps/AppsLibrary.tsx b/src/components/Apps/AppsLibrary.tsx index 78f083a..c1cc3ef 100644 --- a/src/components/Apps/AppsLibrary.tsx +++ b/src/components/Apps/AppsLibrary.tsx @@ -74,7 +74,7 @@ const ScrollerStyled = styled('div')({ "-ms-overflow-style": "none", }); -export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp }) => { +export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, isShow }) => { const [searchValue, setSearchValue] = useState(""); const virtuosoRef = useRef(); const { rootHeight } = useContext(MyContext); @@ -133,7 +133,9 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp }) return ( - + { }} src={NavAdd} /> { + if(!selectedTab) return handleClick(e) }}> {/* Right Logout Icon */} - { setMobileViewModeKeepOpen("messaging"); }} - edge="end" - color="inherit" - aria-label="logout" - - // onClick={onLogoutClick} > - + - - + { const [pinnedApps, setPinnedApps] = useRecoilState(sortablePinnedAppsAtom); + const [canSave, _] = useRecoilState(canSaveSettingToQdnAtom); + const [openSnack, setOpenSnack] = useState(false); + const [isLoading, setIsLoading] = useState(false) + const [infoSnack, setInfoSnack] = useState(null); const [oldPinnedApps, setOldPinnedApps] = useState(pinnedApps) console.log('oldpin', {oldPinnedApps, pinnedApps}) + const { show } = useContext(MyContext); const hasChanged = useMemo(()=> { - return !isEqual(pinnedApps, oldPinnedApps) + const newChanges = { + sortablePinnedApps: pinnedApps.map((item)=> { + return { + name: item?.name, + service: item?.service + } + }) + } + const oldChanges = { + sortablePinnedApps: oldPinnedApps.map((item)=> { + return { + name: item?.name, + service: item?.service + } + }) + } + return !isEqual(oldChanges, newChanges) }, [oldPinnedApps, pinnedApps]) + + const saveToQdn = async ()=> { + try { + setIsLoading(true) + const data64 = await objectToBase64({ + sortablePinnedApps: pinnedApps + }) + const encryptData = await new Promise((res, rej) => { + chrome?.runtime?.sendMessage( + { + action: "ENCRYPT_DATA", + type: "qortalRequest", + payload: { + data64 + }, + }, + (response) => { + console.log("response", response); + if (response.error) { + rej(response?.message); + return; + } else { + res(response); + + } + } + ); + }); + if(encryptData && !encryptData?.error){ + const fee = await getFee('ARBITRARY') + + await show({ + message: "Would you like to publish your settings to QDN (encrypted) ?" , + publishFee: fee.fee + ' QORT' + }) + const response = await new Promise((res, rej) => { + chrome?.runtime?.sendMessage( + { + action: "publishOnQDN", + payload: { + data: encryptData, + identifier: "ext_saved_settings", + service: 'DOCUMENT_PRIVATE' + }, + }, + (response) => { + + if (!response?.error) { + res(response); + return + } + rej(response.error); + } + ); + }); + console.log('saved', response) + if(response?.identifier){ + setOldPinnedApps(pinnedApps) + setInfoSnack({ + type: "success", + message: + "Sucessfully published to QDN", + }); + setOpenSnack(true); + } + } + console.log('save encryptedData', encryptData) + } catch (error) { + setInfoSnack({ + type: "error", + message: + error?.message || "Unable to save to QDN", + }); + setOpenSnack(true); + } finally { + setIsLoading(false) + } + } return ( -
{hasChanged && 'Save'}
+ <> + + + + + + ) } diff --git a/src/useQortalGetSaveSettings.tsx b/src/useQortalGetSaveSettings.tsx new file mode 100644 index 0000000..e330077 --- /dev/null +++ b/src/useQortalGetSaveSettings.tsx @@ -0,0 +1,77 @@ +import React, { useCallback, useEffect } from 'react' +import { useSetRecoilState } from 'recoil'; +import { canSaveSettingToQdnAtom, sortablePinnedAppsAtom } from './atoms/global'; +import { getArbitraryEndpointReact, getBaseApiReact } from './App'; +import { decryptResource } from './components/Group/Group'; +import { base64ToUint8Array, uint8ArrayToObject } from './backgroundFunctions/encryption'; + +function fetchFromLocalStorage(key) { + try { + const serializedValue = localStorage.getItem(key); + if (serializedValue === null) { + console.log(`No data found for key: ${key}`); + return null; + } + return JSON.parse(serializedValue); + } catch (error) { + console.error('Error fetching from localStorage:', error); + return null; + } +} + +const getPublishRecord = async (myName) => { + // const validApi = await findUsableApi(); + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=ext_saved_settings&exactmatchnames=true&limit=1&prefix=true&name=${myName}`; + const response = await fetch(url); + if (!response.ok) { + throw new Error("network error"); + } + const publishData = await response.json(); + + if(publishData?.length > 0) return true + + return false + }; + const getPublish = async (myName) => { + let data + const res = await fetch( + `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${myName}/ext_saved_settings?encoding=base64` + ); + data = await res.text(); + + + if(!data) throw new Error('Unable to fetch publish') + + const decryptedKey: any = await decryptResource(data); + + const dataint8Array = base64ToUint8Array(decryptedKey.data); + const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); + return decryptedKeyToObject + }; + +export const useQortalGetSaveSettings = (myName) => { + const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom); + const setCanSave = useSetRecoilState(canSaveSettingToQdnAtom); + + + const getSavedSettings = useCallback(async (myName)=> { + try { + const hasPublishRecord = await getPublishRecord(myName) + if(hasPublishRecord){ + const settings = await getPublish(myName) + if(settings?.sortablePinnedApps){ + fetchFromLocalStorage('sortablePinnedApps', settings.sortablePinnedApps) + setSortablePinnedApps(settings.sortablePinnedApps) + } + } + setCanSave(true) + } catch (error) { + + } + }, []) + useEffect(()=> { + if(!myName) return + getSavedSettings(myName) + }, [getSavedSettings, myName]) + +}