Qortal-Hub/src/hooks/useHandlePrivateApps.tsx
2025-05-23 07:30:12 +02:00

276 lines
8.2 KiB
TypeScript

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 '../components/Apps/AppsNavBarDesktop';
import { base64ToUint8Array } from '../qdn/encryption/group-encryption';
import { uint8ArrayToObject } from '../backgroundFunctions/encryption';
import { useSetAtom } from 'jotai';
import { useTranslation } from 'react-i18next';
export const useHandlePrivateApps = () => {
const [status, setStatus] = useState('');
const {
openSnackGlobal,
setOpenSnackGlobal,
infoSnackCustom,
setInfoSnackCustom,
} = useContext(MyContext);
const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom);
const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom);
const { t } = useTranslation(['auth', 'core', 'group']);
const openApp = async (
privateAppProperties,
addToPinnedApps,
setLoadingStatePrivateApp
) => {
try {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp(
t('core:message.generic.downloading_decrypting_app', {
postProcess: 'capitalizeFirstChar',
})
);
}
setOpenSnackGlobal(true);
setInfoSnackCustom({
type: 'info',
message: t('core:message.generic.fetching_data', {
postProcess: 'capitalizeFirstChar',
}),
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(
t('core:message.generic.unable_download_private_app', {
postProcess: 'capitalizeFirstChar',
})
);
}
throw new Error(
t('core:message.error.fetch_app', {
postProcess: 'capitalizeFirstChar',
})
);
}
data = await responseData.text();
if (data?.error) {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp(
t('core:message.generic.unable_download_private_app', {
postProcess: 'capitalizeFirstChar',
})
);
}
throw new Error(
t('core:message.generic.unable_fetch_app', {
postProcess: 'capitalizeFirstChar',
})
);
}
} catch (error) {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp(
t('core:message.generic.unable_download_private_app', {
postProcess: 'capitalizeFirstChar',
})
);
}
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(
t('core:message.generic.unable_decrypt_app', {
postProcess: 'capitalizeFirstChar',
})
);
}
throw new Error(decryptedData?.error);
}
} catch (error) {
if (setLoadingStatePrivateApp) {
setLoadingStatePrivateApp(
t('core:message.generic.unable_decrypt_app', {
postProcess: 'capitalizeFirstChar',
})
);
}
throw error;
}
try {
const convertToUint = base64ToUint8Array(decryptedData);
const UintToObject = uint8ArrayToObject(convertToUint);
if (decryptedData) {
setInfoSnackCustom({
type: 'info',
message: t('core:message.generic.building_app', {
postProcess: 'capitalizeFirstChar',
}),
});
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: t('core:message.generic.opened', {
postProcess: 'capitalizeFirstChar',
}),
});
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 ||
t('core:message.error.build_app', {
postProcess: 'capitalizeFirstChar',
})
}`
);
}
throw error;
}
} catch (error) {
setInfoSnackCustom({
type: 'error',
message:
error?.message ||
t('core:message.error.fetch_app', {
postProcess: 'capitalizeFirstChar',
}),
});
}
};
return {
openApp,
status,
};
};