mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-05-15 22:26:58 +00:00
updated to react 19
This commit is contained in:
parent
2d8bf8fb97
commit
ea0de88b1f
1479
package-lock.json
generated
1479
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
69
package.json
69
package.json
@ -20,30 +20,28 @@
|
||||
"@capacitor/core": "^6.1.2",
|
||||
"@capacitor/filesystem": "^6.0.1",
|
||||
"@capacitor/local-notifications": "^6.1.0",
|
||||
"@chatscope/chat-ui-kit-react": "^2.0.3",
|
||||
"@dnd-kit/core": "^6.1.0",
|
||||
"@dnd-kit/sortable": "^8.0.0",
|
||||
"@dnd-kit/core": "^6.3.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@electron/packager": "^18.3.6",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@evva/capacitor-secure-storage-plugin": "^3.0.1",
|
||||
"@mui/icons-material": "^5.16.4",
|
||||
"@mui/lab": "^5.0.0-alpha.173",
|
||||
"@mui/material": "^5.16.7",
|
||||
"@reduxjs/toolkit": "^2.2.7",
|
||||
"@tanstack/react-virtual": "^3.10.8",
|
||||
"@mui/icons-material": "^7.0.1",
|
||||
"@mui/lab": "^7.0.0-beta.11",
|
||||
"@mui/material": "^7.0.1",
|
||||
"@tanstack/react-virtual": "^3.13.6",
|
||||
"@testing-library/jest-dom": "^6.4.6",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@tiptap/extension-color": "^2.5.9",
|
||||
"@tiptap/extension-highlight": "^2.6.6",
|
||||
"@tiptap/extension-image": "^2.6.6",
|
||||
"@tiptap/extension-mention": "^2.9.1",
|
||||
"@tiptap/extension-placeholder": "^2.6.2",
|
||||
"@tiptap/extension-text-style": "^2.5.9",
|
||||
"@tiptap/extension-underline": "^2.6.6",
|
||||
"@tiptap/pm": "^2.5.9",
|
||||
"@tiptap/react": "^2.5.9",
|
||||
"@tiptap/starter-kit": "^2.5.9",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@tiptap/extension-color": "^2.11.7",
|
||||
"@tiptap/extension-highlight": "^2.11.7",
|
||||
"@tiptap/extension-image": "^2.11.7",
|
||||
"@tiptap/extension-mention": "^2.11.7",
|
||||
"@tiptap/extension-placeholder": "^2.11.7",
|
||||
"@tiptap/extension-text-style": "^2.11.7",
|
||||
"@tiptap/extension-underline": "^2.11.7",
|
||||
"@tiptap/pm": "^2.11.7",
|
||||
"@tiptap/react": "^2.11.7",
|
||||
"@tiptap/starter-kit": "^2.11.7",
|
||||
"@transistorsoft/capacitor-background-fetch": "^6.0.1",
|
||||
"@types/chrome": "^0.0.263",
|
||||
"@uiw/react-color": "^2.5.1",
|
||||
@ -66,30 +64,25 @@
|
||||
"i18next-browser-languagedetector": "^8.0.5",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"i18next-localstorage-backend": "^4.2.0",
|
||||
"jotai": "^2.12.3",
|
||||
"jssha": "3.3.1",
|
||||
"lit": "^3.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mime": "^4.0.4",
|
||||
"moment": "^2.30.1",
|
||||
"npm": "^10.8.3",
|
||||
"quill-image-resize-module-react": "^3.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react": "^19.1.0",
|
||||
"react-countdown-circle-timer": "^3.2.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-frame-component": "^5.2.7",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-infinite-scroller": "^1.2.6",
|
||||
"react-intersection-observer": "^9.13.0",
|
||||
"react-json-view-lite": "^2.0.1",
|
||||
"react-intersection-observer": "^9.16.0",
|
||||
"react-json-view-lite": "^2.4.1",
|
||||
"react-loader-spinner": "^6.1.6",
|
||||
"react-qr-code": "^2.0.15",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-virtualized": "^9.22.5",
|
||||
"react-virtualized": "^9.22.6",
|
||||
"react-virtuoso": "^4.10.4",
|
||||
"recoil": "^0.7.7",
|
||||
"short-unique-id": "^5.2.0",
|
||||
"slate": "^0.103.0",
|
||||
"slate-react": "^0.109.0",
|
||||
@ -101,14 +94,12 @@
|
||||
"vite-plugin-wasm": "^3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "^10.3.0",
|
||||
"@testing-library/react": "^16.0.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/lodash": "^4.17.7",
|
||||
"@types/react": "^18.2.64",
|
||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||
"@types/react-dom": "^18.2.21",
|
||||
"@types/react-infinite-scroller": "^1.2.5",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.0",
|
||||
"@types/react-virtualized": "^9.21.30",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||
"@typescript-eslint/parser": "^7.1.1",
|
||||
@ -122,5 +113,11 @@
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.1.6",
|
||||
"vitest": "^1.6.1"
|
||||
},
|
||||
"overrides": {
|
||||
"react-loader-spinner": {
|
||||
"react": "^18 || ^19",
|
||||
"react-dom": "^18 || ^19"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
85
src/App.tsx
85
src/App.tsx
@ -33,7 +33,6 @@ import DownloadIcon from '@mui/icons-material/Download';
|
||||
import ltcLogo from './assets/ltc.png';
|
||||
import PersonSearchIcon from '@mui/icons-material/PersonSearch';
|
||||
import qortLogo from './assets/qort.png';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import { Return } from './assets/Icons/Return.tsx';
|
||||
import WarningIcon from '@mui/icons-material/Warning';
|
||||
import './utils/seedPhrase/RandomSentenceGenerator';
|
||||
@ -96,7 +95,6 @@ import { Settings } from './components/Group/Settings';
|
||||
import { MainAvatar } from './components/MainAvatar';
|
||||
import { useRetrieveDataLocalStorage } from './useRetrieveDataLocalStorage';
|
||||
import { useQortalGetSaveSettings } from './useQortalGetSaveSettings';
|
||||
import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil';
|
||||
import {
|
||||
canSaveSettingToQdnAtom,
|
||||
enabledDevModeAtom,
|
||||
@ -142,6 +140,8 @@ import LanguageSelector from './components/Language/LanguageSelector.tsx';
|
||||
import { DownloadWallet } from './components/Auth/DownloadWallet.tsx';
|
||||
import { CopyIcon } from './assets/Icons/CopyIcon.tsx';
|
||||
import { SuccessIcon } from './assets/Icons/SuccessIcon.tsx';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
import { useResetAtom } from 'jotai/utils';
|
||||
|
||||
type extStates =
|
||||
| 'not-authenticated'
|
||||
@ -330,9 +330,10 @@ function App() {
|
||||
const [txList, setTxList] = useState([]);
|
||||
const [memberGroups, setMemberGroups] = useState([]);
|
||||
const [isFocused, setIsFocused] = useState(true);
|
||||
const [hasSettingsChanged, setHasSettingsChanged] = useRecoilState(
|
||||
const [hasSettingsChanged, setHasSettingsChanged] = useAtom(
|
||||
hasSettingsChangedAtom
|
||||
);
|
||||
|
||||
const balanceSetIntervalRef = useRef(null);
|
||||
const { downloadResource } = useFetchResources();
|
||||
const holdRefExtState = useRef<extStates>('not-authenticated');
|
||||
@ -405,9 +406,10 @@ function App() {
|
||||
const qortalRequestCheckbox1Ref = useRef(null);
|
||||
useRetrieveDataLocalStorage(userInfo?.address);
|
||||
useQortalGetSaveSettings(userInfo?.name, extState === 'authenticated');
|
||||
const [isEnabledDevMode, setIsEnabledDevMode] =
|
||||
useRecoilState(enabledDevModeAtom);
|
||||
const setIsDisabledEditorEnter = useSetRecoilState(isDisabledEditorEnterAtom);
|
||||
const setIsEnabledDevMode = useSetAtom(enabledDevModeAtom);
|
||||
|
||||
const setIsDisabledEditorEnter = useSetAtom(isDisabledEditorEnterAtom);
|
||||
|
||||
const [isOpenMinting, setIsOpenMinting] = useState(false);
|
||||
const generatorRef = useRef(null);
|
||||
|
||||
@ -453,45 +455,33 @@ function App() {
|
||||
}, [extState, walletToBeDownloaded, shownTutorialsInitiated]);
|
||||
|
||||
//resets for recoil
|
||||
const resetAtomSortablePinnedAppsAtom = useResetRecoilState(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
|
||||
const resetAtomIsUsingImportExportSettingsAtom = useResetRecoilState(
|
||||
const resetAtomSortablePinnedAppsAtom = useResetAtom(sortablePinnedAppsAtom);
|
||||
const resetAtomIsUsingImportExportSettingsAtom = useResetAtom(
|
||||
isUsingImportExportSettingsAtom
|
||||
);
|
||||
const resetAtomCanSaveSettingToQdnAtom = useResetRecoilState(
|
||||
const resetAtomCanSaveSettingToQdnAtom = useResetAtom(
|
||||
canSaveSettingToQdnAtom
|
||||
);
|
||||
|
||||
const resetAtomSettingsQDNLastUpdatedAtom = useResetRecoilState(
|
||||
const resetAtomSettingsQDNLastUpdatedAtom = useResetAtom(
|
||||
settingsQDNLastUpdatedAtom
|
||||
);
|
||||
|
||||
const resetAtomSettingsLocalLastUpdatedAtom = useResetRecoilState(
|
||||
const resetAtomSettingsLocalLastUpdatedAtom = useResetAtom(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
|
||||
const resetAtomOldPinnedAppsAtom = useResetRecoilState(oldPinnedAppsAtom);
|
||||
const resetAtomQMailLastEnteredTimestampAtom = useResetRecoilState(
|
||||
const resetAtomOldPinnedAppsAtom = useResetAtom(oldPinnedAppsAtom);
|
||||
const resetAtomQMailLastEnteredTimestampAtom = useResetAtom(
|
||||
qMailLastEnteredTimestampAtom
|
||||
);
|
||||
|
||||
const resetAtomMailsAtom = useResetRecoilState(mailsAtom);
|
||||
const resetGroupPropertiesAtom = useResetRecoilState(groupsPropertiesAtom);
|
||||
const resetLastPaymentSeenTimestampAtom = useResetRecoilState(
|
||||
const resetAtomMailsAtom = useResetAtom(mailsAtom);
|
||||
const resetGroupPropertiesAtom = useResetAtom(groupsPropertiesAtom);
|
||||
const resetLastPaymentSeenTimestampAtom = useResetAtom(
|
||||
lastPaymentSeenTimestampAtom
|
||||
);
|
||||
const resetGroupsOwnerNamesAtom = useResetRecoilState(groupsOwnerNamesAtom);
|
||||
const resetGroupAnnouncementsAtom = useResetRecoilState(
|
||||
groupAnnouncementsAtom
|
||||
);
|
||||
const resetMutedGroupsAtom = useResetRecoilState(mutedGroupsAtom);
|
||||
|
||||
const resetGroupChatTimestampsAtom = useResetRecoilState(
|
||||
groupChatTimestampsAtom
|
||||
);
|
||||
const resetTimestampEnterAtom = useResetRecoilState(timestampEnterDataAtom);
|
||||
const resetGroupsOwnerNamesAtom = useResetAtom(groupsOwnerNamesAtom);
|
||||
const resetGroupAnnouncementsAtom = useResetAtom(groupAnnouncementsAtom);
|
||||
const resetMutedGroupsAtom = useResetAtom(mutedGroupsAtom);
|
||||
const resetGroupChatTimestampsAtom = useResetAtom(groupChatTimestampsAtom);
|
||||
const resetTimestampEnterAtom = useResetAtom(timestampEnterDataAtom);
|
||||
|
||||
const resetAllRecoil = () => {
|
||||
resetAtomSortablePinnedAppsAtom();
|
||||
@ -1314,13 +1304,23 @@ function App() {
|
||||
|
||||
<Spacer height="32px" />
|
||||
|
||||
<CopyToClipboard text={rawWallet?.ltcAddress}>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
if (rawWallet?.ltcAddress) {
|
||||
navigator.clipboard
|
||||
.writeText(rawWallet.ltcAddress)
|
||||
.catch((err) => {
|
||||
console.error('Failed to copy LTC address:', err);
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AddressBox>
|
||||
{rawWallet?.ltcAddress?.slice(0, 6)}...
|
||||
{rawWallet?.ltcAddress?.slice(-4)}{' '}
|
||||
<CopyIcon color={theme.palette.text.primary} />
|
||||
</AddressBox>
|
||||
</CopyToClipboard>
|
||||
</ButtonBase>
|
||||
|
||||
<Spacer height="10px" />
|
||||
|
||||
@ -1380,13 +1380,24 @@ function App() {
|
||||
|
||||
<Spacer height="10px" />
|
||||
|
||||
<CopyToClipboard text={rawWallet?.address0}>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
if (rawWallet?.address0) {
|
||||
navigator.clipboard
|
||||
.writeText(rawWallet.address0)
|
||||
.catch((err) => {
|
||||
console.error('Failed to copy address:', err);
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AddressBox>
|
||||
{rawWallet?.address0?.slice(0, 6)}...
|
||||
{rawWallet?.address0?.slice(-4)}{' '}
|
||||
<CopyIcon color={theme.palette.text.primary} />
|
||||
</AddressBox>
|
||||
</CopyToClipboard>
|
||||
</ButtonBase>
|
||||
|
||||
<Spacer height="10px" />
|
||||
{qortBalanceLoading && (
|
||||
<CircularProgress color="success" size={16} />
|
||||
|
@ -1,273 +1,80 @@
|
||||
import { atom, selectorFamily } from 'recoil';
|
||||
import { atom } from 'jotai';
|
||||
import { atomWithReset, atomFamily } from 'jotai/utils';
|
||||
|
||||
export const sortablePinnedAppsAtom = atom({
|
||||
key: 'sortablePinnedAppsFromAtom',
|
||||
default: [
|
||||
{
|
||||
name: 'Q-Tube',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Mail',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Share',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Fund',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Shop',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Trade',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Support',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Manager',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Blog',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Mintership',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Wallets',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Search',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Nodecontrol',
|
||||
service: 'APP',
|
||||
},
|
||||
],
|
||||
});
|
||||
// Atoms (resettable)
|
||||
export const sortablePinnedAppsAtom = atomWithReset([
|
||||
{ name: 'Q-Tube', service: 'APP' },
|
||||
{ name: 'Q-Mail', service: 'APP' },
|
||||
{ name: 'Q-Share', service: 'APP' },
|
||||
{ name: 'Q-Fund', service: 'APP' },
|
||||
{ name: 'Q-Shop', service: 'APP' },
|
||||
{ name: 'Q-Trade', service: 'APP' },
|
||||
{ name: 'Q-Support', service: 'APP' },
|
||||
{ name: 'Q-Manager', service: 'APP' },
|
||||
{ name: 'Q-Blog', service: 'APP' },
|
||||
{ name: 'Q-Mintership', service: 'APP' },
|
||||
{ name: 'Q-Wallets', service: 'APP' },
|
||||
{ name: 'Q-Search', service: 'APP' },
|
||||
{ name: 'Q-Nodecontrol', service: 'APP' },
|
||||
]);
|
||||
|
||||
export const canSaveSettingToQdnAtom = atom({
|
||||
key: 'canSaveSettingToQdnAtom',
|
||||
default: false,
|
||||
});
|
||||
export const canSaveSettingToQdnAtom = atomWithReset(false);
|
||||
export const settingsQDNLastUpdatedAtom = atomWithReset(-100);
|
||||
export const settingsLocalLastUpdatedAtom = atomWithReset(0);
|
||||
export const oldPinnedAppsAtom = atomWithReset([]);
|
||||
export const isUsingImportExportSettingsAtom = atomWithReset(null);
|
||||
export const fullScreenAtom = atomWithReset(false);
|
||||
export const hasSettingsChangedAtom = atomWithReset(false);
|
||||
export const navigationControllerAtom = atomWithReset({});
|
||||
export const enabledDevModeAtom = atomWithReset(false);
|
||||
export const myGroupsWhereIAmAdminAtom = atomWithReset([]);
|
||||
export const promotionTimeIntervalAtom = atomWithReset(0);
|
||||
export const promotionsAtom = atomWithReset([]);
|
||||
export const resourceDownloadControllerAtom = atomWithReset({});
|
||||
export const blobControllerAtom = atomWithReset({});
|
||||
export const selectedGroupIdAtom = atomWithReset(null);
|
||||
export const addressInfoControllerAtom = atomWithReset({});
|
||||
export const isDisabledEditorEnterAtom = atomWithReset(false);
|
||||
export const qMailLastEnteredTimestampAtom = atomWithReset(null);
|
||||
export const lastPaymentSeenTimestampAtom = atomWithReset(null);
|
||||
export const mailsAtom = atomWithReset([]);
|
||||
export const groupsPropertiesAtom = atomWithReset({});
|
||||
export const groupsOwnerNamesAtom = atomWithReset({});
|
||||
export const isOpenBlockedModalAtom = atomWithReset(false);
|
||||
export const groupAnnouncementsAtom = atomWithReset({});
|
||||
export const mutedGroupsAtom = atomWithReset([]);
|
||||
export const groupChatTimestampsAtom = atomWithReset({});
|
||||
export const timestampEnterDataAtom = atomWithReset({});
|
||||
|
||||
export const settingsQDNLastUpdatedAtom = atom({
|
||||
key: 'settingsQDNLastUpdatedAtom',
|
||||
default: -100,
|
||||
});
|
||||
// Atom Families (replacing selectorFamily)
|
||||
export const resourceKeySelector = atomFamily((key) =>
|
||||
atom((get) => get(resourceDownloadControllerAtom)[key] || null)
|
||||
);
|
||||
|
||||
export const settingsLocalLastUpdatedAtom = atom({
|
||||
key: 'settingsLocalLastUpdatedAtom',
|
||||
default: 0,
|
||||
});
|
||||
export const blobKeySelector = atomFamily((key) =>
|
||||
atom((get) => get(blobControllerAtom)[key] || null)
|
||||
);
|
||||
|
||||
export const oldPinnedAppsAtom = atom({
|
||||
key: 'oldPinnedAppsAtom',
|
||||
default: [],
|
||||
});
|
||||
export const addressInfoKeySelector = atomFamily((key) =>
|
||||
atom((get) => get(addressInfoControllerAtom)[key] || null)
|
||||
);
|
||||
|
||||
export const isUsingImportExportSettingsAtom = atom({
|
||||
key: 'isUsingImportExportSettingsAtom',
|
||||
default: null,
|
||||
});
|
||||
export const groupsOwnerNamesSelector = atomFamily((key) =>
|
||||
atom((get) => get(groupsOwnerNamesAtom)[key] || null)
|
||||
);
|
||||
|
||||
export const fullScreenAtom = atom({
|
||||
key: 'fullScreenAtom',
|
||||
default: false,
|
||||
});
|
||||
export const groupAnnouncementSelector = atomFamily((key) =>
|
||||
atom((get) => get(groupAnnouncementsAtom)[key] || null)
|
||||
);
|
||||
|
||||
export const hasSettingsChangedAtom = atom({
|
||||
key: 'hasSettingsChangedAtom',
|
||||
default: false,
|
||||
});
|
||||
export const groupPropertySelector = atomFamily((key) =>
|
||||
atom((get) => get(groupsPropertiesAtom)[key] || null)
|
||||
);
|
||||
|
||||
export const navigationControllerAtom = atom({
|
||||
key: 'navigationControllerAtom',
|
||||
default: {},
|
||||
});
|
||||
export const groupChatTimestampSelector = atomFamily((key) =>
|
||||
atom((get) => get(groupChatTimestampsAtom)[key] || null)
|
||||
);
|
||||
|
||||
export const enabledDevModeAtom = atom({
|
||||
key: 'enabledDevModeAtom',
|
||||
default: false,
|
||||
});
|
||||
|
||||
export const myGroupsWhereIAmAdminAtom = atom({
|
||||
key: 'myGroupsWhereIAmAdminAtom',
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const promotionTimeIntervalAtom = atom({
|
||||
key: 'promotionTimeIntervalAtom',
|
||||
default: 0,
|
||||
});
|
||||
|
||||
export const promotionsAtom = atom({
|
||||
key: 'promotionsAtom',
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const resourceDownloadControllerAtom = atom({
|
||||
key: 'resourceDownloadControllerAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const resourceKeySelector = selectorFamily({
|
||||
key: 'resourceKeySelector',
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const resources = get(resourceDownloadControllerAtom);
|
||||
return resources[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
|
||||
export const blobControllerAtom = atom({
|
||||
key: 'blobControllerAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const blobKeySelector = selectorFamily({
|
||||
key: 'blobKeySelector',
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const blobs = get(blobControllerAtom);
|
||||
return blobs[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
|
||||
export const selectedGroupIdAtom = atom({
|
||||
key: 'selectedGroupIdAtom',
|
||||
default: null,
|
||||
});
|
||||
|
||||
export const addressInfoControllerAtom = atom({
|
||||
key: 'addressInfoControllerAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const addressInfoKeySelector = selectorFamily({
|
||||
key: 'addressInfoKeySelector',
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const userInfo = get(addressInfoControllerAtom);
|
||||
return userInfo[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
|
||||
export const isDisabledEditorEnterAtom = atom({
|
||||
key: 'isDisabledEditorEnterAtom',
|
||||
default: false,
|
||||
});
|
||||
|
||||
export const qMailLastEnteredTimestampAtom = atom({
|
||||
key: 'qMailLastEnteredTimestampAtom',
|
||||
default: null,
|
||||
});
|
||||
|
||||
export const lastPaymentSeenTimestampAtom = atom<null | number>({
|
||||
key: 'lastPaymentSeenTimestampAtom',
|
||||
default: null,
|
||||
});
|
||||
|
||||
export const mailsAtom = atom({
|
||||
key: 'mailsAtom',
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const groupsPropertiesAtom = atom({
|
||||
key: 'groupsPropertiesAtom',
|
||||
default: {},
|
||||
});
|
||||
export const groupsOwnerNamesAtom = atom({
|
||||
key: 'groupsOwnerNamesAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const isOpenBlockedModalAtom = atom({
|
||||
key: 'isOpenBlockedModalAtom',
|
||||
default: false,
|
||||
});
|
||||
|
||||
export const groupsOwnerNamesSelector = selectorFamily({
|
||||
key: 'groupsOwnerNamesSelector',
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const data = get(groupsOwnerNamesAtom);
|
||||
return data[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
|
||||
export const groupAnnouncementsAtom = atom({
|
||||
key: 'groupAnnouncementsAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const groupAnnouncementSelector = selectorFamily({
|
||||
key: 'groupAnnouncementSelector',
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const data = get(groupAnnouncementsAtom);
|
||||
return data[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
|
||||
export const groupPropertySelector = selectorFamily({
|
||||
key: 'groupPropertySelector',
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const data = get(groupsPropertiesAtom);
|
||||
return data[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
|
||||
export const mutedGroupsAtom = atom({
|
||||
key: 'mutedGroupsAtom',
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const groupChatTimestampsAtom = atom({
|
||||
key: 'groupChatTimestampsAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const groupChatTimestampSelector = selectorFamily({
|
||||
key: 'groupChatTimestampSelector',
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const data = get(groupChatTimestampsAtom);
|
||||
return data[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
|
||||
export const timestampEnterDataAtom = atom({
|
||||
key: 'timestampEnterDataAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const timestampEnterDataSelector = selectorFamily({
|
||||
key: 'timestampEnterDataSelector',
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const data = get(timestampEnterDataAtom);
|
||||
return data[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
export const timestampEnterDataSelector = atomFamily((key) =>
|
||||
atom((get) => get(timestampEnterDataAtom)[key] || null)
|
||||
);
|
||||
|
@ -1,12 +1,13 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { resourceDownloadControllerAtom } from '../atoms/global';
|
||||
import { getBaseApiReact } from '../App';
|
||||
import { useSetAtom } from 'jotai';
|
||||
|
||||
export const useFetchResources = () => {
|
||||
const [resources, setResources] = useRecoilState(resourceDownloadControllerAtom);
|
||||
const setResources = useSetAtom(resourceDownloadControllerAtom);
|
||||
|
||||
const downloadResource = useCallback(({ service, name, identifier }, build) => {
|
||||
const downloadResource = useCallback(
|
||||
({ service, name, identifier }, build) => {
|
||||
setResources((prev) => ({
|
||||
...prev,
|
||||
[`${service}-${name}-${identifier}`]: {
|
||||
@ -22,32 +23,30 @@ export const useFetchResources = () => {
|
||||
let percentLoaded = 0;
|
||||
let timer = 24;
|
||||
let tries = 0;
|
||||
let calledFirstTime = false
|
||||
let intervalId
|
||||
let timeoutId
|
||||
let calledFirstTime = false;
|
||||
let intervalId;
|
||||
let timeoutId;
|
||||
const callFunction = async () => {
|
||||
if (isCalling) return;
|
||||
isCalling = true;
|
||||
|
||||
|
||||
|
||||
let res
|
||||
let res;
|
||||
|
||||
if (!build) {
|
||||
const urlFirstTime = `${getBaseApiReact()}/arbitrary/resource/status/${service}/${name}/${identifier}`;
|
||||
const resCall = await fetch(urlFirstTime, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
res = await resCall.json()
|
||||
res = await resCall.json();
|
||||
if (tries > 18) {
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId)
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId)
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
setResources((prev) => ({
|
||||
...prev,
|
||||
@ -59,30 +58,30 @@ export const useFetchResources = () => {
|
||||
},
|
||||
},
|
||||
}));
|
||||
return
|
||||
return;
|
||||
}
|
||||
tries = tries + 1
|
||||
|
||||
tries = tries + 1;
|
||||
}
|
||||
|
||||
|
||||
if (build || (calledFirstTime === false && res?.status !== 'READY')) {
|
||||
const url = `${getBaseApiReact()}/arbitrary/resource/properties/${service}/${name}/${identifier}?build=true`;
|
||||
const resCall = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
res = await resCall.json();
|
||||
|
||||
}
|
||||
calledFirstTime = true
|
||||
calledFirstTime = true;
|
||||
isCalling = false;
|
||||
|
||||
if (res.localChunkCount) {
|
||||
if (res.percentLoaded) {
|
||||
if (res.percentLoaded === percentLoaded && res.percentLoaded !== 100) {
|
||||
if (
|
||||
res.percentLoaded === percentLoaded &&
|
||||
res.percentLoaded !== 100
|
||||
) {
|
||||
timer = timer - 5;
|
||||
} else {
|
||||
timer = 24;
|
||||
@ -129,10 +128,9 @@ export const useFetchResources = () => {
|
||||
if (res?.status === 'READY') {
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
|
||||
}
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId)
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
// Update Recoil state for completion
|
||||
setResources((prev) => ({
|
||||
@ -146,23 +144,24 @@ 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",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
res = await resCall.json();
|
||||
}
|
||||
}
|
||||
callFunction()
|
||||
};
|
||||
callFunction();
|
||||
intervalId = setInterval(async () => {
|
||||
callFunction()
|
||||
callFunction();
|
||||
}, 5000);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error during resource fetch:', error);
|
||||
}
|
||||
}, [setResources]);
|
||||
},
|
||||
[setResources]
|
||||
);
|
||||
|
||||
return { downloadResource };
|
||||
};
|
||||
|
@ -28,21 +28,21 @@ import {
|
||||
sortablePinnedAppsAtom,
|
||||
} from '../../atoms/global';
|
||||
import { saveToLocalStorage } from './AppsNavBarDesktop';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
|
||||
export const AppInfo = ({ app, myName }) => {
|
||||
const isInstalled = app?.status?.status === 'READY';
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useAtom(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const isSelectedAppPinned = !!sortablePinnedApps?.find(
|
||||
(item) => item?.name === app?.name && item?.service === app?.service
|
||||
);
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom);
|
||||
|
||||
return (
|
||||
<AppsLibraryContainer
|
||||
|
@ -17,12 +17,12 @@ import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { AppRating } from './AppRating';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import {
|
||||
settingsLocalLastUpdatedAtom,
|
||||
sortablePinnedAppsAtom,
|
||||
} from '../../atoms/global';
|
||||
import { saveToLocalStorage } from './AppsNavBarDesktop';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
|
||||
export const AppInfoSnippet = ({
|
||||
app,
|
||||
@ -31,16 +31,15 @@ export const AppInfoSnippet = ({
|
||||
parentStyles = {},
|
||||
}) => {
|
||||
const isInstalled = app?.status?.status === 'READY';
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useAtom(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom);
|
||||
|
||||
const isSelectedAppPinned = !!sortablePinnedApps?.find(
|
||||
(item) => item?.name === app?.name && item?.service === app?.service
|
||||
);
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
|
@ -74,15 +74,18 @@ export const AppViewer = React.forwardRef(
|
||||
}, [app, path, isDevMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!iframeRef?.current) return;
|
||||
const targetOrigin = iframeRef.current
|
||||
? new URL(iframeRef.current.src).origin
|
||||
: '*';
|
||||
// Send the navigation command after setting up the listener and timeout
|
||||
iframeRef.current.contentWindow.postMessage(
|
||||
const iframe = iframeRef?.current;
|
||||
if (!iframe) return;
|
||||
|
||||
try {
|
||||
const targetOrigin = new URL(iframe.src).origin;
|
||||
iframe.contentWindow?.postMessage(
|
||||
{ action: 'THEME_CHANGED', theme: themeMode, requestedHandler: 'UI' },
|
||||
targetOrigin
|
||||
);
|
||||
} catch (err) {
|
||||
console.error('Failed to send theme change to iframe:', err);
|
||||
}
|
||||
}, [themeMode]);
|
||||
|
||||
const removeTrailingSlash = (str) => str.replace(/\/$/, '');
|
||||
|
@ -20,11 +20,11 @@ import { HomeIcon } from '../../assets/Icons/HomeIcon';
|
||||
import { MessagingIcon } from '../../assets/Icons/MessagingIcon';
|
||||
import { Save } from '../Save/Save';
|
||||
import { IconWrapper } from '../Desktop/DesktopFooter';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { enabledDevModeAtom } from '../../atoms/global';
|
||||
import { AppsIcon } from '../../assets/Icons/AppsIcon';
|
||||
import { CoreSyncStatus } from '../CoreSyncStatus';
|
||||
import { MessagingIconFilled } from '../../assets/Icons/MessagingIconFilled';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
const uid = new ShortUniqueId({ length: 8 });
|
||||
|
||||
@ -47,8 +47,8 @@ export const AppsDesktop = ({
|
||||
const [isNewTabWindow, setIsNewTabWindow] = useState(false);
|
||||
const [categories, setCategories] = useState([]);
|
||||
const iframeRefs = useRef({});
|
||||
const [isEnabledDevMode, setIsEnabledDevMode] =
|
||||
useRecoilState(enabledDevModeAtom);
|
||||
const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom);
|
||||
|
||||
const { showTutorial } = useContext(GlobalContext);
|
||||
const theme = useTheme();
|
||||
|
||||
|
@ -13,16 +13,17 @@ import {
|
||||
unsubscribeFromEvent,
|
||||
} from '../../utils/events';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { navigationControllerAtom } from '../../atoms/global';
|
||||
import { AppsDevModeTabComponent } from './AppsDevModeTabComponent';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
export const AppsDevModeNavBar = () => {
|
||||
const [tabs, setTabs] = useState([]);
|
||||
const [selectedTab, setSelectedTab] = useState(null);
|
||||
const [navigationController, setNavigationController] = useRecoilState(
|
||||
const [navigationController, setNavigationController] = useAtom(
|
||||
navigationControllerAtom
|
||||
);
|
||||
|
||||
const theme = useTheme();
|
||||
const [isNewTabWindow, setIsNewTabWindow] = useState(false);
|
||||
const tabsRef = useRef(null);
|
||||
|
@ -26,12 +26,12 @@ import {
|
||||
import TabComponent from './TabComponent';
|
||||
import PushPinIcon from '@mui/icons-material/PushPin';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import {
|
||||
navigationControllerAtom,
|
||||
settingsLocalLastUpdatedAtom,
|
||||
sortablePinnedAppsAtom,
|
||||
} from '../../atoms/global';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
|
||||
export function saveToLocalStorage(key, subKey, newValue) {
|
||||
try {
|
||||
@ -67,22 +67,21 @@ export function saveToLocalStorage(key, subKey, newValue) {
|
||||
export const AppsNavBarDesktop = ({ disableBack }) => {
|
||||
const [tabs, setTabs] = useState([]);
|
||||
const [selectedTab, setSelectedTab] = useState(null);
|
||||
const [navigationController, setNavigationController] = useRecoilState(
|
||||
const [navigationController, setNavigationController] = useAtom(
|
||||
navigationControllerAtom
|
||||
);
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useAtom(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const [isNewTabWindow, setIsNewTabWindow] = useState(false);
|
||||
const tabsRef = useRef(null);
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom);
|
||||
|
||||
const handleClick = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
|
@ -15,7 +15,6 @@ import {
|
||||
} from '@mui/material';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { useHandlePrivateApps } from './useHandlePrivateApps';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import {
|
||||
groupsPropertiesAtom,
|
||||
myGroupsWhereIAmAdminAtom,
|
||||
@ -35,6 +34,7 @@ import { MyContext } from '../../App';
|
||||
import { fileToBase64 } from '../../utils/fileReading';
|
||||
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
|
||||
import { getFee } from '../../background';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
const maxFileSize = 50 * 1024 * 1024; // 50MB
|
||||
|
||||
@ -44,11 +44,10 @@ export const AppsPrivate = ({ myName }) => {
|
||||
const [logo, setLogo] = useState(null);
|
||||
const [qortalUrl, setQortalUrl] = useState('');
|
||||
const [selectedGroup, setSelectedGroup] = useState(0);
|
||||
const [groupsProperties] = useRecoilState(groupsPropertiesAtom);
|
||||
|
||||
const [valueTabPrivateApp, setValueTabPrivateApp] = useState(0);
|
||||
const [myGroupsWhereIAmAdminFromGlobal] = useRecoilState(
|
||||
myGroupsWhereIAmAdminAtom
|
||||
);
|
||||
const [groupsProperties] = useAtom(groupsPropertiesAtom);
|
||||
const [myGroupsWhereIAmAdminFromGlobal] = useAtom(myGroupsWhereIAmAdminAtom);
|
||||
|
||||
const myGroupsWhereIAmAdmin = useMemo(() => {
|
||||
return myGroupsWhereIAmAdminFromGlobal?.filter(
|
||||
|
@ -22,11 +22,11 @@ import {
|
||||
settingsLocalLastUpdatedAtom,
|
||||
sortablePinnedAppsAtom,
|
||||
} from '../../atoms/global';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { saveToLocalStorage } from './AppsNavBarDesktop';
|
||||
import { ContextMenuPinnedApps } from '../ContextMenuPinnedApps';
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
import { useHandlePrivateApps } from './useHandlePrivateApps';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
|
||||
const SortableItem = ({ id, name, app, isDesktop }) => {
|
||||
const { openApp } = useHandlePrivateApps();
|
||||
@ -137,10 +137,8 @@ export const SortablePinnedApps = ({
|
||||
myApp,
|
||||
availableQapps = [],
|
||||
}) => {
|
||||
const [pinnedApps, setPinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
const [pinnedApps, setPinnedApps] = useAtom(sortablePinnedAppsAtom);
|
||||
const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom);
|
||||
|
||||
const transformPinnedApps = useMemo(() => {
|
||||
// Clone the existing pinned apps list
|
||||
|
@ -1,49 +1,44 @@
|
||||
import React, { useContext, useState } from "react";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { getBaseApiReact, MyContext } from "../../App";
|
||||
import { createEndpoint } from "../../background";
|
||||
import { useRecoilState, useSetRecoilState } from "recoil";
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { getBaseApiReact, MyContext } from '../../App';
|
||||
import { createEndpoint } from '../../background';
|
||||
import {
|
||||
settingsLocalLastUpdatedAtom,
|
||||
sortablePinnedAppsAtom,
|
||||
} from "../../atoms/global";
|
||||
import { saveToLocalStorage } from "./AppsNavBarDesktop";
|
||||
import { base64ToBlobUrl } from "../../utils/fileReading";
|
||||
import { base64ToUint8Array } from "../../qdn/encryption/group-encryption";
|
||||
import { uint8ArrayToObject } from "../../backgroundFunctions/encryption";
|
||||
} from '../../atoms/global';
|
||||
import { saveToLocalStorage } from './AppsNavBarDesktop';
|
||||
import { base64ToBlobUrl } from '../../utils/fileReading';
|
||||
import { base64ToUint8Array } from '../../qdn/encryption/group-encryption';
|
||||
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
|
||||
export const useHandlePrivateApps = () => {
|
||||
const [status, setStatus] = useState("");
|
||||
const [status, setStatus] = useState('');
|
||||
const {
|
||||
openSnackGlobal,
|
||||
setOpenSnackGlobal,
|
||||
infoSnackCustom,
|
||||
setInfoSnackCustom,
|
||||
} = useContext(MyContext);
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom);
|
||||
|
||||
const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom);
|
||||
|
||||
const openApp = async (
|
||||
privateAppProperties,
|
||||
addToPinnedApps,
|
||||
setLoadingStatePrivateApp
|
||||
) => {
|
||||
try {
|
||||
|
||||
|
||||
if (setLoadingStatePrivateApp) {
|
||||
setLoadingStatePrivateApp(`Downloading and decrypting private app.`);
|
||||
|
||||
}
|
||||
setOpenSnackGlobal(true);
|
||||
|
||||
setInfoSnackCustom({
|
||||
type: "info",
|
||||
message: "Fetching app data",
|
||||
duration: null
|
||||
type: 'info',
|
||||
message: 'Fetching app data',
|
||||
duration: null,
|
||||
});
|
||||
const urlData = `${getBaseApiReact()}/arbitrary/${
|
||||
privateAppProperties?.service
|
||||
@ -53,32 +48,30 @@ export const useHandlePrivateApps = () => {
|
||||
let data;
|
||||
try {
|
||||
const responseData = await fetch(urlData, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!responseData?.ok) {
|
||||
if (setLoadingStatePrivateApp) {
|
||||
setLoadingStatePrivateApp("Error! Unable to download private app.");
|
||||
setLoadingStatePrivateApp('Error! Unable to download private app.');
|
||||
}
|
||||
|
||||
throw new Error("Unable to fetch app");
|
||||
throw new Error('Unable to fetch app');
|
||||
}
|
||||
|
||||
data = await responseData.text();
|
||||
if (data?.error) {
|
||||
if (setLoadingStatePrivateApp) {
|
||||
|
||||
setLoadingStatePrivateApp("Error! Unable to download private app.");
|
||||
setLoadingStatePrivateApp('Error! Unable to download private app.');
|
||||
}
|
||||
throw new Error("Unable to fetch app");
|
||||
throw new Error('Unable to fetch app');
|
||||
}
|
||||
} catch (error) {
|
||||
if (setLoadingStatePrivateApp) {
|
||||
|
||||
setLoadingStatePrivateApp("Error! Unable to download private app.");
|
||||
setLoadingStatePrivateApp('Error! Unable to download private app.');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@ -87,7 +80,7 @@ export const useHandlePrivateApps = () => {
|
||||
// eslint-disable-next-line no-useless-catch
|
||||
try {
|
||||
decryptedData = await window.sendMessage(
|
||||
"DECRYPT_QORTAL_GROUP_DATA",
|
||||
'DECRYPT_QORTAL_GROUP_DATA',
|
||||
|
||||
{
|
||||
base64: data,
|
||||
@ -96,15 +89,13 @@ export const useHandlePrivateApps = () => {
|
||||
);
|
||||
if (decryptedData?.error) {
|
||||
if (setLoadingStatePrivateApp) {
|
||||
|
||||
setLoadingStatePrivateApp("Error! Unable to decrypt private app.");
|
||||
setLoadingStatePrivateApp('Error! Unable to decrypt private app.');
|
||||
}
|
||||
throw new Error(decryptedData?.error);
|
||||
}
|
||||
} catch (error) {
|
||||
if (setLoadingStatePrivateApp) {
|
||||
|
||||
setLoadingStatePrivateApp("Error! Unable to decrypt private app.");
|
||||
setLoadingStatePrivateApp('Error! Unable to decrypt private app.');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@ -115,16 +106,16 @@ export const useHandlePrivateApps = () => {
|
||||
|
||||
if (decryptedData) {
|
||||
setInfoSnackCustom({
|
||||
type: "info",
|
||||
message: "Building app",
|
||||
type: 'info',
|
||||
message: 'Building app',
|
||||
});
|
||||
const endpoint = await createEndpoint(
|
||||
`/arbitrary/APP/${privateAppProperties?.name}/zip?preview=true`
|
||||
);
|
||||
const response = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
body: UintToObject?.app,
|
||||
});
|
||||
@ -135,7 +126,7 @@ export const useHandlePrivateApps = () => {
|
||||
);
|
||||
const res = await fetch(checkIfPreviewLinkStillWorksUrl);
|
||||
if (res.ok) {
|
||||
executeEvent("refreshApp", {
|
||||
executeEvent('refreshApp', {
|
||||
tabId: tabId,
|
||||
});
|
||||
} else {
|
||||
@ -143,20 +134,20 @@ export const useHandlePrivateApps = () => {
|
||||
`/arbitrary/APP/${privateAppProperties?.name}/zip?preview=true`
|
||||
);
|
||||
const response = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
body: UintToObject?.app,
|
||||
});
|
||||
const previewPath = await response.text();
|
||||
executeEvent("updateAppUrl", {
|
||||
executeEvent('updateAppUrl', {
|
||||
tabId: tabId,
|
||||
url: await createEndpoint(previewPath),
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
executeEvent("refreshApp", {
|
||||
executeEvent('refreshApp', {
|
||||
tabId: tabId,
|
||||
});
|
||||
}, 300);
|
||||
@ -173,20 +164,19 @@ export const useHandlePrivateApps = () => {
|
||||
isPreview: true,
|
||||
isPrivate: true,
|
||||
privateAppProperties: { ...privateAppProperties, logo, appName },
|
||||
filePath: "",
|
||||
filePath: '',
|
||||
refreshFunc: (tabId) => {
|
||||
refreshfunc(tabId, privateAppProperties);
|
||||
},
|
||||
};
|
||||
executeEvent("addTab", {
|
||||
executeEvent('addTab', {
|
||||
data: dataBody,
|
||||
});
|
||||
setInfoSnackCustom({
|
||||
type: "success",
|
||||
message: "Opened",
|
||||
type: 'success',
|
||||
message: 'Opened',
|
||||
});
|
||||
if (setLoadingStatePrivateApp) {
|
||||
|
||||
setLoadingStatePrivateApp(``);
|
||||
}
|
||||
if (addToPinnedApps) {
|
||||
@ -205,8 +195,8 @@ export const useHandlePrivateApps = () => {
|
||||
];
|
||||
|
||||
saveToLocalStorage(
|
||||
"ext_saved_settings",
|
||||
"sortablePinnedApps",
|
||||
'ext_saved_settings',
|
||||
'sortablePinnedApps',
|
||||
updatedApps
|
||||
);
|
||||
return updatedApps;
|
||||
@ -216,19 +206,18 @@ export const useHandlePrivateApps = () => {
|
||||
}
|
||||
} catch (error) {
|
||||
if (setLoadingStatePrivateApp) {
|
||||
|
||||
setLoadingStatePrivateApp(`Error! ${error?.message || 'Unable to build private app.'}`);
|
||||
setLoadingStatePrivateApp(
|
||||
`Error! ${error?.message || 'Unable to build private app.'}`
|
||||
);
|
||||
}
|
||||
throw error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
setInfoSnackCustom({
|
||||
type: "error",
|
||||
message: error?.message || "Unable to fetch app",
|
||||
type: 'error',
|
||||
message: error?.message || 'Unable to fetch app',
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
return {
|
||||
openApp,
|
||||
|
@ -1,15 +1,12 @@
|
||||
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { navigationControllerAtom } from '../../atoms/global';
|
||||
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
|
||||
import { saveFile } from '../../qortalRequests/get';
|
||||
import { mimeToExtensionMap } from '../../utils/memeTypes';
|
||||
import { MyContext } from '../../App';
|
||||
import FileSaver from 'file-saver';
|
||||
|
||||
|
||||
|
||||
import { useSetAtom } from 'jotai';
|
||||
|
||||
export const saveFileInChunks = async (
|
||||
blob: Blob,
|
||||
@ -45,7 +42,6 @@ export const saveFileInChunks = async (
|
||||
|
||||
// Map MIME type to file extension
|
||||
const mimeTypeToExtension = (mimeType: string): string => {
|
||||
|
||||
return mimeToExtensionMap[mimeType] || existingExtension || ''; // Use existing extension if MIME type not found
|
||||
};
|
||||
|
||||
@ -76,21 +72,18 @@ export const saveFileInChunks = async (
|
||||
offset += chunkSize;
|
||||
isFirstChunk = false;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error saving file in chunks:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Helper function to convert a Blob to a Base64 string
|
||||
const blobToBase64 = (blob: Blob): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
const base64data = reader.result?.toString().split(",")[1];
|
||||
resolve(base64data || "");
|
||||
const base64data = reader.result?.toString().split(',')[1];
|
||||
resolve(base64data || '');
|
||||
};
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(blob);
|
||||
@ -99,67 +92,68 @@ const blobToBase64 = (blob: Blob): Promise<string> => {
|
||||
|
||||
class Semaphore {
|
||||
constructor(count) {
|
||||
this.count = count
|
||||
this.waiting = []
|
||||
this.count = count;
|
||||
this.waiting = [];
|
||||
}
|
||||
acquire() {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
if (this.count > 0) {
|
||||
this.count--
|
||||
resolve()
|
||||
this.count--;
|
||||
resolve();
|
||||
} else {
|
||||
this.waiting.push(resolve)
|
||||
this.waiting.push(resolve);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
release() {
|
||||
if (this.waiting.length > 0) {
|
||||
const resolve = this.waiting.shift()
|
||||
resolve()
|
||||
const resolve = this.waiting.shift();
|
||||
resolve();
|
||||
} else {
|
||||
this.count++
|
||||
this.count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
let semaphore = new Semaphore(1)
|
||||
let reader = new FileReader()
|
||||
let semaphore = new Semaphore(1);
|
||||
let reader = new FileReader();
|
||||
|
||||
const fileToBase64 = (file) => new Promise(async (resolve, reject) => {
|
||||
const fileToBase64 = (file) =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
if (!reader) {
|
||||
reader = new FileReader()
|
||||
reader = new FileReader();
|
||||
}
|
||||
await semaphore.acquire()
|
||||
reader.readAsDataURL(file)
|
||||
await semaphore.acquire();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
const dataUrl = reader.result
|
||||
if (typeof dataUrl === "string") {
|
||||
const base64String = dataUrl.split(',')[1]
|
||||
reader.onload = null
|
||||
reader.onerror = null
|
||||
resolve(base64String)
|
||||
const dataUrl = reader.result;
|
||||
if (typeof dataUrl === 'string') {
|
||||
const base64String = dataUrl.split(',')[1];
|
||||
reader.onload = null;
|
||||
reader.onerror = null;
|
||||
resolve(base64String);
|
||||
} else {
|
||||
reader.onload = null
|
||||
reader.onerror = null
|
||||
reject(new Error('Invalid data URL'))
|
||||
}
|
||||
semaphore.release()
|
||||
reader.onload = null;
|
||||
reader.onerror = null;
|
||||
reject(new Error('Invalid data URL'));
|
||||
}
|
||||
semaphore.release();
|
||||
};
|
||||
reader.onerror = (error) => {
|
||||
reader.onload = null
|
||||
reader.onerror = null
|
||||
reject(error)
|
||||
semaphore.release()
|
||||
}
|
||||
})
|
||||
reader.onload = null;
|
||||
reader.onerror = null;
|
||||
reject(error);
|
||||
semaphore.release();
|
||||
};
|
||||
});
|
||||
|
||||
export function openIndexedDB() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open("fileStorageDB", 1);
|
||||
const request = indexedDB.open('fileStorageDB', 1);
|
||||
|
||||
request.onupgradeneeded = function (event) {
|
||||
const db = event.target.result;
|
||||
if (!db.objectStoreNames.contains("files")) {
|
||||
db.createObjectStore("files", { keyPath: "id" });
|
||||
if (!db.objectStoreNames.contains('files')) {
|
||||
db.createObjectStore('files', { keyPath: 'id' });
|
||||
}
|
||||
};
|
||||
|
||||
@ -168,12 +162,11 @@ export function openIndexedDB() {
|
||||
};
|
||||
|
||||
request.onerror = function () {
|
||||
reject("Error opening IndexedDB");
|
||||
reject('Error opening IndexedDB');
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export const listOfAllQortalRequests = [
|
||||
'GET_USER_ACCOUNT',
|
||||
'DECRYPT_DATA',
|
||||
@ -259,8 +252,8 @@ export const listOfAllQortalRequests = [
|
||||
'UPDATE_GROUP',
|
||||
'SELL_NAME',
|
||||
'CANCEL_SELL_NAME',
|
||||
'BUY_NAME'
|
||||
]
|
||||
'BUY_NAME',
|
||||
];
|
||||
|
||||
export const UIQortalRequests = [
|
||||
'GET_USER_ACCOUNT',
|
||||
@ -319,17 +312,13 @@ export const UIQortalRequests = [
|
||||
'UPDATE_GROUP',
|
||||
'SELL_NAME',
|
||||
'CANCEL_SELL_NAME',
|
||||
'BUY_NAME'
|
||||
'BUY_NAME',
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async function retrieveFileFromIndexedDB(fileId) {
|
||||
const db = await openIndexedDB();
|
||||
const transaction = db.transaction(["files"], "readwrite");
|
||||
const objectStore = transaction.objectStore("files");
|
||||
const transaction = db.transaction(['files'], 'readwrite');
|
||||
const objectStore = transaction.objectStore('files');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const getRequest = objectStore.get(fileId);
|
||||
@ -341,12 +330,12 @@ export const UIQortalRequests = [
|
||||
objectStore.delete(fileId);
|
||||
resolve(file);
|
||||
} else {
|
||||
reject("File not found in IndexedDB");
|
||||
reject('File not found in IndexedDB');
|
||||
}
|
||||
};
|
||||
|
||||
getRequest.onerror = function () {
|
||||
reject("Error retrieving file from IndexedDB");
|
||||
reject('Error retrieving file from IndexedDB');
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -354,8 +343,8 @@ export const UIQortalRequests = [
|
||||
async function deleteQortalFilesFromIndexedDB() {
|
||||
try {
|
||||
const db = await openIndexedDB();
|
||||
const transaction = db.transaction(["files"], "readwrite");
|
||||
const objectStore = transaction.objectStore("files");
|
||||
const transaction = db.transaction(['files'], 'readwrite');
|
||||
const objectStore = transaction.objectStore('files');
|
||||
|
||||
// Create a request to get all keys
|
||||
const getAllKeysRequest = objectStore.getAllKeys();
|
||||
@ -365,99 +354,89 @@ export const UIQortalRequests = [
|
||||
|
||||
// Iterate through keys to find and delete those containing '_qortalfile'
|
||||
for (let key of keys) {
|
||||
if (key.includes("_qortalfile")) {
|
||||
if (key.includes('_qortalfile')) {
|
||||
const deleteRequest = objectStore.delete(key);
|
||||
|
||||
deleteRequest.onsuccess = function () {
|
||||
console.log(`File with key '${key}' has been deleted from IndexedDB`);
|
||||
console.log(
|
||||
`File with key '${key}' has been deleted from IndexedDB`
|
||||
);
|
||||
};
|
||||
|
||||
deleteRequest.onerror = function () {
|
||||
console.error(`Failed to delete file with key '${key}' from IndexedDB`);
|
||||
console.error(
|
||||
`Failed to delete file with key '${key}' from IndexedDB`
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getAllKeysRequest.onerror = function () {
|
||||
console.error("Failed to retrieve keys from IndexedDB");
|
||||
console.error('Failed to retrieve keys from IndexedDB');
|
||||
};
|
||||
|
||||
transaction.oncomplete = function () {
|
||||
console.log("Transaction complete for deleting files from IndexedDB");
|
||||
console.log('Transaction complete for deleting files from IndexedDB');
|
||||
};
|
||||
|
||||
transaction.onerror = function () {
|
||||
console.error("Error occurred during transaction for deleting files");
|
||||
console.error('Error occurred during transaction for deleting files');
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error opening IndexedDB:", error);
|
||||
console.error('Error opening IndexedDB:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export const showSaveFilePicker = async (data, {openSnackGlobal,
|
||||
setOpenSnackGlobal,
|
||||
infoSnackCustom,
|
||||
setInfoSnackCustom}) => {
|
||||
|
||||
|
||||
export const showSaveFilePicker = async (
|
||||
data,
|
||||
{ openSnackGlobal, setOpenSnackGlobal, infoSnackCustom, setInfoSnackCustom }
|
||||
) => {
|
||||
try {
|
||||
const { filename, mimeType, blob, fileHandleOptions } = data;
|
||||
|
||||
setInfoSnackCustom({
|
||||
type: "info",
|
||||
message:
|
||||
"Saving file...",
|
||||
type: 'info',
|
||||
message: 'Saving file...',
|
||||
});
|
||||
|
||||
|
||||
setOpenSnackGlobal(true);
|
||||
|
||||
|
||||
|
||||
FileSaver.saveAs(blob, filename)
|
||||
FileSaver.saveAs(blob, filename);
|
||||
|
||||
setInfoSnackCustom({
|
||||
type: "success",
|
||||
message:
|
||||
"Saving file success!",
|
||||
type: 'success',
|
||||
message: 'Saving file success!',
|
||||
});
|
||||
|
||||
|
||||
setOpenSnackGlobal(true);
|
||||
} catch (error) {
|
||||
setInfoSnackCustom({
|
||||
type: "error",
|
||||
message:
|
||||
error?.message ? `Error saving file: ${error?.message}` : 'Error saving file',
|
||||
type: 'error',
|
||||
message: error?.message
|
||||
? `Error saving file: ${error?.message}`
|
||||
: 'Error saving file',
|
||||
});
|
||||
|
||||
|
||||
setOpenSnackGlobal(true);
|
||||
console.error("Error saving file:", error);
|
||||
|
||||
console.error('Error saving file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
declare var cordova: any;
|
||||
|
||||
|
||||
|
||||
async function storeFilesInIndexedDB(obj) {
|
||||
// First delete any existing files in IndexedDB with '_qortalfile' in their ID
|
||||
await deleteQortalFilesFromIndexedDB();
|
||||
|
||||
// Open the IndexedDB
|
||||
const db = await openIndexedDB();
|
||||
const transaction = db.transaction(["files"], "readwrite");
|
||||
const objectStore = transaction.objectStore("files");
|
||||
const transaction = db.transaction(['files'], 'readwrite');
|
||||
const objectStore = transaction.objectStore('files');
|
||||
|
||||
// Handle the obj.file if it exists and is a File instance
|
||||
if (obj.file) {
|
||||
const fileId = Date.now() + "objFile_qortalfile";
|
||||
const fileId = Date.now() + 'objFile_qortalfile';
|
||||
|
||||
// Store the file in IndexedDB
|
||||
const fileData = {
|
||||
@ -471,7 +450,7 @@ export const UIQortalRequests = [
|
||||
delete obj.file;
|
||||
}
|
||||
if (obj.blob) {
|
||||
const fileId = Date.now() + "objFile_qortalfile";
|
||||
const fileId = Date.now() + 'objFile_qortalfile';
|
||||
|
||||
// Store the file in IndexedDB
|
||||
const fileData = {
|
||||
@ -482,17 +461,17 @@ export const UIQortalRequests = [
|
||||
|
||||
// Replace the file object with the file ID in the original object
|
||||
let blobObj = {
|
||||
type: obj.blob?.type
|
||||
}
|
||||
type: obj.blob?.type,
|
||||
};
|
||||
obj.fileId = fileId;
|
||||
delete obj.blob;
|
||||
obj.blob = blobObj
|
||||
obj.blob = blobObj;
|
||||
}
|
||||
|
||||
// Iterate through resources to find files and save them to IndexedDB
|
||||
for (let resource of (obj?.resources || [])) {
|
||||
for (let resource of obj?.resources || []) {
|
||||
if (resource.file) {
|
||||
const fileId = resource.identifier + Date.now() + "_qortalfile";
|
||||
const fileId = resource.identifier + Date.now() + '_qortalfile';
|
||||
|
||||
// Store the file in IndexedDB
|
||||
const fileData = {
|
||||
@ -509,30 +488,39 @@ export const UIQortalRequests = [
|
||||
|
||||
// Set transaction completion handlers
|
||||
transaction.oncomplete = function () {
|
||||
console.log("Files saved successfully to IndexedDB");
|
||||
console.log('Files saved successfully to IndexedDB');
|
||||
};
|
||||
|
||||
transaction.onerror = function () {
|
||||
console.error("Error saving files to IndexedDB");
|
||||
console.error('Error saving files to IndexedDB');
|
||||
};
|
||||
|
||||
return obj; // Updated object with references to stored files
|
||||
}
|
||||
|
||||
export const useQortalMessageListener = (frameWindow, iframeRef, tabId, isDevMode, appName, appService, skipAuth) => {
|
||||
const [path, setPath] = useState('')
|
||||
export const useQortalMessageListener = (
|
||||
frameWindow,
|
||||
iframeRef,
|
||||
tabId,
|
||||
isDevMode,
|
||||
appName,
|
||||
appService,
|
||||
skipAuth
|
||||
) => {
|
||||
const [path, setPath] = useState('');
|
||||
const [history, setHistory] = useState({
|
||||
customQDNHistoryPaths: [],
|
||||
currentIndex: -1,
|
||||
isDOMContentLoaded: false
|
||||
})
|
||||
const setHasSettingsChangedAtom = useSetRecoilState(navigationControllerAtom);
|
||||
const { openSnackGlobal,
|
||||
isDOMContentLoaded: false,
|
||||
});
|
||||
const setHasSettingsChangedAtom = useSetAtom(navigationControllerAtom);
|
||||
|
||||
const {
|
||||
openSnackGlobal,
|
||||
setOpenSnackGlobal,
|
||||
infoSnackCustom,
|
||||
setInfoSnackCustom } = useContext(MyContext);
|
||||
|
||||
|
||||
setInfoSnackCustom,
|
||||
} = useContext(MyContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (tabId && !isNaN(history?.currentIndex)) {
|
||||
@ -541,49 +529,59 @@ isDOMContentLoaded: false
|
||||
...prev,
|
||||
[tabId]: {
|
||||
hasBack: history?.currentIndex > 0,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [history?.currentIndex, tabId])
|
||||
|
||||
}, [history?.currentIndex, tabId]);
|
||||
|
||||
const changeCurrentIndex = useCallback((value) => {
|
||||
setHistory((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
currentIndex: value
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
currentIndex: value,
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const resetHistory = useCallback(() => {
|
||||
setHistory({
|
||||
customQDNHistoryPaths: [],
|
||||
currentIndex: -1,
|
||||
isManualNavigation: true,
|
||||
isDOMContentLoaded: false
|
||||
})
|
||||
}, [])
|
||||
isDOMContentLoaded: false,
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const listener = async (event) => {
|
||||
|
||||
|
||||
if (event?.data?.requestedHandler !== 'UI') return;
|
||||
|
||||
const sendMessageToRuntime = (message, eventPort) => {
|
||||
window.sendMessage(message.action, message.payload, 300000, message.isExtension, {
|
||||
name: appName, service: appService
|
||||
}, skipAuth)
|
||||
window
|
||||
.sendMessage(
|
||||
message.action,
|
||||
message.payload,
|
||||
300000,
|
||||
message.isExtension,
|
||||
{
|
||||
name: appName,
|
||||
service: appService,
|
||||
},
|
||||
skipAuth
|
||||
)
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
eventPort.postMessage({
|
||||
result: null,
|
||||
error: {
|
||||
error: response?.error,
|
||||
message: typeof response?.error === 'string' ? response?.error : typeof response?.message === 'string' ? response?.message : 'An error has occurred'
|
||||
message:
|
||||
typeof response?.error === 'string'
|
||||
? response?.error
|
||||
: typeof response?.message === 'string'
|
||||
? response?.message
|
||||
: 'An error has occurred',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
@ -594,41 +592,47 @@ isDOMContentLoaded: false
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
console.error('Failed qortalRequest', error);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// Check if action is included in the predefined list of UI requests
|
||||
if (UIQortalRequests.includes(event.data.action)) {
|
||||
sendMessageToRuntime(
|
||||
{ action: event.data.action, type: 'qortalRequest', payload: event.data, isExtension: true },
|
||||
{
|
||||
action: event.data.action,
|
||||
type: 'qortalRequest',
|
||||
payload: event.data,
|
||||
isExtension: true,
|
||||
},
|
||||
event.ports[0]
|
||||
);
|
||||
} else if(event?.data?.action === 'SAVE_FILE'
|
||||
){
|
||||
} else if (event?.data?.action === 'SAVE_FILE') {
|
||||
try {
|
||||
const res = await saveFile(event.data, null, true, {
|
||||
openSnackGlobal,
|
||||
setOpenSnackGlobal,
|
||||
infoSnackCustom,
|
||||
setInfoSnackCustom
|
||||
setInfoSnackCustom,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
} catch (error) {}
|
||||
} else if (
|
||||
event?.data?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' ||
|
||||
event?.data?.action === 'PUBLISH_QDN_RESOURCE' ||
|
||||
event?.data?.action === 'ENCRYPT_DATA' || event?.data?.action === 'ENCRYPT_DATA_WITH_SHARING_KEY' || event?.data?.action === 'ENCRYPT_QORTAL_GROUP_DATA'
|
||||
|
||||
event?.data?.action === 'ENCRYPT_DATA' ||
|
||||
event?.data?.action === 'ENCRYPT_DATA_WITH_SHARING_KEY' ||
|
||||
event?.data?.action === 'ENCRYPT_QORTAL_GROUP_DATA'
|
||||
) {
|
||||
const data = event.data;
|
||||
|
||||
if (data) {
|
||||
sendMessageToRuntime(
|
||||
{ action: event.data.action, type: 'qortalRequest', payload: data, isExtension: true },
|
||||
{
|
||||
action: event.data.action,
|
||||
type: 'qortalRequest',
|
||||
payload: data,
|
||||
isExtension: true,
|
||||
},
|
||||
event.ports[0]
|
||||
);
|
||||
} else {
|
||||
@ -637,52 +641,73 @@ isDOMContentLoaded: false
|
||||
error: 'Failed to prepare data for publishing',
|
||||
});
|
||||
}
|
||||
} else if(event?.data?.action === 'LINK_TO_QDN_RESOURCE' ||
|
||||
event?.data?.action === 'QDN_RESOURCE_DISPLAYED'){
|
||||
const pathUrl = event?.data?.path != null ? (event?.data?.path.startsWith('/') ? '' : '/') + event?.data?.path : null
|
||||
setPath(pathUrl)
|
||||
} else if (
|
||||
event?.data?.action === 'LINK_TO_QDN_RESOURCE' ||
|
||||
event?.data?.action === 'QDN_RESOURCE_DISPLAYED'
|
||||
) {
|
||||
const pathUrl =
|
||||
event?.data?.path != null
|
||||
? (event?.data?.path.startsWith('/') ? '' : '/') + event?.data?.path
|
||||
: null;
|
||||
setPath(pathUrl);
|
||||
if (appName?.toLowerCase() === 'q-mail') {
|
||||
window.sendMessage("addEnteredQmailTimestamp").catch((error) => {
|
||||
window.sendMessage('addEnteredQmailTimestamp').catch((error) => {
|
||||
// error
|
||||
});
|
||||
} else if (appName?.toLowerCase() === 'q-wallets') {
|
||||
executeEvent('setLastEnteredTimestampPaymentEvent', {})
|
||||
executeEvent('setLastEnteredTimestampPaymentEvent', {});
|
||||
}
|
||||
} else if (event?.data?.action === 'NAVIGATION_HISTORY') {
|
||||
if (event?.data?.payload?.isDOMContentLoaded) {
|
||||
setHistory((prev) => {
|
||||
const copyPrev = {...prev}
|
||||
if((copyPrev?.customQDNHistoryPaths || []).at(-1) === (event?.data?.payload?.customQDNHistoryPaths || []).at(-1)) {
|
||||
const copyPrev = { ...prev };
|
||||
if (
|
||||
(copyPrev?.customQDNHistoryPaths || []).at(-1) ===
|
||||
(event?.data?.payload?.customQDNHistoryPaths || []).at(-1)
|
||||
) {
|
||||
return {
|
||||
...prev,
|
||||
currentIndex: prev.customQDNHistoryPaths.length - 1 === -1 ? 0 : prev.customQDNHistoryPaths.length - 1
|
||||
currentIndex:
|
||||
prev.customQDNHistoryPaths.length - 1 === -1
|
||||
? 0
|
||||
: prev.customQDNHistoryPaths.length - 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
const copyHistory = {...prev}
|
||||
const paths = [...(copyHistory?.customQDNHistoryPaths.slice(0, copyHistory.currentIndex + 1) || []), ...(event?.data?.payload?.customQDNHistoryPaths || [])]
|
||||
const copyHistory = { ...prev };
|
||||
const paths = [
|
||||
...(copyHistory?.customQDNHistoryPaths.slice(
|
||||
0,
|
||||
copyHistory.currentIndex + 1
|
||||
) || []),
|
||||
...(event?.data?.payload?.customQDNHistoryPaths || []),
|
||||
];
|
||||
return {
|
||||
...prev,
|
||||
customQDNHistoryPaths: paths,
|
||||
currentIndex: paths.length - 1
|
||||
}
|
||||
})
|
||||
currentIndex: paths.length - 1,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
setHistory(event?.data?.payload)
|
||||
|
||||
setHistory(event?.data?.payload);
|
||||
}
|
||||
} else if (event?.data?.action === 'SET_TAB' && !isDevMode) {
|
||||
executeEvent("addTab", {
|
||||
data: event?.data?.payload
|
||||
})
|
||||
const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*";
|
||||
executeEvent('addTab', {
|
||||
data: event?.data?.payload,
|
||||
});
|
||||
const targetOrigin = iframeRef.current
|
||||
? new URL(iframeRef.current.src).origin
|
||||
: '*';
|
||||
iframeRef.current.contentWindow.postMessage(
|
||||
{ action: 'SET_TAB_SUCCESS', requestedHandler: 'UI',payload: {
|
||||
name: event?.data?.payload?.name
|
||||
} }, targetOrigin
|
||||
{
|
||||
action: 'SET_TAB_SUCCESS',
|
||||
requestedHandler: 'UI',
|
||||
payload: {
|
||||
name: event?.data?.payload?.name,
|
||||
},
|
||||
},
|
||||
targetOrigin
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
// Add the listener for messages coming from the frameWindow
|
||||
@ -692,12 +717,7 @@ isDOMContentLoaded: false
|
||||
return () => {
|
||||
frameWindow.removeEventListener('message', listener);
|
||||
};
|
||||
|
||||
|
||||
}, [isDevMode, appName, appService]); // Empty dependency array to run once when the component mounts
|
||||
|
||||
|
||||
|
||||
return {path, history, resetHistory, changeCurrentIndex}
|
||||
return { path, history, resetHistory, changeCurrentIndex };
|
||||
};
|
||||
|
||||
|
@ -1,54 +0,0 @@
|
||||
import {
|
||||
MainContainer,
|
||||
ChatContainer,
|
||||
MessageList,
|
||||
Message,
|
||||
MessageInput,
|
||||
Avatar,
|
||||
} from '@chatscope/chat-ui-kit-react';
|
||||
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
||||
|
||||
export const ChatContainerComp = ({ messages }) => {
|
||||
// const [messages, setMessages] = useState([
|
||||
// { id: 1, text: "Hello! How are you?", sender: "Joe"},
|
||||
// { id: 2, text: "I'm good, thank you!", sender: "Me" }
|
||||
// ]);
|
||||
|
||||
// const loadMoreMessages = () => {
|
||||
// // Simulate loading more messages (you could fetch these from an API)
|
||||
// const moreMessages = [
|
||||
// { id: 3, text: "What about you?", sender: "Joe", direction: "incoming" },
|
||||
// { id: 4, text: "I'm great, thanks!", sender: "Me", direction: "outgoing" }
|
||||
// ];
|
||||
// setMessages((prevMessages) => [...moreMessages, ...prevMessages]);
|
||||
// };
|
||||
|
||||
return (
|
||||
<div style={{ height: '500px', width: '300px' }}>
|
||||
<MainContainer>
|
||||
<ChatContainer>
|
||||
<MessageList>
|
||||
{messages.map((msg) => (
|
||||
<Message
|
||||
key={msg.id}
|
||||
model={{
|
||||
message: msg.text,
|
||||
sentTime: 'just now',
|
||||
sender: msg.senderName,
|
||||
direction: 'incoming',
|
||||
position: 'single',
|
||||
}}
|
||||
>
|
||||
{msg.direction === 'incoming' && (
|
||||
<Avatar name={msg.senderName} />
|
||||
)}
|
||||
</Message>
|
||||
))}
|
||||
</MessageList>
|
||||
|
||||
<MessageInput placeholder="Type a message..." />
|
||||
</ChatContainer>
|
||||
</MainContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -8,7 +8,6 @@ import React, {
|
||||
} from 'react';
|
||||
|
||||
import { ChatList } from './ChatList';
|
||||
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
||||
import Tiptap from './TipTap';
|
||||
import { CustomButton } from '../../styles/App-styles';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
objectToBase64,
|
||||
} from '../../qdn/encryption/group-encryption';
|
||||
import { ChatList } from './ChatList';
|
||||
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
||||
import Tiptap from './TipTap';
|
||||
import { CustomButton } from '../../styles/App-styles';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
@ -1238,8 +1237,6 @@ export const ChatGroup = ({
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* <ChatContainerComp messages={formatMessages} /> */}
|
||||
|
||||
<LoadingSnackbar
|
||||
open={isLoading}
|
||||
info={{
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
base64ToUint8Array,
|
||||
objectToBase64,
|
||||
} from '../../qdn/encryption/group-encryption';
|
||||
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
||||
import Tiptap from './TipTap';
|
||||
import { CustomButton } from '../../styles/App-styles';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
|
@ -27,9 +27,9 @@ import tippy from 'tippy.js';
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
import { ReactRenderer } from '@tiptap/react';
|
||||
import MentionList from './MentionList.jsx';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { isDisabledEditorEnterAtom } from '../../atoms/global.js';
|
||||
import { Box, Checkbox, Typography, useTheme } from '@mui/material';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
function textMatcher(doc, from) {
|
||||
const textBeforeCursor = doc.textBetween(0, from, ' ', ' ');
|
||||
@ -368,9 +368,10 @@ export default ({
|
||||
enableMentions,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useRecoilState(
|
||||
const [isDisabledEditorEnter, setIsDisabledEditorEnter] = useAtom(
|
||||
isDisabledEditorEnterAtom
|
||||
);
|
||||
|
||||
const extensionsFiltered = isChat
|
||||
? extensions.filter((item) => item?.name !== 'image')
|
||||
: extensions;
|
||||
|
@ -10,8 +10,8 @@ import {
|
||||
import MailOutlineIcon from '@mui/icons-material/MailOutline';
|
||||
import NotificationsOffIcon from '@mui/icons-material/NotificationsOff';
|
||||
import { executeEvent } from '../utils/events';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { mutedGroupsAtom } from '../atoms/global';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
const CustomStyledMenu = styled(Menu)(({ theme }) => ({
|
||||
'& .MuiPaper-root': {
|
||||
@ -35,7 +35,8 @@ export const ContextMenu = ({ children, groupId, getUserSettings }) => {
|
||||
const longPressTimeout = useRef(null);
|
||||
const preventClick = useRef(false); // Flag to prevent click after long-press or right-click
|
||||
const theme = useTheme();
|
||||
const [mutedGroups] = useRecoilState(mutedGroupsAtom);
|
||||
const [mutedGroups] = useAtom(mutedGroupsAtom);
|
||||
|
||||
const isMuted = useMemo(() => {
|
||||
return mutedGroups.includes(groupId);
|
||||
}, [mutedGroups, groupId]);
|
||||
|
@ -9,8 +9,8 @@ import {
|
||||
} from '@mui/material';
|
||||
import PushPinIcon from '@mui/icons-material/PushPin';
|
||||
import { saveToLocalStorage } from './Apps/AppsNavBarDesktop';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { sortablePinnedAppsAtom } from '../atoms/global';
|
||||
import { useSetAtom } from 'jotai';
|
||||
|
||||
const CustomStyledMenu = styled(Menu)(({ theme }) => ({
|
||||
'& .MuiPaper-root': {
|
||||
@ -34,9 +34,9 @@ export const ContextMenuPinnedApps = ({ children, app, isMine }) => {
|
||||
const maxHoldTimeout = useRef(null);
|
||||
const preventClick = useRef(false);
|
||||
const startTouchPosition = useRef({ x: 0, y: 0 }); // Track initial touch position
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
|
||||
const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const handleContextMenu = (event) => {
|
||||
|
@ -6,8 +6,9 @@ import AppIcon from '../../assets/svgs/AppIcon.svg';
|
||||
|
||||
import { HomeIcon } from '../../assets/Icons/HomeIcon';
|
||||
import { Save } from '../Save/Save';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { enabledDevModeAtom } from '../../atoms/global';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
export const IconWrapper = ({
|
||||
children,
|
||||
@ -65,8 +66,7 @@ export const DesktopFooter = ({
|
||||
setIsOpenSideViewDirects,
|
||||
setIsOpenSideViewGroups,
|
||||
}) => {
|
||||
const [isEnabledDevMode, setIsEnabledDevMode] =
|
||||
useRecoilState(enabledDevModeAtom);
|
||||
const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
|
@ -2,13 +2,13 @@ import { Box, ButtonBase, useTheme } from '@mui/material';
|
||||
import { HomeIcon } from '../assets/Icons/HomeIcon';
|
||||
import { Save } from './Save/Save';
|
||||
import { IconWrapper } from './Desktop/DesktopFooter';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { enabledDevModeAtom } from '../atoms/global';
|
||||
import { AppsIcon } from '../assets/Icons/AppsIcon';
|
||||
import ThemeSelector from './Theme/ThemeSelector';
|
||||
import { CoreSyncStatus } from './CoreSyncStatus';
|
||||
import LanguageSelector from './Language/LanguageSelector';
|
||||
import { MessagingIconFilled } from '../assets/Icons/MessagingIconFilled';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
export const DesktopSideBar = ({
|
||||
goToHome,
|
||||
@ -24,13 +24,10 @@ export const DesktopSideBar = ({
|
||||
desktopViewMode,
|
||||
myName,
|
||||
}) => {
|
||||
const [isEnabledDevMode, setIsEnabledDevMode] =
|
||||
useRecoilState(enabledDevModeAtom);
|
||||
const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
console.log('test', desktopViewMode === 'home');
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -27,8 +27,7 @@ import { Spacer } from '../../common/Spacer';
|
||||
import { FileAttachmentContainer, FileAttachmentFont } from './Embed-styles';
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
import SaveIcon from '@mui/icons-material/Save';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { blobControllerAtom } from '../../atoms/global';
|
||||
|
||||
import { decodeIfEncoded } from '../../utils/decode';
|
||||
|
||||
export const AttachmentCard = ({
|
||||
|
@ -7,7 +7,6 @@ import { extractComponents } from '../Chat/MessageDisplay';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
|
||||
import { base64ToBlobUrl } from '../../utils/fileReading';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import {
|
||||
blobControllerAtom,
|
||||
blobKeySelector,
|
||||
@ -19,6 +18,7 @@ import { PollCard } from './PollEmbed';
|
||||
import { ImageCard } from './ImageEmbed';
|
||||
import { AttachmentCard } from './AttachmentEmbed';
|
||||
import { decodeIfEncoded } from '../../utils/decode';
|
||||
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
|
||||
|
||||
const getPoll = async (name) => {
|
||||
const pollName = name;
|
||||
@ -63,8 +63,9 @@ export const Embed = ({ embedLink }) => {
|
||||
const [external, setExternal] = useState(null);
|
||||
const [imageUrl, setImageUrl] = useState('');
|
||||
const [parsedData, setParsedData] = useState(null);
|
||||
const setBlobs = useSetRecoilState(blobControllerAtom);
|
||||
const [selectedGroupId] = useRecoilState(selectedGroupIdAtom);
|
||||
const setBlobs = useSetAtom(blobControllerAtom);
|
||||
const [selectedGroupId] = useAtom(selectedGroupIdAtom);
|
||||
|
||||
const resourceData = useMemo(() => {
|
||||
const parsedDataOnTheFly = parseQortalLink(embedLink);
|
||||
if (
|
||||
@ -98,7 +99,8 @@ export const Embed = ({ embedLink }) => {
|
||||
return undefined;
|
||||
}
|
||||
}, [resourceData]);
|
||||
const blobUrl = useRecoilValue(blobKeySelector(keyIdentifier));
|
||||
|
||||
const blobUrl = useAtomValue(blobKeySelector(keyIdentifier));
|
||||
|
||||
const handlePoll = async (parsedData) => {
|
||||
try {
|
||||
@ -312,7 +314,7 @@ export const Embed = ({ embedLink }) => {
|
||||
hasFetched.current = true;
|
||||
}, [embedLink]);
|
||||
|
||||
const resourceDetails = useRecoilValue(resourceKeySelector(keyIdentifier));
|
||||
const resourceDetails = useAtomValue(resourceKeySelector(keyIdentifier));
|
||||
|
||||
const { parsedType, encryptionType } = useMemo(() => {
|
||||
let parsedType;
|
||||
|
@ -19,7 +19,8 @@ import { Menu, MenuItem } from '@mui/material';
|
||||
import { MoreVert as MoreIcon } from '@mui/icons-material';
|
||||
import { GlobalContext, getBaseApiReact } from '../../App';
|
||||
import { resourceKeySelector } from '../../atoms/global';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useAtomValue } from 'jotai';
|
||||
const VideoContainer = styled(Box)`
|
||||
position: relative;
|
||||
display: flex;
|
||||
@ -80,7 +81,9 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
return undefined;
|
||||
}
|
||||
}, [service, name, identifier]);
|
||||
const download = useRecoilValue(resourceKeySelector(keyIdentifier));
|
||||
|
||||
const download = useAtomValue(resourceKeySelector(keyIdentifier));
|
||||
|
||||
const { downloadResource } = useContext(GlobalContext);
|
||||
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||
|
@ -21,15 +21,16 @@ import {
|
||||
import { validateAddress } from '../../utils/validateAddress';
|
||||
import { getNameInfo, requestQueueMemberNames } from './Group';
|
||||
import { useModal } from '../../common/useModal';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { isOpenBlockedModalAtom } from '../../atoms/global';
|
||||
import InfoIcon from '@mui/icons-material/Info';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
export const BlockedUsersModal = () => {
|
||||
const theme = useTheme();
|
||||
const [isOpenBlockedModal, setIsOpenBlockedModal] = useRecoilState(
|
||||
const [isOpenBlockedModal, setIsOpenBlockedModal] = useAtom(
|
||||
isOpenBlockedModalAtom
|
||||
);
|
||||
|
||||
const [hasChanged, setHasChanged] = useState(false);
|
||||
const [value, setValue] = useState('');
|
||||
const [addressesWithNames, setAddressesWithNames] = useState({});
|
||||
|
@ -516,12 +516,7 @@ export const NewThread = ({
|
||||
overrideMobile
|
||||
customEditorHeight="240px"
|
||||
/>
|
||||
{/* <TextEditor
|
||||
inlineContent={value}
|
||||
setInlineContent={(val: any) => {
|
||||
setValue(val);
|
||||
}}
|
||||
/> */}
|
||||
|
||||
</Box>
|
||||
</InstanceListContainer>
|
||||
|
||||
|
@ -1,40 +0,0 @@
|
||||
import ReactQuill, { Quill } from 'react-quill';
|
||||
import 'react-quill/dist/quill.snow.css';
|
||||
import ImageResize from 'quill-image-resize-module-react';
|
||||
import './texteditor.css';
|
||||
|
||||
Quill.register('modules/imageResize', ImageResize);
|
||||
|
||||
const modules = {
|
||||
imageResize: {
|
||||
parchment: Quill.import('parchment'),
|
||||
modules: ['Resize', 'DisplaySize'],
|
||||
},
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline', 'strike'], // styled text
|
||||
['blockquote', 'code-block'], // blocks
|
||||
[{ header: 1 }, { header: 2 }], // custom button values
|
||||
[{ list: 'ordered' }, { list: 'bullet' }], // lists
|
||||
[{ script: 'sub' }, { script: 'super' }], // superscript/subscript
|
||||
[{ indent: '-1' }, { indent: '+1' }], // outdent/indent
|
||||
[{ direction: 'rtl' }], // text direction
|
||||
[{ size: ['small', false, 'large', 'huge'] }], // custom dropdown
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }], // custom button values
|
||||
[{ color: [] }, { background: [] }], // dropdown with defaults
|
||||
[{ font: [] }], // font family
|
||||
[{ align: [] }], // text align
|
||||
['clean'], // remove formatting
|
||||
// ["image"], // image
|
||||
],
|
||||
};
|
||||
|
||||
export const TextEditor = ({ inlineContent, setInlineContent }: any) => {
|
||||
return (
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={inlineContent}
|
||||
onChange={setInlineContent}
|
||||
modules={modules}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,70 +0,0 @@
|
||||
.ql-editor {
|
||||
min-height: 200px;
|
||||
width: 100%;
|
||||
color: black;
|
||||
font-size: 16px;
|
||||
font-family: Roboto;
|
||||
max-height: 225px;
|
||||
overflow-y: scroll;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.ql-editor::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
.ql-editor::-webkit-scrollbar-track:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.ql-editor::-webkit-scrollbar {
|
||||
width: 16px;
|
||||
height: 10px;
|
||||
background-color: rgba(229, 229, 229, 0.7);
|
||||
}
|
||||
|
||||
.ql-editor::-webkit-scrollbar-thumb {
|
||||
background-color: #b0b0b0;
|
||||
border-radius: 8px;
|
||||
background-clip: content-box;
|
||||
border: 4px solid transparent;
|
||||
}
|
||||
|
||||
.ql-editor img {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ql-editor-display {
|
||||
min-height: 20px;
|
||||
width: 100%;
|
||||
color: black;
|
||||
font-size: 16px;
|
||||
font-family: Roboto;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.ql-editor-display img {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ql-container {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.ql-toolbar .ql-stroke {
|
||||
fill: none !important;
|
||||
stroke: black !important;
|
||||
}
|
||||
|
||||
.ql-toolbar .ql-fill {
|
||||
fill: black !important;
|
||||
stroke: none !important;
|
||||
}
|
||||
|
||||
.ql-toolbar .ql-picker {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.ql-toolbar .ql-picker-options {
|
||||
background-color: white !important;
|
||||
}
|
@ -65,7 +65,7 @@ import { HubsIcon } from '../../assets/Icons/HubsIcon';
|
||||
import { MessagingIcon } from '../../assets/Icons/MessagingIcon';
|
||||
import { formatEmailDate } from './QMailMessages';
|
||||
import { AdminSpace } from '../Chat/AdminSpace';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import {
|
||||
addressInfoControllerAtom,
|
||||
groupAnnouncementsAtom,
|
||||
@ -85,6 +85,7 @@ import { BlockedUsersModal } from './BlockedUsersModal';
|
||||
import { WalletsAppWrapper } from './WalletsAppWrapper';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { GroupList } from './GroupList';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
|
||||
export const getPublishesFromAdmins = async (admins: string[], groupId) => {
|
||||
const queryString = admins.map((name) => `name=${name}`).join('&');
|
||||
@ -415,9 +416,10 @@ export const Group = ({
|
||||
const { setMemberGroups, rootHeight, isRunningPublicNode } =
|
||||
useContext(MyContext);
|
||||
const lastGroupNotification = useRef<null | number>(null);
|
||||
const [timestampEnterData, setTimestampEnterData] = useRecoilState(
|
||||
const [timestampEnterData, setTimestampEnterData] = useAtom(
|
||||
timestampEnterDataAtom
|
||||
);
|
||||
|
||||
const [chatMode, setChatMode] = useState('groups');
|
||||
const [newChat, setNewChat] = useState(false);
|
||||
const [openSnack, setOpenSnack] = React.useState(false);
|
||||
@ -428,18 +430,19 @@ export const Group = ({
|
||||
const [firstSecretKeyInCreation, setFirstSecretKeyInCreation] =
|
||||
React.useState(false);
|
||||
const [groupSection, setGroupSection] = React.useState('home');
|
||||
const [groupAnnouncements, setGroupAnnouncements] = useRecoilState(
|
||||
const [groupAnnouncements, setGroupAnnouncements] = useAtom(
|
||||
groupAnnouncementsAtom
|
||||
);
|
||||
|
||||
const [defaultThread, setDefaultThread] = React.useState(null);
|
||||
const [isOpenDrawer, setIsOpenDrawer] = React.useState(false);
|
||||
const setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom);
|
||||
const setIsOpenBlockedUserModal = useSetAtom(isOpenBlockedModalAtom);
|
||||
|
||||
const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false);
|
||||
const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState('');
|
||||
const [drawerMode, setDrawerMode] = React.useState('groups');
|
||||
const setMutedGroups = useSetRecoilState(mutedGroupsAtom);
|
||||
const setMutedGroups = useSetAtom(mutedGroupsAtom);
|
||||
|
||||
const [mobileViewMode, setMobileViewMode] = useState('home');
|
||||
const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState('');
|
||||
const isFocusedRef = useRef(true);
|
||||
@ -453,9 +456,10 @@ export const Group = ({
|
||||
const settimeoutForRefetchSecretKey = useRef(null);
|
||||
const { clearStatesMessageQueueProvider } = useMessageQueue();
|
||||
const initiatedGetMembers = useRef(false);
|
||||
const [groupChatTimestamps, setGroupChatTimestamps] = useRecoilState(
|
||||
const [groupChatTimestamps, setGroupChatTimestamps] = useAtom(
|
||||
groupChatTimestampsAtom
|
||||
);
|
||||
|
||||
const [appsMode, setAppsMode] = useState('home');
|
||||
const [appsModeDev, setAppsModeDev] = useState('home');
|
||||
const [isOpenSideViewDirects, setIsOpenSideViewDirects] = useState(false);
|
||||
@ -465,12 +469,10 @@ export const Group = ({
|
||||
const groupsOwnerNamesRef = useRef({});
|
||||
const { t } = useTranslation(['core', 'group']);
|
||||
|
||||
const [groupsProperties, setGroupsProperties] =
|
||||
useRecoilState(groupsPropertiesAtom);
|
||||
const [groupsOwnerNames, setGroupsOwnerNames] =
|
||||
useRecoilState(groupsOwnerNamesAtom);
|
||||
const [groupsProperties, setGroupsProperties] = useAtom(groupsPropertiesAtom);
|
||||
const setGroupsOwnerNames = useSetAtom(groupsOwnerNamesAtom);
|
||||
|
||||
const setUserInfoForLevels = useSetRecoilState(addressInfoControllerAtom);
|
||||
const setUserInfoForLevels = useSetAtom(addressInfoControllerAtom);
|
||||
|
||||
const isPrivate = useMemo(() => {
|
||||
if (selectedGroup?.groupId === '0') return false;
|
||||
@ -481,7 +483,8 @@ export const Group = ({
|
||||
return null;
|
||||
}, [selectedGroup]);
|
||||
|
||||
const setSelectedGroupId = useSetRecoilState(selectedGroupIdAtom);
|
||||
const setSelectedGroupId = useSetAtom(selectedGroupIdAtom);
|
||||
|
||||
const toggleSideViewDirects = () => {
|
||||
if (isOpenSideViewGroups) {
|
||||
setIsOpenSideViewGroups(false);
|
||||
|
@ -11,10 +11,10 @@ import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material';
|
||||
import { CustomLoader } from '../../common/CustomLoader';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import { myGroupsWhereIAmAdminAtom } from '../../atoms/global';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSetAtom } from 'jotai';
|
||||
export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2);
|
||||
|
||||
export const GroupJoinRequests = ({
|
||||
@ -34,7 +34,8 @@ export const GroupJoinRequests = ({
|
||||
);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const { txList, setTxList } = React.useContext(MyContext);
|
||||
const setMyGroupsWhereIAmAdmin = useSetRecoilState(myGroupsWhereIAmAdminAtom);
|
||||
const setMyGroupsWhereIAmAdmin = useSetAtom(myGroupsWhereIAmAdminAtom);
|
||||
|
||||
const theme = useTheme();
|
||||
const getJoinRequests = async () => {
|
||||
try {
|
||||
|
@ -28,8 +28,9 @@ import {
|
||||
groupsOwnerNamesSelector,
|
||||
timestampEnterDataSelector,
|
||||
} from '../../atoms/global';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { timeDifferenceForNotificationChats } from './Group';
|
||||
import { useAtomValue } from 'jotai';
|
||||
|
||||
export const GroupList = ({
|
||||
selectGroupFunc,
|
||||
@ -211,17 +212,18 @@ export const GroupList = ({
|
||||
const GroupItem = React.memo(
|
||||
({ selectGroupFunc, group, selectedGroup, getUserSettings, myAddress }) => {
|
||||
const theme = useTheme();
|
||||
const ownerName = useRecoilValue(groupsOwnerNamesSelector(group?.groupId));
|
||||
const announcement = useRecoilValue(
|
||||
const ownerName = useAtomValue(groupsOwnerNamesSelector(group?.groupId));
|
||||
const announcement = useAtomValue(
|
||||
groupAnnouncementSelector(group?.groupId)
|
||||
);
|
||||
const groupProperty = useRecoilValue(groupPropertySelector(group?.groupId));
|
||||
const groupChatTimestamp = useRecoilValue(
|
||||
const groupProperty = useAtomValue(groupPropertySelector(group?.groupId));
|
||||
const groupChatTimestamp = useAtomValue(
|
||||
groupChatTimestampSelector(group?.groupId)
|
||||
);
|
||||
const timestampEnterData = useRecoilValue(
|
||||
const timestampEnterData = useAtomValue(
|
||||
timestampEnterDataSelector(group?.groupId)
|
||||
);
|
||||
|
||||
const selectGroupHandler = useCallback(() => {
|
||||
selectGroupFunc(group);
|
||||
}, [group, selectGroupFunc]);
|
||||
|
@ -34,7 +34,7 @@ import {
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { CustomLoader } from '../../common/CustomLoader';
|
||||
import { RequestQueueWithPromise } from '../../utils/queue/queue';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import {
|
||||
myGroupsWhereIAmAdminAtom,
|
||||
promotionTimeIntervalAtom,
|
||||
@ -49,6 +49,7 @@ import ErrorBoundary from '../../common/ErrorBoundary';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||
import { getFee } from '../../background';
|
||||
import { useAtom } from 'jotai';
|
||||
export const requestQueuePromos = new RequestQueueWithPromise(3);
|
||||
|
||||
export function utf8ToBase64(inputString: string): string {
|
||||
@ -77,13 +78,14 @@ export const ListOfGroupPromotions = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isShowModal, setIsShowModal] = useState(false);
|
||||
const [text, setText] = useState('');
|
||||
const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useRecoilState(
|
||||
const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useAtom(
|
||||
myGroupsWhereIAmAdminAtom
|
||||
);
|
||||
const [promotions, setPromotions] = useRecoilState(promotionsAtom);
|
||||
const [promotionTimeInterval, setPromotionTimeInterval] = useRecoilState(
|
||||
const [promotions, setPromotions] = useAtom(promotionsAtom);
|
||||
const [promotionTimeInterval, setPromotionTimeInterval] = useAtom(
|
||||
promotionTimeIntervalAtom
|
||||
);
|
||||
|
||||
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||
|
||||
const [openSnack, setOpenSnack] = useState(false);
|
||||
|
@ -11,11 +11,12 @@ import MailIcon from '@mui/icons-material/Mail';
|
||||
import MailOutlineIcon from '@mui/icons-material/MailOutline';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { CustomLoader } from '../../common/CustomLoader';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { mailsAtom, qMailLastEnteredTimestampAtom } from '../../atoms/global';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||
import MarkEmailUnreadIcon from '@mui/icons-material/MarkEmailUnread';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
export const isLessThanOneWeekOld = (timestamp) => {
|
||||
// Current time in milliseconds
|
||||
@ -46,10 +47,11 @@ export function formatEmailDate(timestamp: number) {
|
||||
|
||||
export const QMailMessages = ({ userName, userAddress }) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [mails, setMails] = useRecoilState(mailsAtom);
|
||||
const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState(
|
||||
const [mails, setMails] = useAtom(mailsAtom);
|
||||
const [lastEnteredTimestamp, setLastEnteredTimestamp] = useAtom(
|
||||
qMailLastEnteredTimestampAtom
|
||||
);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const theme = useTheme();
|
||||
|
||||
|
@ -17,8 +17,9 @@ import Slide from '@mui/material/Slide';
|
||||
import { TransitionProps } from '@mui/material/transitions';
|
||||
import { Box, FormControlLabel, Switch, styled, useTheme } from '@mui/material';
|
||||
import { enabledDevModeAtom } from '../../atoms/global';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import ThemeManager from '../Theme/ThemeManager';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
const LocalNodeSwitch = styled(Switch)(({ theme }) => ({
|
||||
padding: 8,
|
||||
@ -64,8 +65,8 @@ const Transition = forwardRef(function Transition(
|
||||
|
||||
export const Settings = ({ address, open, setOpen }) => {
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [isEnabledDevMode, setIsEnabledDevMode] =
|
||||
useRecoilState(enabledDevModeAtom);
|
||||
const [isEnabledDevMode, setIsEnabledDevMode] = useAtom(enabledDevModeAtom);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
|
@ -7,18 +7,19 @@ import {
|
||||
subscribeToEvent,
|
||||
unsubscribeFromEvent,
|
||||
} from '../../utils/events';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { navigationControllerAtom } from '../../atoms/global';
|
||||
import { AppsNavBarLeft, AppsNavBarParent } from '../Apps/Apps-styles';
|
||||
import { NavBack } from '../../assets/Icons/NavBack.tsx';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
export const WalletsAppWrapper = () => {
|
||||
const iframeRef = useRef(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [navigationController, setNavigationController] = useRecoilState(
|
||||
const [navigationController, setNavigationController] = useAtom(
|
||||
navigationControllerAtom
|
||||
);
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState({
|
||||
tabId: '5558589',
|
||||
name: 'Q-Wallets',
|
||||
|
@ -1,20 +1,20 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { mailsAtom, qMailLastEnteredTimestampAtom } from '../atoms/global';
|
||||
import { isLessThanOneWeekOld } from './Group/QMailMessages';
|
||||
import { ButtonBase, Tooltip, useTheme } from '@mui/material';
|
||||
import { executeEvent } from '../utils/events';
|
||||
import { Mail } from '@mui/icons-material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
export const QMailStatus = () => {
|
||||
const { t } = useTranslation(['core']);
|
||||
const theme = useTheme();
|
||||
|
||||
const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState(
|
||||
const [lastEnteredTimestamp, setLastEnteredTimestamp] = useAtom(
|
||||
qMailLastEnteredTimestampAtom
|
||||
);
|
||||
const [mails, setMails] = useRecoilState(mailsAtom);
|
||||
const [mails, setMails] = useAtom(mailsAtom);
|
||||
|
||||
const hasNewMail = useMemo(() => {
|
||||
if (mails?.length === 0) return false;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import isEqual from 'lodash/isEqual'; // TODO Import deep comparison utility
|
||||
import {
|
||||
canSaveSettingToQdnAtom,
|
||||
@ -34,6 +33,7 @@ import {
|
||||
uint8ArrayToObject,
|
||||
} from '../../backgroundFunctions/encryption';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
|
||||
export const handleImportClick = async () => {
|
||||
const fileInput = document.createElement('input');
|
||||
@ -66,22 +66,21 @@ export const handleImportClick = async () => {
|
||||
};
|
||||
|
||||
export const Save = ({ isDesktop, disableWidth, myName }) => {
|
||||
const [pinnedApps, setPinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
||||
const [settingsQdnLastUpdated, setSettingsQdnLastUpdated] = useRecoilState(
|
||||
const [pinnedApps, setPinnedApps] = useAtom(sortablePinnedAppsAtom);
|
||||
const [settingsQdnLastUpdated, setSettingsQdnLastUpdated] = useAtom(
|
||||
settingsQDNLastUpdatedAtom
|
||||
);
|
||||
const [settingsLocalLastUpdated] = useRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
const [settingsLocalLastUpdated] = useAtom(settingsLocalLastUpdatedAtom);
|
||||
const setHasSettingsChangedAtom = useSetAtom(hasSettingsChangedAtom);
|
||||
const [isUsingImportExportSettings, setIsUsingImportExportSettings] = useAtom(
|
||||
isUsingImportExportSettingsAtom
|
||||
);
|
||||
const setHasSettingsChangedAtom = useSetRecoilState(hasSettingsChangedAtom);
|
||||
const [isUsingImportExportSettings, setIsUsingImportExportSettings] =
|
||||
useRecoilState(isUsingImportExportSettingsAtom);
|
||||
|
||||
const [canSave] = useRecoilState(canSaveSettingToQdnAtom);
|
||||
const [openSnack, setOpenSnack] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [infoSnack, setInfoSnack] = useState(null);
|
||||
const [oldPinnedApps, setOldPinnedApps] = useRecoilState(oldPinnedAppsAtom);
|
||||
const [oldPinnedApps, setOldPinnedApps] = useAtom(oldPinnedAppsAtom);
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const { show } = useContext(MyContext);
|
||||
const theme = useTheme();
|
||||
|
@ -2,9 +2,9 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { getBaseApiReact } from '../App';
|
||||
import { getData, storeData } from '../utils/chromeStorage';
|
||||
import { checkDifference, getNameInfoForOthers } from '../background';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { lastPaymentSeenTimestampAtom } from '../atoms/global';
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
export const useHandlePaymentNotification = (address) => {
|
||||
const [latestTx, setLatestTx] = useState(null);
|
||||
@ -12,8 +12,9 @@ export const useHandlePaymentNotification = (address) => {
|
||||
const nameAddressOfSender = useRef({});
|
||||
const isFetchingName = useRef({});
|
||||
|
||||
const [lastEnteredTimestampPayment, setLastEnteredTimestampPayment] =
|
||||
useRecoilState(lastPaymentSeenTimestampAtom);
|
||||
const [lastEnteredTimestampPayment, setLastEnteredTimestampPayment] = useAtom(
|
||||
lastPaymentSeenTimestampAtom
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (lastEnteredTimestampPayment && address) {
|
||||
|
@ -1,21 +1,18 @@
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import '../src/styles/index.css';
|
||||
import './messaging/messagesToBackground';
|
||||
import { MessageQueueProvider } from './MessageQueueContext.tsx';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { ThemeProvider } from './components/Theme/ThemeContext.tsx';
|
||||
import { CssBaseline } from '@mui/material';
|
||||
import '../i18n';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<>
|
||||
<ThemeProvider>
|
||||
<CssBaseline />
|
||||
<MessageQueueProvider>
|
||||
<RecoilRoot>
|
||||
<App />
|
||||
</RecoilRoot>
|
||||
</MessageQueueProvider>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import {
|
||||
canSaveSettingToQdnAtom,
|
||||
isUsingImportExportSettingsAtom,
|
||||
@ -14,6 +13,7 @@ import {
|
||||
base64ToUint8Array,
|
||||
uint8ArrayToObject,
|
||||
} from './backgroundFunctions/encryption';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
|
||||
function fetchFromLocalStorage(key) {
|
||||
try {
|
||||
@ -67,19 +67,15 @@ const getPublish = async (myName) => {
|
||||
};
|
||||
|
||||
export const useQortalGetSaveSettings = (myName, isAuthenticated) => {
|
||||
const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom);
|
||||
const setCanSave = useSetRecoilState(canSaveSettingToQdnAtom);
|
||||
const setSettingsQDNLastUpdated = useSetRecoilState(
|
||||
settingsQDNLastUpdatedAtom
|
||||
);
|
||||
const [settingsLocalLastUpdated] = useRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
const [isUsingImportExportSettings] = useRecoilState(
|
||||
const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom);
|
||||
const setCanSave = useSetAtom(canSaveSettingToQdnAtom);
|
||||
const setSettingsQDNLastUpdated = useSetAtom(settingsQDNLastUpdatedAtom);
|
||||
|
||||
const [settingsLocalLastUpdated] = useAtom(settingsLocalLastUpdatedAtom);
|
||||
const [isUsingImportExportSettings] = useAtom(
|
||||
isUsingImportExportSettingsAtom
|
||||
);
|
||||
|
||||
const [oldPinnedApps, setOldPinnedApps] = useRecoilState(oldPinnedAppsAtom);
|
||||
const setOldPinnedApps = useSetAtom(oldPinnedAppsAtom);
|
||||
|
||||
const getSavedSettings = useCallback(
|
||||
async (myName, settingsLocalLastUpdated) => {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import {
|
||||
isUsingImportExportSettingsAtom,
|
||||
oldPinnedAppsAtom,
|
||||
@ -7,6 +6,7 @@ import {
|
||||
settingsQDNLastUpdatedAtom,
|
||||
sortablePinnedAppsAtom,
|
||||
} from './atoms/global';
|
||||
import { useSetAtom } from 'jotai';
|
||||
|
||||
function fetchFromLocalStorage(key) {
|
||||
try {
|
||||
@ -22,21 +22,13 @@ function fetchFromLocalStorage(key) {
|
||||
}
|
||||
|
||||
export const useRetrieveDataLocalStorage = (address) => {
|
||||
const setSortablePinnedApps = useSetRecoilState(sortablePinnedAppsAtom);
|
||||
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
|
||||
const setIsUsingImportExportSettings = useSetRecoilState(
|
||||
const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom);
|
||||
const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom);
|
||||
const setIsUsingImportExportSettings = useSetAtom(
|
||||
isUsingImportExportSettingsAtom
|
||||
);
|
||||
|
||||
const setSettingsQDNLastUpdated = useSetRecoilState(
|
||||
settingsQDNLastUpdatedAtom
|
||||
);
|
||||
|
||||
const setOldPinnedApps = useSetRecoilState(oldPinnedAppsAtom);
|
||||
const setSettingsQDNLastUpdated = useSetAtom(settingsQDNLastUpdatedAtom);
|
||||
const setOldPinnedApps = useSetAtom(oldPinnedAppsAtom);
|
||||
|
||||
const getSortablePinnedApps = useCallback(() => {
|
||||
const pinnedAppsLocal = fetchFromLocalStorage('ext_saved_settings');
|
||||
|
Loading…
x
Reference in New Issue
Block a user