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