mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-06 16:36:58 +00:00
Add chat translations
This commit is contained in:
parent
af65881784
commit
30b5d1e36b
@ -1,7 +1,7 @@
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { MyContext } from '../../App';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { AdminSpaceInner } from './AdminSpaceInner';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const AdminSpace = ({
|
||||
selectedGroup,
|
||||
@ -19,6 +19,8 @@ export const AdminSpace = ({
|
||||
isOwner,
|
||||
}) => {
|
||||
const [isMoved, setIsMoved] = useState(false);
|
||||
const { t } = useTranslation(['core', 'group']);
|
||||
|
||||
useEffect(() => {
|
||||
if (hide) {
|
||||
setTimeout(() => setIsMoved(true), 300); // Wait for the fade-out to complete before moving
|
||||
@ -35,10 +37,10 @@ export const AdminSpace = ({
|
||||
height: 'calc(100vh - 70px)',
|
||||
left: hide && '-1000px',
|
||||
opacity: hide ? 0 : 1,
|
||||
overflow: 'auto',
|
||||
position: hide ? 'fixed' : 'relative',
|
||||
visibility: hide && 'hidden',
|
||||
width: '100%',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{!isAdmin && (
|
||||
@ -50,9 +52,14 @@ export const AdminSpace = ({
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography>Sorry, this space is only for Admins.</Typography>
|
||||
<Typography>
|
||||
{t('core:message.generic.space_for_admins', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{isAdmin && (
|
||||
<AdminSpaceInner
|
||||
setIsForceShowCreationKeyPopup={setIsForceShowCreationKeyPopup}
|
||||
|
@ -16,6 +16,8 @@ import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
||||
import { formatTimestampForum } from '../../utils/time';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { GroupAvatar } from './GroupAvatar';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import i18next from 'i18next';
|
||||
|
||||
export const getPublishesFromAdminsAdminSpace = async (
|
||||
admins: string[],
|
||||
@ -26,7 +28,7 @@ export const getPublishesFromAdminsAdminSpace = async (
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('network error');
|
||||
throw new Error(i18next.t('core:message.error.network_generic'));
|
||||
}
|
||||
const adminData = await response.json();
|
||||
|
||||
@ -72,6 +74,7 @@ export const AdminSpaceInner = ({
|
||||
const [isLoadingPublishKey, setIsLoadingPublishKey] = useState(false);
|
||||
const { show, setInfoSnackCustom, setOpenSnackGlobal } =
|
||||
useContext(MyContext);
|
||||
const { t } = useTranslation(['auth', 'core', 'group']);
|
||||
|
||||
const getAdminGroupSecretKey = useCallback(async () => {
|
||||
try {
|
||||
@ -81,20 +84,24 @@ export const AdminSpaceInner = ({
|
||||
selectedGroup
|
||||
);
|
||||
if (getLatestPublish === false) return;
|
||||
let data;
|
||||
|
||||
const res = await fetch(
|
||||
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${
|
||||
getLatestPublish.name
|
||||
}/${getLatestPublish.identifier}?encoding=base64&rebuild=true`
|
||||
);
|
||||
data = await res.text();
|
||||
|
||||
const data = await res.text();
|
||||
const decryptedKey: any = await decryptResource(data);
|
||||
const dataint8Array = base64ToUint8Array(decryptedKey.data);
|
||||
const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
|
||||
|
||||
if (!validateSecretKey(decryptedKeyToObject))
|
||||
throw new Error('SecretKey is not valid');
|
||||
throw new Error(
|
||||
t('auth:message.error.invalid_secret_key', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})
|
||||
);
|
||||
setAdminGroupSecretKey(decryptedKeyToObject);
|
||||
setAdminGroupSecretKeyPublishDetails(getLatestPublish);
|
||||
} catch (error) {
|
||||
@ -125,7 +132,10 @@ export const AdminSpaceInner = ({
|
||||
const fee = await getFee('ARBITRARY');
|
||||
|
||||
await show({
|
||||
message: 'Would you like to perform an ARBITRARY transaction?',
|
||||
message: t('core:question.perform_transaction', {
|
||||
action: 'ARBITRARY',
|
||||
postProcess: 'capitalizeFirst',
|
||||
}),
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
|
||||
@ -141,22 +151,31 @@ export const AdminSpaceInner = ({
|
||||
if (!response?.error) {
|
||||
setInfoSnackCustom({
|
||||
type: 'success',
|
||||
message:
|
||||
'Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.',
|
||||
message: t('auth:message.success.reencrypted_secret_key', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
}),
|
||||
});
|
||||
setOpenSnackGlobal(true);
|
||||
return;
|
||||
}
|
||||
setInfoSnackCustom({
|
||||
type: 'error',
|
||||
message: response?.error || 'unable to re-encrypt secret key',
|
||||
message:
|
||||
response?.error ||
|
||||
t('auth:message.error.unable_reencrypt_secret_key', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
}),
|
||||
});
|
||||
setOpenSnackGlobal(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnackCustom({
|
||||
type: 'error',
|
||||
message: error?.message || 'unable to re-encrypt secret key',
|
||||
message:
|
||||
error?.message ||
|
||||
t('auth:message.error.unable_reencrypt_secret_key', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
}),
|
||||
});
|
||||
setOpenSnackGlobal(true);
|
||||
});
|
||||
@ -184,10 +203,13 @@ export const AdminSpaceInner = ({
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
Reminder: After publishing the key, it will take a couple of minutes for
|
||||
it to appear. Please just wait.
|
||||
{t('auth:message.error.publishing_key', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
|
||||
<Spacer height="25px" />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
border: '1px solid gray',
|
||||
@ -201,28 +223,43 @@ export const AdminSpaceInner = ({
|
||||
}}
|
||||
>
|
||||
{isFetchingGroupSecretKey && (
|
||||
<Typography>Fetching Group secret key publishes</Typography>
|
||||
)}
|
||||
{!isFetchingGroupSecretKey &&
|
||||
groupSecretKeyPublishDetails === false && (
|
||||
<Typography>No secret key published yet</Typography>
|
||||
)}
|
||||
{groupSecretKeyPublishDetails && (
|
||||
<Typography>
|
||||
Last encryption date:{' '}
|
||||
{formatTimestampForum(
|
||||
groupSecretKeyPublishDetails?.updated ||
|
||||
groupSecretKeyPublishDetails?.created
|
||||
)}{' '}
|
||||
{` by ${groupSecretKeyPublishDetails?.name}`}
|
||||
{t('auth:message.generic.fetching_group_secret_key', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{!isFetchingGroupSecretKey &&
|
||||
groupSecretKeyPublishDetails === false && (
|
||||
<Typography>
|
||||
{t('auth:message.generic.no_secret_key_published', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{groupSecretKeyPublishDetails && (
|
||||
<Typography>
|
||||
{t('auth:message.generic.last_encryption_date', {
|
||||
date: formatTimestampForum(
|
||||
groupSecretKeyPublishDetails?.updated ||
|
||||
groupSecretKeyPublishDetails?.created
|
||||
),
|
||||
name: groupSecretKeyPublishDetails?.name,
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Button
|
||||
disabled={isFetchingGroupSecretKey}
|
||||
onClick={() => setIsForceShowCreationKeyPopup(true)}
|
||||
variant="contained"
|
||||
>
|
||||
Publish group secret key
|
||||
{t('auth:action.publish_group_secret_key', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Button>
|
||||
|
||||
<Spacer height="20px" />
|
||||
@ -232,9 +269,9 @@ export const AdminSpaceInner = ({
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
This key is to encrypt GROUP related content. This is the only one
|
||||
used in this UI as of now. All group members will be able to see
|
||||
content encrypted with this key.
|
||||
{t('auth:tips.key_encrypt_group', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@ -253,26 +290,41 @@ export const AdminSpaceInner = ({
|
||||
}}
|
||||
>
|
||||
{isFetchingAdminGroupSecretKey && (
|
||||
<Typography>Fetching Admins secret key</Typography>
|
||||
)}
|
||||
{!isFetchingAdminGroupSecretKey && !adminGroupSecretKey && (
|
||||
<Typography>No secret key published yet</Typography>
|
||||
)}
|
||||
{adminGroupSecretKeyPublishDetails && (
|
||||
<Typography>
|
||||
Last encryption date:{' '}
|
||||
{formatTimestampForum(
|
||||
adminGroupSecretKeyPublishDetails?.updated ||
|
||||
adminGroupSecretKeyPublishDetails?.created
|
||||
)}
|
||||
{t('auth:message.generic.fetching_admin_secret_key', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{!isFetchingAdminGroupSecretKey && !adminGroupSecretKey && (
|
||||
<Typography>
|
||||
{t('auth:message.generic.no_secret_key_published', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{adminGroupSecretKeyPublishDetails && (
|
||||
<Typography>
|
||||
{t('auth:message.generic.last_encryption_date', {
|
||||
date: formatTimestampForum(
|
||||
adminGroupSecretKeyPublishDetails?.updated ||
|
||||
adminGroupSecretKeyPublishDetails?.created
|
||||
),
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Button
|
||||
disabled={isFetchingAdminGroupSecretKey}
|
||||
onClick={createCommonSecretForAdmins}
|
||||
variant="contained"
|
||||
>
|
||||
Publish admin secret key
|
||||
{t('auth:action.publish_admin_secret_key', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Button>
|
||||
|
||||
<Spacer height="20px" />
|
||||
@ -282,11 +334,14 @@ export const AdminSpaceInner = ({
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
This key is to encrypt ADMIN related content. Only admins would see
|
||||
content encrypted with it.
|
||||
{t('auth:tips.key_encrypt_admin', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Spacer height="25px" />
|
||||
|
||||
{isOwner && (
|
||||
<Box
|
||||
sx={{
|
||||
@ -301,7 +356,11 @@ export const AdminSpaceInner = ({
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography>Group Avatar</Typography>
|
||||
<Typography>
|
||||
{t('group:group.avatar', {
|
||||
postProcess: 'capitalizeFirst',
|
||||
})}
|
||||
</Typography>
|
||||
|
||||
<GroupAvatar
|
||||
setOpenSnack={setOpenSnackGlobal}
|
||||
|
@ -14,6 +14,8 @@
|
||||
"create_account": "create account",
|
||||
"choose_password": "choose new password",
|
||||
"download_account": "download account",
|
||||
"publish_admin_secret_key": "publish admin secret key",
|
||||
"publish_group_secret_key": "publish group secret key",
|
||||
"return_to_list": "return to list"
|
||||
},
|
||||
"advanced_users": "for advanced users",
|
||||
@ -28,13 +30,24 @@
|
||||
"build_version": "build version",
|
||||
"message": {
|
||||
"error": {
|
||||
"account_creation": "could not create account."
|
||||
"account_creation": "could not create account.",
|
||||
"incorrect_password": "incorrect password",
|
||||
"invalid_secret_key": "secretKey is not valid",
|
||||
"unable_reencrypt_secret_key": "unable to re-encrypt secret key"
|
||||
},
|
||||
"generic": {
|
||||
"no_account": "No accounts saved",
|
||||
"no_account": "no accounts saved",
|
||||
"no_secret_key_published": "no secret key published yet",
|
||||
"fetching_admin_secret_key": "fetching Admins secret key",
|
||||
"fetching_group_secret_key": "fetching Group secret key publishes",
|
||||
"last_encryption_date": "last encryption date: {{ date }} by {{ name }}",
|
||||
"keep_secure": "keep your account file secure",
|
||||
"publishing_key": "reminder: After publishing the key, it will take a couple of minutes for it to appear. Please just wait.",
|
||||
"type_seed": "type or paste in your seed-phrase",
|
||||
"your_accounts": "your saved accounts"
|
||||
},
|
||||
"success": {
|
||||
"reencrypted_secret_key": "successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins."
|
||||
}
|
||||
},
|
||||
"node": {
|
||||
@ -53,6 +66,8 @@
|
||||
"additional_wallet": "use this option to connect additional Qortal wallets you've already made, in order to login with them afterwards. You will need access to your backup JSON file in order to do so.",
|
||||
"digital_id": "your wallet is like your digital ID on Qortal, and is how you will login to the Qortal User Interface. It holds your public address and the Qortal name you will eventually choose. Every transaction you make is linked to your ID, and this is where you manage all your QORT and other tradeable cryptocurrencies on Qortal.",
|
||||
"existing_account": "already have a Qortal account? Enter your secret backup phrase here to access it. This phrase is one of the ways to recover your account.",
|
||||
"key_encrypt_admin": "this key is to encrypt ADMIN related content. Only admins would see content encrypted with it.",
|
||||
"key_encrypt_group": "this key is to encrypt GROUP related content. This is the only one used in this UI as of now. All group members will be able to see content encrypted with this key.",
|
||||
"new_account": "creating an account means creating a new wallet and digital ID to start using Qortal. Once you have made your account, you can start doing things like obtaining some QORT, buying a name and avatar, publishing videos and blogs, and much more.",
|
||||
"new_users": "new users start here!"
|
||||
},
|
||||
|
@ -112,12 +112,12 @@
|
||||
"app_need_name": "your app needs a name",
|
||||
"file_too_large": "file {{ filename }} is too large. Max size allowed is {{ size }} MB.",
|
||||
"generic": "an error occurred",
|
||||
"incorrect_password": "incorrect password",
|
||||
"invalid_zip": "invalid zip",
|
||||
"minting_account_add": "unable to add minting account",
|
||||
"minting_account_remove": "unable to remove minting account",
|
||||
"missing_fields": "missing: {{ fields }}",
|
||||
"navigation_timeout": "navigation timeout",
|
||||
"network_generic": "network error",
|
||||
"publish_app": "unable to publish app",
|
||||
"rating_option": "cannot find rating option",
|
||||
"save_qdn": "unable to save to QDN",
|
||||
@ -145,7 +145,8 @@
|
||||
"secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party.",
|
||||
"select_file": "please select a file",
|
||||
"select_image": "please select an image for a logo",
|
||||
"select_zip": "select .zip file containing static content:"
|
||||
"select_zip": "select .zip file containing static content:",
|
||||
"space_for_admins": "sorry, this space is only for Admins."
|
||||
},
|
||||
"question": {
|
||||
"new_user": "are you a new user?"
|
||||
|
@ -33,6 +33,7 @@
|
||||
"maximum": "maximum Block delay for Group Transaction Approvals"
|
||||
},
|
||||
"group": {
|
||||
"avatar": "group avatar",
|
||||
"closed": "closed (private) - users need permission to join",
|
||||
"description": "description of group",
|
||||
"id": "group id",
|
||||
|
@ -4,6 +4,7 @@ import { crypto } from '../constants/decryptWallet';
|
||||
import Base58 from '../deps/Base58';
|
||||
import { AES_CBC, HmacSha512 } from 'asmcrypto.js';
|
||||
import { doInitWorkers, kdf } from '../deps/kdf';
|
||||
import i18n from 'i18next';
|
||||
|
||||
export const decryptStoredWallet = async (password, wallet) => {
|
||||
const threads = doInitWorkers(crypto.kdfThreads);
|
||||
@ -18,7 +19,7 @@ export const decryptStoredWallet = async (password, wallet) => {
|
||||
.process(encryptedSeedBytes)
|
||||
.finish().result;
|
||||
if (Base58.encode(mac) !== wallet.mac) {
|
||||
throw new Error('Incorrect password');
|
||||
throw new Error(i18n.t('auth:message.error.incorrect_password')); // TODO: i18n non-react integration
|
||||
}
|
||||
const decryptedBytes = AES_CBC.decrypt(
|
||||
encryptedSeedBytes,
|
||||
|
Loading…
x
Reference in New Issue
Block a user