Move all hooks into src/hooks/ folder

This commit is contained in:
Nicola Benaglia
2025-05-18 19:22:27 +02:00
parent 10bbf420c3
commit 7a076a9f1f
15 changed files with 30 additions and 30 deletions

View File

@@ -3,7 +3,7 @@ import { Box } from '@mui/material';
import { getBaseApiReact } from '../../App';
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
import { useFrame } from 'react-frame-component';
import { useQortalMessageListener } from './useQortalMessageListener';
import { useQortalMessageListener } from '../../hooks/useQortalMessageListener';
import { useThemeContext } from '../Theme/ThemeContext';
import { useTranslation } from 'react-i18next';

View File

@@ -14,7 +14,7 @@ import {
useTheme,
} from '@mui/material';
import { useDropzone } from 'react-dropzone';
import { useHandlePrivateApps } from './useHandlePrivateApps';
import { useHandlePrivateApps } from '../../hooks/useHandlePrivateApps';
import {
groupsPropertiesAtom,
memberGroupsAtom,

View File

@@ -25,7 +25,7 @@ import {
import { saveToLocalStorage } from './AppsNavBarDesktop';
import { ContextMenuPinnedApps } from '../ContextMenuPinnedApps';
import LockIcon from '@mui/icons-material/Lock';
import { useHandlePrivateApps } from './useHandlePrivateApps';
import { useHandlePrivateApps } from '../../hooks/useHandlePrivateApps';
import { useAtom, useSetAtom } from 'jotai';
const SortableItem = ({ id, name, app, isDesktop }) => {

View File

@@ -1,225 +0,0 @@
import { useContext, useState } from 'react';
import { executeEvent } from '../../utils/events';
import { getBaseApiReact, MyContext } from '../../App';
import { createEndpoint } from '../../background';
import {
settingsLocalLastUpdatedAtom,
sortablePinnedAppsAtom,
} from '../../atoms/global';
import { saveToLocalStorage } from './AppsNavBarDesktop';
import { base64ToUint8Array } from '../../qdn/encryption/group-encryption';
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
import { useSetAtom } from 'jotai';
export const useHandlePrivateApps = () => {
const [status, setStatus] = useState('');
const {
openSnackGlobal,
setOpenSnackGlobal,
infoSnackCustom,
setInfoSnackCustom,
} = useContext(MyContext);
const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom);
const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom);
const openApp = async (
privateAppProperties,
addToPinnedApps,
setLoadingStatePrivateApp
) => {
try {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp(`Downloading and decrypting private app.`);
}
setOpenSnackGlobal(true);
setInfoSnackCustom({
type: 'info',
message: 'Fetching app data',
duration: null,
});
const urlData = `${getBaseApiReact()}/arbitrary/${
privateAppProperties?.service
}/${privateAppProperties?.name}/${
privateAppProperties?.identifier
}?encoding=base64`;
let data;
try {
const responseData = await fetch(urlData, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!responseData?.ok) {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp('Error! Unable to download private app.');
}
throw new Error('Unable to fetch app');
}
data = await responseData.text();
if (data?.error) {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp('Error! Unable to download private app.');
}
throw new Error('Unable to fetch app');
}
} catch (error) {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp('Error! Unable to download private app.');
}
throw error;
}
let decryptedData;
// eslint-disable-next-line no-useless-catch
try {
decryptedData = await window.sendMessage(
'DECRYPT_QORTAL_GROUP_DATA',
{
base64: data,
groupId: privateAppProperties?.groupId,
}
);
if (decryptedData?.error) {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp('Error! Unable to decrypt private app.');
}
throw new Error(decryptedData?.error);
}
} catch (error) {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp('Error! Unable to decrypt private app.');
}
throw error;
}
try {
const convertToUint = base64ToUint8Array(decryptedData);
const UintToObject = uint8ArrayToObject(convertToUint);
if (decryptedData) {
setInfoSnackCustom({
type: 'info',
message: 'Building app',
});
const endpoint = await createEndpoint(
`/arbitrary/APP/${privateAppProperties?.name}/zip?preview=true`
);
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: UintToObject?.app,
});
const previewPath = await response.text();
const refreshfunc = async (tabId, privateAppProperties) => {
const checkIfPreviewLinkStillWorksUrl = await createEndpoint(
`/render/hash/HmtnZpcRPwisMfprUXuBp27N2xtv5cDiQjqGZo8tbZS?secret=E39WTiG4qBq3MFcMPeRZabtQuzyfHg9ZuR5SgY7nW1YH`
);
const res = await fetch(checkIfPreviewLinkStillWorksUrl);
if (res.ok) {
executeEvent('refreshApp', {
tabId: tabId,
});
} else {
const endpoint = await createEndpoint(
`/arbitrary/APP/${privateAppProperties?.name}/zip?preview=true`
);
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
body: UintToObject?.app,
});
const previewPath = await response.text();
executeEvent('updateAppUrl', {
tabId: tabId,
url: await createEndpoint(previewPath),
});
setTimeout(() => {
executeEvent('refreshApp', {
tabId: tabId,
});
}, 300);
}
};
const appName = UintToObject?.name;
const logo = UintToObject?.logo
? `data:image/png;base64,${UintToObject?.logo}`
: null;
const dataBody = {
url: await createEndpoint(previewPath),
isPreview: true,
isPrivate: true,
privateAppProperties: { ...privateAppProperties, logo, appName },
filePath: '',
refreshFunc: (tabId) => {
refreshfunc(tabId, privateAppProperties);
},
};
executeEvent('addTab', {
data: dataBody,
});
setInfoSnackCustom({
type: 'success',
message: 'Opened',
});
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp(``);
}
if (addToPinnedApps) {
setSortablePinnedApps((prev) => {
const updatedApps = [
...prev,
{
isPrivate: true,
isPreview: true,
privateAppProperties: {
...privateAppProperties,
logo,
appName,
},
},
];
saveToLocalStorage(
'ext_saved_settings',
'sortablePinnedApps',
updatedApps
);
return updatedApps;
});
setSettingsLocalLastUpdated(Date.now());
}
}
} catch (error) {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp(
`Error! ${error?.message || 'Unable to build private app.'}`
);
}
throw error;
}
} catch (error) {
setInfoSnackCustom({
type: 'error',
message: error?.message || 'Unable to fetch app',
});
}
};
return {
openApp,
status,
};
};

View File

@@ -1,729 +0,0 @@
import { useCallback, useContext, useEffect, useState } from 'react';
import { executeEvent } from '../../utils/events';
import { navigationControllerAtom } from '../../atoms/global';
import { Filesystem, Directory } from '@capacitor/filesystem';
import { saveFile } from '../../qortalRequests/get';
import { mimeToExtensionMap } from '../../utils/memeTypes';
import { MyContext } from '../../App';
import FileSaver from 'file-saver';
import { useSetAtom } from 'jotai';
export const saveFileInChunks = async (
blob: Blob,
fileName: string,
chunkSize = 1024 * 1024
) => {
try {
let offset = 0;
let isFirstChunk = true;
// Extract the MIME type from the blob
const mimeType = blob.type || 'application/octet-stream';
// Create the dynamic base64 prefix
const base64Prefix = `data:${mimeType};base64,`;
// Function to extract extension from fileName
const getExtensionFromFileName = (name: string): string => {
const lastDotIndex = name.lastIndexOf('.');
if (lastDotIndex !== -1) {
return name.substring(lastDotIndex); // includes the dot
}
return '';
};
// Extract existing extension from fileName
const existingExtension = getExtensionFromFileName(fileName);
// Remove existing extension from fileName to avoid duplication
if (existingExtension) {
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
}
// Map MIME type to file extension
const mimeTypeToExtension = (mimeType: string): string => {
return mimeToExtensionMap[mimeType] || existingExtension || ''; // Use existing extension if MIME type not found
};
// Determine the final extension to use
const extension = mimeTypeToExtension(mimeType);
// Construct the full file name with timestamp and extension
const fullFileName = `${fileName}_${Date.now()}${extension}`;
// Read the blob in chunks
while (offset < blob.size) {
// Extract the current chunk
const chunk = blob.slice(offset, offset + chunkSize);
// Convert the chunk to Base64
const base64Chunk = await blobToBase64(chunk);
// Write the chunk to the file with the prefix added on the first chunk
await Filesystem.writeFile({
path: fullFileName,
data: isFirstChunk ? base64Prefix + base64Chunk : base64Chunk,
directory: Directory.Documents,
recursive: true,
append: !isFirstChunk, // Append after the first chunk
});
// Update offset and flag
offset += chunkSize;
isFirstChunk = false;
}
} catch (error) {
console.error('Error saving file in chunks:', error);
}
};
// Helper function to convert a Blob to a Base64 string
const blobToBase64 = (blob: Blob): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
const base64data = reader.result?.toString().split(',')[1];
resolve(base64data || '');
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
};
class Semaphore {
constructor(count) {
this.count = count;
this.waiting = [];
}
acquire() {
return new Promise((resolve) => {
if (this.count > 0) {
this.count--;
resolve();
} else {
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');
};
});
}
export const listOfAllQortalRequests = [
'GET_USER_ACCOUNT',
'DECRYPT_DATA',
'SEND_COIN',
'GET_LIST_ITEMS',
'ADD_LIST_ITEMS',
'DELETE_LIST_ITEM',
'VOTE_ON_POLL',
'CREATE_POLL',
'SEND_CHAT_MESSAGE',
'JOIN_GROUP',
'DEPLOY_AT',
'GET_USER_WALLET',
'GET_WALLET_BALANCE',
'GET_USER_WALLET_INFO',
'GET_CROSSCHAIN_SERVER_INFO',
'GET_TX_ACTIVITY_SUMMARY',
'GET_FOREIGN_FEE',
'UPDATE_FOREIGN_FEE',
'GET_SERVER_CONNECTION_HISTORY',
'SET_CURRENT_FOREIGN_SERVER',
'ADD_FOREIGN_SERVER',
'REMOVE_FOREIGN_SERVER',
'GET_DAY_SUMMARY',
'CREATE_TRADE_BUY_ORDER',
'CREATE_TRADE_SELL_ORDER',
'CANCEL_TRADE_SELL_ORDER',
'IS_USING_PUBLIC_NODE',
'ADMIN_ACTION',
'SIGN_TRANSACTION',
'OPEN_NEW_TAB',
'CREATE_AND_COPY_EMBED_LINK',
'DECRYPT_QORTAL_GROUP_DATA',
'DECRYPT_DATA_WITH_SHARING_KEY',
'DELETE_HOSTED_DATA',
'GET_HOSTED_DATA',
'PUBLISH_MULTIPLE_QDN_RESOURCES',
'PUBLISH_QDN_RESOURCE',
'ENCRYPT_DATA',
'ENCRYPT_DATA_WITH_SHARING_KEY',
'ENCRYPT_QORTAL_GROUP_DATA',
'SAVE_FILE',
'GET_ACCOUNT_DATA',
'GET_ACCOUNT_NAMES',
'SEARCH_NAMES',
'GET_NAME_DATA',
'GET_QDN_RESOURCE_URL',
'LINK_TO_QDN_RESOURCE',
'LIST_QDN_RESOURCES',
'SEARCH_QDN_RESOURCES',
'FETCH_QDN_RESOURCE',
'GET_QDN_RESOURCE_STATUS',
'GET_QDN_RESOURCE_PROPERTIES',
'GET_QDN_RESOURCE_METADATA',
'SEARCH_CHAT_MESSAGES',
'LIST_GROUPS',
'GET_BALANCE',
'GET_AT',
'GET_AT_DATA',
'LIST_ATS',
'FETCH_BLOCK',
'FETCH_BLOCK_RANGE',
'SEARCH_TRANSACTIONS',
'GET_PRICE',
'SHOW_ACTIONS',
'REGISTER_NAME',
'UPDATE_NAME',
'LEAVE_GROUP',
'INVITE_TO_GROUP',
'KICK_FROM_GROUP',
'BAN_FROM_GROUP',
'CANCEL_GROUP_BAN',
'ADD_GROUP_ADMIN',
'REMOVE_GROUP_ADMIN',
'DECRYPT_AESGCM',
'CANCEL_GROUP_INVITE',
'CREATE_GROUP',
'GET_USER_WALLET_TRANSACTIONS',
'GET_NODE_INFO',
'GET_NODE_STATUS',
'GET_ARRR_SYNC_STATUS',
'SHOW_PDF_READER',
'UPDATE_GROUP',
'SELL_NAME',
'CANCEL_SELL_NAME',
'BUY_NAME',
'SIGN_FOREIGN_FEES',
'MULTI_ASSET_PAYMENT_WITH_PRIVATE_DATA',
'TRANSFER_ASSET',
];
export const UIQortalRequests = [
'GET_USER_ACCOUNT',
'DECRYPT_DATA',
'SEND_COIN',
'GET_LIST_ITEMS',
'ADD_LIST_ITEMS',
'DELETE_LIST_ITEM',
'VOTE_ON_POLL',
'CREATE_POLL',
'SEND_CHAT_MESSAGE',
'JOIN_GROUP',
'DEPLOY_AT',
'GET_USER_WALLET',
'GET_WALLET_BALANCE',
'GET_USER_WALLET_INFO',
'GET_CROSSCHAIN_SERVER_INFO',
'GET_TX_ACTIVITY_SUMMARY',
'GET_FOREIGN_FEE',
'UPDATE_FOREIGN_FEE',
'GET_SERVER_CONNECTION_HISTORY',
'SET_CURRENT_FOREIGN_SERVER',
'ADD_FOREIGN_SERVER',
'REMOVE_FOREIGN_SERVER',
'GET_DAY_SUMMARY',
'CREATE_TRADE_BUY_ORDER',
'CREATE_TRADE_SELL_ORDER',
'CANCEL_TRADE_SELL_ORDER',
'IS_USING_PUBLIC_NODE',
'ADMIN_ACTION',
'SIGN_TRANSACTION',
'OPEN_NEW_TAB',
'CREATE_AND_COPY_EMBED_LINK',
'DECRYPT_QORTAL_GROUP_DATA',
'DECRYPT_DATA_WITH_SHARING_KEY',
'DELETE_HOSTED_DATA',
'GET_HOSTED_DATA',
'SHOW_ACTIONS',
'REGISTER_NAME',
'UPDATE_NAME',
'LEAVE_GROUP',
'INVITE_TO_GROUP',
'KICK_FROM_GROUP',
'BAN_FROM_GROUP',
'CANCEL_GROUP_BAN',
'ADD_GROUP_ADMIN',
'REMOVE_GROUP_ADMIN',
'DECRYPT_AESGCM',
'CANCEL_GROUP_INVITE',
'CREATE_GROUP',
'GET_USER_WALLET_TRANSACTIONS',
'GET_NODE_INFO',
'GET_NODE_STATUS',
'GET_ARRR_SYNC_STATUS',
'SHOW_PDF_READER',
'UPDATE_GROUP',
'SELL_NAME',
'CANCEL_SELL_NAME',
'BUY_NAME',
'SIGN_FOREIGN_FEES',
'MULTI_ASSET_PAYMENT_WITH_PRIVATE_DATA',
'TRANSFER_ASSET',
];
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) {
// 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 transaction = db.transaction(['files'], 'readwrite');
const objectStore = transaction.objectStore('files');
// Create a request to get all keys
const getAllKeysRequest = objectStore.getAllKeys();
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',
message: 'Saving file...',
});
setOpenSnackGlobal(true);
FileSaver.saveAs(blob, filename);
setInfoSnackCustom({
type: 'success',
message: 'Saving file success!',
});
setOpenSnackGlobal(true);
} 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');
};
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({
customQDNHistoryPaths: [],
currentIndex: -1,
isDOMContentLoaded: false,
});
const setHasSettingsChangedAtom = useSetAtom(navigationControllerAtom);
const {
openSnackGlobal,
setOpenSnackGlobal,
infoSnackCustom,
setInfoSnackCustom,
} = useContext(MyContext);
useEffect(() => {
if (tabId && !isNaN(history?.currentIndex)) {
setHasSettingsChangedAtom((prev) => {
return {
...prev,
[tabId]: {
hasBack: history?.currentIndex > 0,
},
};
});
}
}, [history?.currentIndex, tabId]);
const changeCurrentIndex = useCallback((value) => {
setHistory((prev) => {
return {
...prev,
currentIndex: value,
};
});
}, []);
const resetHistory = useCallback(() => {
setHistory({
customQDNHistoryPaths: [],
currentIndex: -1,
isManualNavigation: true,
isDOMContentLoaded: false,
});
}, []);
useEffect(() => {
const listener = async (event) => {
if (event?.data?.requestedHandler !== 'UI') return;
const sendMessageToRuntime = (message, eventPort) => {
window
.sendMessage(
message.action,
message.payload,
300000,
message.isExtension,
{
name: appName,
service: appService,
},
skipAuth
)
.then((response) => {
if (response.error) {
eventPort.postMessage({
result: null,
error: {
error: response?.error,
message:
typeof response?.error === 'string'
? response?.error
: typeof response?.message === 'string'
? response?.message
: 'An error has occurred',
},
});
} 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
if (UIQortalRequests.includes(event.data.action)) {
sendMessageToRuntime(
{
action: event.data.action,
type: 'qortalRequest',
payload: event.data,
isExtension: true,
},
event.ports[0]
);
} else if (event?.data?.action === 'SAVE_FILE') {
try {
const res = await saveFile(event.data, null, true, {
openSnackGlobal,
setOpenSnackGlobal,
infoSnackCustom,
setInfoSnackCustom,
});
} catch (error) {}
} else if (
event?.data?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' ||
event?.data?.action === 'PUBLISH_QDN_RESOURCE' ||
event?.data?.action === 'ENCRYPT_DATA' ||
event?.data?.action === 'ENCRYPT_DATA_WITH_SHARING_KEY' ||
event?.data?.action === 'ENCRYPT_QORTAL_GROUP_DATA'
) {
const data = event.data;
if (data) {
sendMessageToRuntime(
{
action: event.data.action,
type: 'qortalRequest',
payload: data,
isExtension: true,
},
event.ports[0]
);
} else {
event.ports[0].postMessage({
result: null,
error: 'Failed to prepare data for publishing',
});
}
} else if (
event?.data?.action === 'LINK_TO_QDN_RESOURCE' ||
event?.data?.action === 'QDN_RESOURCE_DISPLAYED'
) {
const pathUrl =
event?.data?.path != null
? (event?.data?.path.startsWith('/') ? '' : '/') + event?.data?.path
: null;
setPath(pathUrl);
if (appName?.toLowerCase() === 'q-mail') {
window.sendMessage('addEnteredQmailTimestamp').catch((error) => {
// error
});
} else if (appName?.toLowerCase() === 'q-wallets') {
executeEvent('setLastEnteredTimestampPaymentEvent', {});
}
} else if (event?.data?.action === 'NAVIGATION_HISTORY') {
if (event?.data?.payload?.isDOMContentLoaded) {
setHistory((prev) => {
const copyPrev = { ...prev };
if (
(copyPrev?.customQDNHistoryPaths || []).at(-1) ===
(event?.data?.payload?.customQDNHistoryPaths || []).at(-1)
) {
return {
...prev,
currentIndex:
prev.customQDNHistoryPaths.length - 1 === -1
? 0
: prev.customQDNHistoryPaths.length - 1,
};
}
const copyHistory = { ...prev };
const paths = [
...(copyHistory?.customQDNHistoryPaths.slice(
0,
copyHistory.currentIndex + 1
) || []),
...(event?.data?.payload?.customQDNHistoryPaths || []),
];
return {
...prev,
customQDNHistoryPaths: paths,
currentIndex: paths.length - 1,
};
});
} else {
setHistory(event?.data?.payload);
}
} else if (event?.data?.action === 'SET_TAB' && !isDevMode) {
executeEvent('addTab', {
data: event?.data?.payload,
});
const targetOrigin = iframeRef.current
? new URL(iframeRef.current.src).origin
: '*';
iframeRef.current.contentWindow.postMessage(
{
action: 'SET_TAB_SUCCESS',
requestedHandler: 'UI',
payload: {
name: event?.data?.payload?.name,
},
},
targetOrigin
);
}
};
// Add the listener for messages coming from the frameWindow
frameWindow.addEventListener('message', listener);
// Cleanup function to remove the event listener when the component is unmounted
return () => {
frameWindow.removeEventListener('message', listener);
};
}, [isDevMode, appName, appService]); // Empty dependency array to run once when the component mounts
return { path, history, resetHistory, changeCurrentIndex };
};

View File

@@ -1,204 +0,0 @@
import { useCallback, useEffect, useMemo, useRef } from 'react';
export const useBlockedAddresses = () => {
const userBlockedRef = useRef({});
const userNamesBlockedRef = useRef({});
const getAllBlockedUsers = useCallback(() => {
return {
names: userNamesBlockedRef.current,
addresses: userBlockedRef.current,
};
}, []);
const isUserBlocked = useCallback((address, name) => {
try {
if (!address) return false;
if (userBlockedRef.current[address]) return true;
return false;
} catch (error) {
console.log(error);
}
}, []);
useEffect(() => {
const fetchBlockedList = async () => {
try {
const response = await new Promise((res, rej) => {
window
.sendMessage('listActions', {
type: 'get',
listName: `blockedAddresses`,
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
const blockedUsers = {};
response?.forEach((item) => {
blockedUsers[item] = true;
});
userBlockedRef.current = blockedUsers;
const response2 = await new Promise((res, rej) => {
window
.sendMessage('listActions', {
type: 'get',
listName: `blockedNames`,
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
const blockedUsers2 = {};
response2?.forEach((item) => {
blockedUsers2[item] = true;
});
userNamesBlockedRef.current = blockedUsers2;
} catch (error) {
console.error(error);
}
};
fetchBlockedList();
}, []);
const removeBlockFromList = useCallback(async (address, name) => {
if (name) {
await new Promise((res, rej) => {
window
.sendMessage('listActions', {
type: 'remove',
items: [name],
listName: 'blockedNames',
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = { ...userNamesBlockedRef.current };
delete copyObject[name];
userNamesBlockedRef.current = copyObject;
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
}
if (address) {
await new Promise((res, rej) => {
window
.sendMessage('listActions', {
type: 'remove',
items: [address],
listName: 'blockedAddresses',
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = { ...userBlockedRef.current };
delete copyObject[address];
userBlockedRef.current = copyObject;
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
}
}, []);
const addToBlockList = useCallback(async (address, name) => {
if (name) {
await new Promise((res, rej) => {
window
.sendMessage('listActions', {
type: 'add',
items: [name],
listName: 'blockedNames',
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = { ...userNamesBlockedRef.current };
copyObject[name] = true;
userNamesBlockedRef.current = copyObject;
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
}
if (address) {
await new Promise((res, rej) => {
window
.sendMessage('listActions', {
type: 'add',
items: [address],
listName: 'blockedAddresses',
})
.then((response) => {
if (response.error) {
rej(response?.message);
return;
} else {
const copyObject = { ...userBlockedRef.current };
copyObject[address] = true;
userBlockedRef.current = copyObject;
res(response);
}
})
.catch((error) => {
console.error('Failed qortalRequest', error);
});
});
}
}, []);
return useMemo(
() => ({
isUserBlocked,
addToBlockList,
removeBlockFromList,
getAllBlockedUsers,
}),
[isUserBlocked, addToBlockList, removeBlockFromList, getAllBlockedUsers]
);
};

View File

@@ -1,30 +0,0 @@
import { useCallback, useRef } from 'react';
import { getBaseApiReact } from '../../App';
export const useHandleUserInfo = () => {
const userInfoRef = useRef({});
const getIndividualUserInfo = useCallback(async (address) => {
try {
if (!address) return null;
if (userInfoRef.current[address] !== undefined)
return userInfoRef.current[address];
const url = `${getBaseApiReact()}/addresses/${address}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error('network error');
}
const data = await response.json();
userInfoRef.current = {
...userInfoRef.current,
[address]: data?.level,
};
return data?.level;
} catch (error) {
console.log(error);
}
}, []);
return getIndividualUserInfo;
};

View File

@@ -1,212 +0,0 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { saveToLocalStorage } from '../Apps/AppsNavBarDesktop';
import creationImg from './img/creation.webp';
import dashboardImg from './img/dashboard.webp';
import groupsImg from './img/groups.webp';
import importantImg from './img/important.webp';
import navigationImg from './img/navigation.webp';
import overviewImg from './img/overview.webp';
import startedImg from './img/started.webp';
import obtainingImg from './img/obtaining-qort.jpg';
import { useTranslation } from 'react-i18next';
const checkIfGatewayIsOnline = async () => {
try {
const url = `https://ext-node.qortal.link/admin/status`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
const data = await response.json();
if (data?.height) {
return true;
}
return false;
} catch (error) {
return false;
}
};
export const useHandleTutorials = () => {
const [openTutorialModal, setOpenTutorialModal] = useState<any>(null);
const [shownTutorials, setShowTutorials] = useState(null);
const { t } = useTranslation(['core', 'tutorial']);
useEffect(() => {
try {
const storedData = localStorage.getItem('shown-tutorials');
if (storedData) {
setShowTutorials(JSON.parse(storedData));
} else {
setShowTutorials({});
}
} catch (error) {
//error
}
}, []);
const saveShowTutorial = useCallback((type) => {
try {
setShowTutorials((prev) => {
return {
...(prev || {}),
[type]: true,
};
});
saveToLocalStorage('shown-tutorials', type, true);
} catch (error) {
//error
}
}, []);
const showTutorial = useCallback(
async (type, isForce) => {
try {
const isOnline = await checkIfGatewayIsOnline();
if (!isOnline) return;
switch (type) {
case 'create-account':
{
if ((shownTutorials || {})['create-account'] && !isForce) return;
saveShowTutorial('create-account');
setOpenTutorialModal({
title: 'Account Creation',
resource: {
name: 'a-test',
service: 'VIDEO',
identifier: 'account-creation-hub',
poster: creationImg,
},
});
}
break;
case 'important-information':
{
if ((shownTutorials || {})['important-information'] && !isForce)
return;
saveShowTutorial('important-information');
setOpenTutorialModal({
title: 'Important Information!',
resource: {
name: 'a-test',
service: 'VIDEO',
identifier: 'important-information-hub',
poster: importantImg,
},
});
}
break;
case 'getting-started':
{
if ((shownTutorials || {})['getting-started'] && !isForce) return;
saveShowTutorial('getting-started');
setOpenTutorialModal({
multi: [
{
title: t('tutorial:1_getting_started', {
postProcess: 'capitalizeFirst',
}),
resource: {
name: 'a-test',
service: 'VIDEO',
identifier: 'getting-started-hub',
poster: startedImg,
},
},
{
title: t('tutorial:2_overview', {
postProcess: 'capitalizeFirst',
}),
resource: {
name: 'a-test',
service: 'VIDEO',
identifier: 'overview-hub',
poster: overviewImg,
},
},
{
title: t('tutorial:3_groups', {
postProcess: 'capitalizeFirst',
}),
resource: {
name: 'a-test',
service: 'VIDEO',
identifier: 'groups-hub',
poster: groupsImg,
},
},
{
title: t('tutorial:4_obtain_qort', {
postProcess: 'capitalizeFirst',
}),
resource: {
name: 'a-test',
service: 'VIDEO',
identifier: 'obtaining-qort',
poster: obtainingImg,
},
},
],
});
}
break;
case 'qapps':
{
if ((shownTutorials || {})['qapps'] && !isForce) return;
saveShowTutorial('qapps');
setOpenTutorialModal({
multi: [
{
title: t('tutorial:apps.dashboard', {
postProcess: 'capitalizeFirst',
}),
resource: {
name: 'a-test',
service: 'VIDEO',
identifier: 'apps-dashboard-hub',
poster: dashboardImg,
},
},
{
title: t('tutorial:apps.navigation', {
postProcess: 'capitalizeFirst',
}),
resource: {
name: 'a-test',
service: 'VIDEO',
identifier: 'apps-navigation-hub',
poster: navigationImg,
},
},
],
});
}
break;
default:
break;
}
} catch (error) {
//error
}
},
[shownTutorials]
);
return useMemo(
() => ({
showTutorial,
hasSeenGettingStarted:
shownTutorials === null
? null
: !!(shownTutorials || {})['getting-started'],
openTutorialModal,
setOpenTutorialModal,
shownTutorialsInitiated: !!shownTutorials,
}),
[showTutorial, openTutorialModal, setOpenTutorialModal, shownTutorials]
);
};