mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-21 23:21:21 +00:00
Merge pull request #43 from nbenaglia/feature/i18n-continue
i18n: Add translations
This commit is contained in:
commit
01ab780175
11
src/App.tsx
11
src/App.tsx
@ -1778,6 +1778,7 @@ function App() {
|
|||||||
<QMailStatus />
|
<QMailStatus />
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
{extState === 'authenticated' && (
|
{extState === 'authenticated' && (
|
||||||
<GeneralNotifications address={userInfo?.address} />
|
<GeneralNotifications address={userInfo?.address} />
|
||||||
)}
|
)}
|
||||||
@ -2513,7 +2514,7 @@ function App() {
|
|||||||
setExtstate('create-wallet');
|
setExtstate('create-wallet');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('auth:create_account', { postProcess: 'capitalize' })}
|
{t('auth:action.create_account', { postProcess: 'capitalize' })}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -2610,7 +2611,7 @@ function App() {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('auth:authenticate', { postProcess: 'capitalize' })}
|
{t('auth:action.authenticate', { postProcess: 'capitalize' })}
|
||||||
</TextP>
|
</TextP>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -2662,7 +2663,7 @@ function App() {
|
|||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
<CustomButton onClick={authenticateWallet}>
|
<CustomButton onClick={authenticateWallet}>
|
||||||
{t('auth:authenticate', { postProcess: 'capitalize' })}
|
{t('auth:action.authenticate', { postProcess: 'capitalize' })}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
|
|
||||||
<ErrorText>{walletToBeDecryptedError}</ErrorText>
|
<ErrorText>{walletToBeDecryptedError}</ErrorText>
|
||||||
@ -2930,7 +2931,9 @@ function App() {
|
|||||||
<Spacer height="17px" />
|
<Spacer height="17px" />
|
||||||
|
|
||||||
<CustomButton onClick={createAccountFunc}>
|
<CustomButton onClick={createAccountFunc}>
|
||||||
{t('auth:create_account', { postProcess: 'capitalize' })}
|
{t('auth:action.create_account', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</Box>
|
</Box>
|
||||||
<ErrorText>{walletToBeDownloadedError}</ErrorText>
|
<ErrorText>{walletToBeDownloadedError}</ErrorText>
|
||||||
|
@ -572,7 +572,7 @@ export const NotAuthenticated = ({
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('auth:create_account', { postProcess: 'capitalize' })}
|
{t('auth:action.create_account', { postProcess: 'capitalize' })}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</HtmlTooltip>
|
</HtmlTooltip>
|
||||||
</Box>
|
</Box>
|
||||||
@ -953,7 +953,9 @@ export const NotAuthenticated = ({
|
|||||||
setCustomNodeToSaveIndex(null);
|
setCustomNodeToSaveIndex(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('auth:return_to_list', { postProcess: 'capitalize' })}
|
{t('auth:action.return_to_list', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
import React, { createContext, useContext, useState, useCallback, useRef } from 'react';
|
import {
|
||||||
import ShortUniqueId from "short-unique-id";
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useState,
|
||||||
|
useCallback,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
import ShortUniqueId from 'short-unique-id';
|
||||||
|
|
||||||
const MessageQueueContext = createContext(null);
|
const MessageQueueContext = createContext(null);
|
||||||
|
const uid = new ShortUniqueId({ length: 8 });
|
||||||
|
|
||||||
export const useMessageQueue = () => useContext(MessageQueueContext);
|
export const useMessageQueue = () => useContext(MessageQueueContext);
|
||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 8 });
|
|
||||||
|
|
||||||
export const MessageQueueProvider = ({ children }) => {
|
export const MessageQueueProvider = ({ children }) => {
|
||||||
const messageQueueRef = useRef([]);
|
const messageQueueRef = useRef([]);
|
||||||
const [queueChats, setQueueChats] = useState({}); // Stores chats and status for display
|
const [queueChats, setQueueChats] = useState({}); // Stores chats and status for display
|
||||||
@ -21,39 +26,41 @@ export const MessageQueueProvider = ({ children }) => {
|
|||||||
const processingPromiseRef = useRef(Promise.resolve());
|
const processingPromiseRef = useRef(Promise.resolve());
|
||||||
|
|
||||||
// Function to add a message to the queue
|
// Function to add a message to the queue
|
||||||
const addToQueue = useCallback((sendMessageFunc, messageObj, type, groupDirectId) => {
|
const addToQueue = useCallback(
|
||||||
const tempId = uid.rnd();
|
(sendMessageFunc, messageObj, type, groupDirectId) => {
|
||||||
const chatData = {
|
const tempId = uid.rnd();
|
||||||
...messageObj,
|
const chatData = {
|
||||||
type,
|
...messageObj,
|
||||||
groupDirectId,
|
type,
|
||||||
signature: uid.rnd(),
|
groupDirectId,
|
||||||
identifier: tempId,
|
signature: uid.rnd(),
|
||||||
retries: 0, // Retry count for display purposes
|
identifier: tempId,
|
||||||
status: 'pending' // Initial status is 'pending'
|
retries: 0, // Retry count for display purposes
|
||||||
};
|
status: 'pending', // Initial status is 'pending'
|
||||||
|
};
|
||||||
|
|
||||||
// Add chat data to queueChats for status tracking
|
// Add chat data to queueChats for status tracking
|
||||||
setQueueChats((prev) => ({
|
setQueueChats((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[groupDirectId]: [...(prev[groupDirectId] || []), chatData]
|
[groupDirectId]: [...(prev[groupDirectId] || []), chatData],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Add the message to the global messageQueueRef
|
// Add the message to the global messageQueueRef
|
||||||
messageQueueRef.current.push({
|
messageQueueRef.current.push({
|
||||||
func: sendMessageFunc,
|
func: sendMessageFunc,
|
||||||
identifier: tempId,
|
identifier: tempId,
|
||||||
groupDirectId,
|
groupDirectId,
|
||||||
specialId: messageObj?.message?.specialId
|
specialId: messageObj?.message?.specialId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start processing the queue
|
// Start processing the queue
|
||||||
processQueue([], groupDirectId);
|
processQueue([], groupDirectId);
|
||||||
}, []);
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
// Function to process the message queue
|
// Function to process the message queue
|
||||||
const processQueue = useCallback((newMessages = [], groupDirectId) => {
|
const processQueue = useCallback((newMessages = [], groupDirectId) => {
|
||||||
|
|
||||||
processingPromiseRef.current = processingPromiseRef.current
|
processingPromiseRef.current = processingPromiseRef.current
|
||||||
.then(() => processQueueInternal(newMessages, groupDirectId))
|
.then(() => processQueueInternal(newMessages, groupDirectId))
|
||||||
.catch((err) => console.error('Error in processQueue:', err));
|
.catch((err) => console.error('Error in processQueue:', err));
|
||||||
@ -62,7 +69,7 @@ export const MessageQueueProvider = ({ children }) => {
|
|||||||
// Internal function to handle queue processing
|
// Internal function to handle queue processing
|
||||||
const processQueueInternal = async (newMessages, groupDirectId) => {
|
const processQueueInternal = async (newMessages, groupDirectId) => {
|
||||||
// Remove any messages from the queue that match the specialId from newMessages
|
// Remove any messages from the queue that match the specialId from newMessages
|
||||||
|
|
||||||
// If the queue is empty, no need to process
|
// If the queue is empty, no need to process
|
||||||
if (messageQueueRef.current.length === 0) return;
|
if (messageQueueRef.current.length === 0) return;
|
||||||
|
|
||||||
@ -92,7 +99,6 @@ export const MessageQueueProvider = ({ children }) => {
|
|||||||
|
|
||||||
// Remove the message from the queue after successful sending
|
// Remove the message from the queue after successful sending
|
||||||
messageQueueRef.current.shift();
|
messageQueueRef.current.shift();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Message sending failed', error);
|
console.error('Message sending failed', error);
|
||||||
|
|
||||||
@ -110,7 +116,8 @@ export const MessageQueueProvider = ({ children }) => {
|
|||||||
updatedChats[groupDirectId][chatIndex].status = 'failed';
|
updatedChats[groupDirectId][chatIndex].status = 'failed';
|
||||||
} else {
|
} else {
|
||||||
// Max retries reached, set status to 'failed-permanent'
|
// Max retries reached, set status to 'failed-permanent'
|
||||||
updatedChats[groupDirectId][chatIndex].status = 'failed-permanent';
|
updatedChats[groupDirectId][chatIndex].status =
|
||||||
|
'failed-permanent';
|
||||||
|
|
||||||
// Remove the message from the queue after max retries
|
// Remove the message from the queue after max retries
|
||||||
messageQueueRef.current.shift();
|
messageQueueRef.current.shift();
|
||||||
@ -127,33 +134,39 @@ export const MessageQueueProvider = ({ children }) => {
|
|||||||
|
|
||||||
// Method to process with new messages and groupDirectId
|
// Method to process with new messages and groupDirectId
|
||||||
const processWithNewMessages = (newMessages, groupDirectId) => {
|
const processWithNewMessages = (newMessages, groupDirectId) => {
|
||||||
let updatedNewMessages = newMessages
|
let updatedNewMessages = newMessages;
|
||||||
if (newMessages.length > 0) {
|
if (newMessages.length > 0) {
|
||||||
// Remove corresponding entries in queueChats for the provided groupDirectId
|
// Remove corresponding entries in queueChats for the provided groupDirectId
|
||||||
setQueueChats((prev) => {
|
setQueueChats((prev) => {
|
||||||
const updatedChats = { ...prev };
|
const updatedChats = { ...prev };
|
||||||
if (updatedChats[groupDirectId]) {
|
if (updatedChats[groupDirectId]) {
|
||||||
|
updatedNewMessages = newMessages?.map((msg) => {
|
||||||
updatedNewMessages = newMessages?.map((msg)=> {
|
const findTempMsg = updatedChats[groupDirectId]?.find(
|
||||||
const findTempMsg = updatedChats[groupDirectId]?.find((msg2)=> msg2?.message?.specialId === msg?.specialId)
|
(msg2) => msg2?.message?.specialId === msg?.specialId
|
||||||
if(findTempMsg){
|
);
|
||||||
|
if (findTempMsg) {
|
||||||
return {
|
return {
|
||||||
...msg,
|
...msg,
|
||||||
tempSignature: findTempMsg?.signature
|
tempSignature: findTempMsg?.signature,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
return msg
|
return msg;
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => {
|
|
||||||
return !newMessages.some(newMsg => newMsg?.specialId === chat?.message?.specialId);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updatedChats[groupDirectId] = updatedChats[groupDirectId].filter(
|
||||||
|
(chat) => {
|
||||||
|
return !newMessages.some(
|
||||||
|
(newMsg) => newMsg?.specialId === chat?.message?.specialId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Remove messages with status 'failed-permanent'
|
// Remove messages with status 'failed-permanent'
|
||||||
updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => {
|
updatedChats[groupDirectId] = updatedChats[groupDirectId].filter(
|
||||||
return chat?.status !== 'failed-permanent';
|
(chat) => {
|
||||||
});
|
return chat?.status !== 'failed-permanent';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// If no more chats for this group, delete the groupDirectId entry
|
// If no more chats for this group, delete the groupDirectId entry
|
||||||
if (updatedChats[groupDirectId].length === 0) {
|
if (updatedChats[groupDirectId].length === 0) {
|
||||||
@ -162,27 +175,36 @@ export const MessageQueueProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
return updatedChats;
|
return updatedChats;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if(!messageQueueRef.current.find((msg) => msg?.groupDirectId === groupDirectId)){
|
if (
|
||||||
|
!messageQueueRef.current.find(
|
||||||
|
(msg) => msg?.groupDirectId === groupDirectId
|
||||||
|
)
|
||||||
|
) {
|
||||||
setQueueChats((prev) => {
|
setQueueChats((prev) => {
|
||||||
const updatedChats = { ...prev };
|
const updatedChats = { ...prev };
|
||||||
if (updatedChats[groupDirectId]) {
|
if (updatedChats[groupDirectId]) {
|
||||||
delete updatedChats[groupDirectId]
|
delete updatedChats[groupDirectId];
|
||||||
}
|
}
|
||||||
|
|
||||||
return updatedChats
|
return updatedChats;
|
||||||
}
|
});
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
return updatedNewMessages
|
return updatedNewMessages;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageQueueContext.Provider value={{ addToQueue, queueChats, clearStatesMessageQueueProvider, processWithNewMessages }}>
|
<MessageQueueContext.Provider
|
||||||
|
value={{
|
||||||
|
addToQueue,
|
||||||
|
queueChats,
|
||||||
|
clearStatesMessageQueueProvider,
|
||||||
|
processWithNewMessages,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</MessageQueueContext.Provider>
|
</MessageQueueContext.Provider>
|
||||||
);
|
);
|
||||||
|
143
src/Wallets.tsx
143
src/Wallets.tsx
@ -1,4 +1,4 @@
|
|||||||
import React, { useContext, useEffect, useState } from 'react';
|
import { Fragment, useContext, useEffect, useState } from 'react';
|
||||||
import List from '@mui/material/List';
|
import List from '@mui/material/List';
|
||||||
import ListItem from '@mui/material/ListItem';
|
import ListItem from '@mui/material/ListItem';
|
||||||
import Divider from '@mui/material/Divider';
|
import Divider from '@mui/material/Divider';
|
||||||
@ -32,6 +32,7 @@ import { LoadingButton } from '@mui/lab';
|
|||||||
import { PasswordField } from './components';
|
import { PasswordField } from './components';
|
||||||
import { HtmlTooltip } from './ExtStates/NotAuthenticated';
|
import { HtmlTooltip } from './ExtStates/NotAuthenticated';
|
||||||
import { MyContext } from './App';
|
import { MyContext } from './App';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const parsefilenameQortal = (filename) => {
|
const parsefilenameQortal = (filename) => {
|
||||||
return filename.startsWith('qortal_backup_') ? filename.slice(14) : filename;
|
return filename.startsWith('qortal_backup_') ? filename.slice(14) : filename;
|
||||||
@ -44,11 +45,11 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
const [seedName, setSeedName] = useState('');
|
const [seedName, setSeedName] = useState('');
|
||||||
const [seedError, setSeedError] = useState('');
|
const [seedError, setSeedError] = useState('');
|
||||||
const { hasSeenGettingStarted } = useContext(MyContext);
|
const { hasSeenGettingStarted } = useContext(MyContext);
|
||||||
|
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [isOpenSeedModal, setIsOpenSeedModal] = useState(false);
|
const [isOpenSeedModal, setIsOpenSeedModal] = useState(false);
|
||||||
const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false);
|
const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const { t } = useTranslation(['core', 'auth']);
|
||||||
const { isShow, onCancel, onOk, show } = useModal();
|
const { isShow, onCancel, onOk, show } = useModal();
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
@ -82,8 +83,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let error: any = null;
|
const uniqueInitialMap = new Map();
|
||||||
let uniqueInitialMap = new Map();
|
|
||||||
|
|
||||||
// Only add a message if it doesn't already exist in the Map
|
// Only add a message if it doesn't already exist in the Map
|
||||||
importedWallets.forEach((wallet) => {
|
importedWallets.forEach((wallet) => {
|
||||||
@ -92,7 +92,9 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
uniqueInitialMap.set(wallet?.address0, wallet);
|
uniqueInitialMap.set(wallet?.address0, wallet);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = Array.from(uniqueInitialMap.values());
|
const data = Array.from(uniqueInitialMap.values());
|
||||||
|
|
||||||
if (data && data?.length > 0) {
|
if (data && data?.length > 0) {
|
||||||
const uniqueNewWallets = data.filter(
|
const uniqueNewWallets = data.filter(
|
||||||
(newWallet) =>
|
(newWallet) =>
|
||||||
@ -148,10 +150,19 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
setPassword('');
|
setPassword('');
|
||||||
setSeedError('');
|
setSeedError('');
|
||||||
} else {
|
} else {
|
||||||
setSeedError('Could not create account.');
|
setSeedError(
|
||||||
|
t('auth:message.error.account_creation', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setSeedError(error?.message || 'Could not create account.');
|
setSeedError(
|
||||||
|
error?.message ||
|
||||||
|
t('auth:message.error.account_creation', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingEncryptSeed(false);
|
setIsLoadingEncryptSeed(false);
|
||||||
}
|
}
|
||||||
@ -189,19 +200,34 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
<div>
|
<div>
|
||||||
{wallets?.length === 0 || !wallets ? (
|
{wallets?.length === 0 || !wallets ? (
|
||||||
<>
|
<>
|
||||||
<Typography>No accounts saved</Typography>
|
<Typography>
|
||||||
|
{t('auth:message.generic.no_account', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
<Spacer height="75px" />
|
<Spacer height="75px" />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Typography>Your saved accounts</Typography>
|
<Typography>
|
||||||
|
{t('auth:message.generic.your_accounts', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{rawWallet && (
|
{rawWallet && (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography>Selected Account:</Typography>
|
<Typography>
|
||||||
|
{t('auth:account.selected', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
:
|
||||||
|
</Typography>
|
||||||
{rawWallet?.name && <Typography>{rawWallet.name}</Typography>}
|
{rawWallet?.name && <Typography>{rawWallet.name}</Typography>}
|
||||||
{rawWallet?.address0 && (
|
{rawWallet?.address0 && (
|
||||||
<Typography>{rawWallet?.address0}</Typography>
|
<Typography>{rawWallet?.address0}</Typography>
|
||||||
@ -211,12 +237,12 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
{wallets?.length > 0 && (
|
{wallets?.length > 0 && (
|
||||||
<List
|
<List
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
maxWidth: '500px',
|
|
||||||
maxHeight: '60vh',
|
|
||||||
overflowY: 'auto',
|
|
||||||
overflowX: 'hidden',
|
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
maxHeight: '60vh',
|
||||||
|
maxWidth: '500px',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
overflowY: 'auto',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{wallets?.map((wallet, idx) => {
|
{wallets?.map((wallet, idx) => {
|
||||||
@ -238,29 +264,27 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
bottom: wallets?.length === 0 ? 'unset' : '20px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
alignItems: 'center',
|
|
||||||
position: wallets?.length === 0 ? 'relative' : 'fixed',
|
position: wallets?.length === 0 ? 'relative' : 'fixed',
|
||||||
bottom: wallets?.length === 0 ? 'unset' : '20px',
|
|
||||||
right: wallets?.length === 0 ? 'unset' : '20px',
|
right: wallets?.length === 0 ? 'unset' : '20px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<HtmlTooltip
|
<HtmlTooltip
|
||||||
disableHoverListener={hasSeenGettingStarted === true}
|
disableHoverListener={hasSeenGettingStarted === true}
|
||||||
title={
|
title={
|
||||||
<React.Fragment>
|
<Fragment>
|
||||||
<Typography
|
<Typography
|
||||||
color="inherit"
|
color="inherit"
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Already have a Qortal account? Enter your secret backup phrase
|
{t('auth:tips.existing_account', { postProcess: 'capitalize' })}
|
||||||
here to access it. This phrase is one of the ways to recover
|
|
||||||
your account.
|
|
||||||
</Typography>
|
</Typography>
|
||||||
</React.Fragment>
|
</Fragment>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CustomButton
|
<CustomButton
|
||||||
@ -270,25 +294,25 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
display: 'inline',
|
display: 'inline',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add seed-phrase
|
{t('auth:action.add.seed_phrase', { postProcess: 'capitalize' })}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</HtmlTooltip>
|
</HtmlTooltip>
|
||||||
|
|
||||||
<HtmlTooltip
|
<HtmlTooltip
|
||||||
disableHoverListener={hasSeenGettingStarted === true}
|
disableHoverListener={hasSeenGettingStarted === true}
|
||||||
title={
|
title={
|
||||||
<React.Fragment>
|
<Fragment>
|
||||||
<Typography
|
<Typography
|
||||||
color="inherit"
|
color="inherit"
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Use this option to connect additional Qortal wallets you've
|
{t('auth:tips.additional_wallet', {
|
||||||
already made, in order to login with them afterwards. You will
|
postProcess: 'capitalize',
|
||||||
need access to your backup JSON file in order to do so.
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</React.Fragment>
|
</Fragment>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CustomButton
|
<CustomButton
|
||||||
@ -298,7 +322,9 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
{...getRootProps()}
|
{...getRootProps()}
|
||||||
>
|
>
|
||||||
<input {...getInputProps()} />
|
<input {...getInputProps()} />
|
||||||
Add account
|
{t('auth:action.add.account', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
</HtmlTooltip>
|
</HtmlTooltip>
|
||||||
</Box>
|
</Box>
|
||||||
@ -314,7 +340,9 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle id="alert-dialog-title">
|
<DialogTitle id="alert-dialog-title">
|
||||||
Type or paste in your seed-phrase
|
{t('auth:message.generic.type_seed', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
@ -324,7 +352,11 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>Name</Label>
|
<Label>
|
||||||
|
{t('auth:name', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
value={seedName}
|
value={seedName}
|
||||||
@ -333,7 +365,11 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
|
|
||||||
<Spacer height="7px" />
|
<Spacer height="7px" />
|
||||||
|
|
||||||
<Label>Seed-phrase</Label>
|
<Label>
|
||||||
|
{t('auth:seed', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
<PasswordField
|
<PasswordField
|
||||||
placeholder="Seed-phrase"
|
placeholder="Seed-phrase"
|
||||||
id="standard-adornment-password"
|
id="standard-adornment-password"
|
||||||
@ -347,7 +383,11 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
|
|
||||||
<Spacer height="7px" />
|
<Spacer height="7px" />
|
||||||
|
|
||||||
<Label>Choose new password</Label>
|
<Label>
|
||||||
|
{t('auth:action.choose_password', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
<PasswordField
|
<PasswordField
|
||||||
id="standard-adornment-password"
|
id="standard-adornment-password"
|
||||||
value={password}
|
value={password}
|
||||||
@ -359,6 +399,7 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
disabled={isLoadingEncryptSeed}
|
disabled={isLoadingEncryptSeed}
|
||||||
@ -371,7 +412,9 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
setSeedError('');
|
setSeedError('');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Close
|
{t('core:action.close', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
loading={isLoadingEncryptSeed}
|
loading={isLoadingEncryptSeed}
|
||||||
@ -383,7 +426,9 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
|||||||
}}
|
}}
|
||||||
autoFocus
|
autoFocus
|
||||||
>
|
>
|
||||||
Add
|
{t('core:action.add', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
@ -404,6 +449,7 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
|||||||
const [note, setNote] = useState('');
|
const [note, setNote] = useState('');
|
||||||
const [isEdit, setIsEdit] = useState(false);
|
const [isEdit, setIsEdit] = useState(false);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const { t } = useTranslation(['core', 'auth']);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (wallet?.name) {
|
if (wallet?.name) {
|
||||||
@ -439,6 +485,7 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
|||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar alt="" src="/static/images/avatar/1.jpg" />
|
<Avatar alt="" src="/static/images/avatar/1.jpg" />
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
|
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={
|
primary={
|
||||||
wallet?.name
|
wallet?.name
|
||||||
@ -468,7 +515,9 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
|||||||
marginTop: '5px',
|
marginTop: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Login
|
{t('core:action.login', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
@ -495,7 +544,11 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
|||||||
padding: '8px',
|
padding: '8px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>Name</Label>
|
<Label>
|
||||||
|
{t('auth:name', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
value={name}
|
value={name}
|
||||||
@ -507,7 +560,11 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
|||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
|
|
||||||
<Label>Note</Label>
|
<Label>
|
||||||
|
{t('auth:note', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Note"
|
placeholder="Note"
|
||||||
value={note}
|
value={note}
|
||||||
@ -535,7 +592,9 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => setIsEdit(false)}
|
onClick={() => setIsEdit(false)}
|
||||||
>
|
>
|
||||||
Close
|
{t('core:action.close', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
@ -551,7 +610,9 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => updateWalletItem(idx, null)}
|
onClick={() => updateWalletItem(idx, null)}
|
||||||
>
|
>
|
||||||
Remove
|
{t('core:action.remove', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
@ -574,7 +635,9 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
|||||||
setIsEdit(false);
|
setIsEdit(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Save
|
{t('core:action.save', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -18,37 +18,36 @@ export const sortablePinnedAppsAtom = atomWithReset([
|
|||||||
{ name: 'Q-Nodecontrol', service: 'APP' },
|
{ name: 'Q-Nodecontrol', service: 'APP' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const canSaveSettingToQdnAtom = atomWithReset(false);
|
|
||||||
export const settingsQDNLastUpdatedAtom = atomWithReset(-100);
|
|
||||||
export const settingsLocalLastUpdatedAtom = atomWithReset(0);
|
|
||||||
export const oldPinnedAppsAtom = atomWithReset([]);
|
|
||||||
export const isUsingImportExportSettingsAtom = atomWithReset(null);
|
|
||||||
export const fullScreenAtom = atomWithReset(false);
|
|
||||||
export const hasSettingsChangedAtom = atomWithReset(false);
|
|
||||||
export const navigationControllerAtom = atomWithReset({});
|
|
||||||
export const enabledDevModeAtom = atomWithReset(false);
|
|
||||||
export const myGroupsWhereIAmAdminAtom = atomWithReset([]);
|
|
||||||
export const promotionTimeIntervalAtom = atomWithReset(0);
|
|
||||||
export const promotionsAtom = atomWithReset([]);
|
|
||||||
export const resourceDownloadControllerAtom = atomWithReset({});
|
|
||||||
export const blobControllerAtom = atomWithReset({});
|
|
||||||
export const selectedGroupIdAtom = atomWithReset(null);
|
|
||||||
export const addressInfoControllerAtom = atomWithReset({});
|
export const addressInfoControllerAtom = atomWithReset({});
|
||||||
|
export const blobControllerAtom = atomWithReset({});
|
||||||
|
export const canSaveSettingToQdnAtom = atomWithReset(false);
|
||||||
|
export const enabledDevModeAtom = atomWithReset(false);
|
||||||
|
export const fullScreenAtom = atomWithReset(false);
|
||||||
|
export const groupAnnouncementsAtom = atomWithReset({});
|
||||||
|
export const groupChatTimestampsAtom = atomWithReset({});
|
||||||
|
export const groupsOwnerNamesAtom = atomWithReset({});
|
||||||
|
export const groupsPropertiesAtom = atomWithReset({});
|
||||||
|
export const hasSettingsChangedAtom = atomWithReset(false);
|
||||||
export const isDisabledEditorEnterAtom = atomWithReset(false);
|
export const isDisabledEditorEnterAtom = atomWithReset(false);
|
||||||
export const qMailLastEnteredTimestampAtom = atomWithReset(null);
|
export const isOpenBlockedModalAtom = atomWithReset(false);
|
||||||
|
export const isRunningPublicNodeAtom = atomWithReset(false);
|
||||||
|
export const isUsingImportExportSettingsAtom = atomWithReset(null);
|
||||||
export const lastPaymentSeenTimestampAtom = atomWithReset(null);
|
export const lastPaymentSeenTimestampAtom = atomWithReset(null);
|
||||||
export const mailsAtom = atomWithReset([]);
|
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 txListAtom = atomWithReset([]);
|
|
||||||
export const memberGroupsAtom = atomWithReset([]);
|
export const memberGroupsAtom = atomWithReset([]);
|
||||||
export const isRunningPublicNodeAtom = atomWithReset(false);
|
export const mutedGroupsAtom = atomWithReset([]);
|
||||||
|
export const myGroupsWhereIAmAdminAtom = atomWithReset([]);
|
||||||
|
export const navigationControllerAtom = atomWithReset({});
|
||||||
|
export const oldPinnedAppsAtom = atomWithReset([]);
|
||||||
|
export const promotionsAtom = atomWithReset([]);
|
||||||
|
export const promotionTimeIntervalAtom = atomWithReset(0);
|
||||||
|
export const qMailLastEnteredTimestampAtom = atomWithReset(null);
|
||||||
|
export const resourceDownloadControllerAtom = atomWithReset({});
|
||||||
|
export const selectedGroupIdAtom = atomWithReset(null);
|
||||||
|
export const settingsLocalLastUpdatedAtom = atomWithReset(0);
|
||||||
|
export const settingsQDNLastUpdatedAtom = atomWithReset(-100);
|
||||||
|
export const timestampEnterDataAtom = atomWithReset({});
|
||||||
|
export const txListAtom = atomWithReset([]);
|
||||||
|
|
||||||
// Atom Families (replacing selectorFamily)
|
// Atom Families (replacing selectorFamily)
|
||||||
export const resourceKeySelector = atomFamily((key) =>
|
export const resourceKeySelector = atomFamily((key) =>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import {
|
import {
|
||||||
AppCircle,
|
AppCircle,
|
||||||
AppCircleContainer,
|
AppCircleContainer,
|
||||||
@ -172,7 +171,7 @@ export const AppInfo = ({ app, myName }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppDownloadButtonText>
|
<AppDownloadButtonText>
|
||||||
{isSelectedAppPinned
|
{isSelectedAppPinned // TODO translate
|
||||||
? 'Unpin from dashboard'
|
? 'Unpin from dashboard'
|
||||||
: 'Pin to dashboard'}
|
: 'Pin to dashboard'}
|
||||||
</AppDownloadButtonText>
|
</AppDownloadButtonText>
|
||||||
|
@ -166,7 +166,7 @@ export const AppInfoSnippet = ({
|
|||||||
sx={{
|
sx={{
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
opacity: isSelectedAppPinned ? 0.6 : 1,
|
opacity: isSelectedAppPinned ? 0.6 : 1,
|
||||||
}}
|
}} // TODO translate
|
||||||
>
|
>
|
||||||
<AppDownloadButtonText>
|
<AppDownloadButtonText>
|
||||||
{' '}
|
{' '}
|
||||||
|
@ -185,6 +185,7 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
const fee = await getFee('ARBITRARY');
|
const fee = await getFee('ARBITRARY');
|
||||||
|
|
||||||
await show({
|
await show({
|
||||||
|
// TODO translate
|
||||||
message: 'Would you like to publish this app?',
|
message: 'Would you like to publish this app?',
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
|
@ -106,6 +106,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = 'right' }) => {
|
|||||||
const fee = await getFee('CREATE_POLL');
|
const fee = await getFee('CREATE_POLL');
|
||||||
|
|
||||||
await show({
|
await show({
|
||||||
|
// TODO translate
|
||||||
message: `Would you like to rate this app a rating of ${newValue}?. It will create a POLL tx.`,
|
message: `Would you like to rate this app a rating of ${newValue}?. It will create a POLL tx.`,
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
|
@ -351,7 +351,7 @@ export const PublishQAppChoseFile = styled(ButtonBase)(({ theme }) => ({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
width: '120px',
|
width: '120px',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'action.hover', // background on hover
|
backgroundColor: 'action.hover',
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -16,20 +16,6 @@ import { Spacer } from '../../common/Spacer';
|
|||||||
import { AppInfoSnippet } from './AppInfoSnippet';
|
import { AppInfoSnippet } from './AppInfoSnippet';
|
||||||
import { Virtuoso } from 'react-virtuoso';
|
import { Virtuoso } from 'react-virtuoso';
|
||||||
|
|
||||||
const ScrollerStyled = styled('div')({
|
|
||||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
|
||||||
'::-webkit-scrollbar': {
|
|
||||||
width: '0px',
|
|
||||||
height: '0px',
|
|
||||||
},
|
|
||||||
|
|
||||||
// Hide scrollbar for Firefox
|
|
||||||
scrollbarWidth: 'none',
|
|
||||||
|
|
||||||
// Hide scrollbar for IE and older Edge
|
|
||||||
msOverflowStyle: 'none',
|
|
||||||
});
|
|
||||||
|
|
||||||
const StyledVirtuosoContainer = styled('div')({
|
const StyledVirtuosoContainer = styled('div')({
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -99,7 +85,8 @@ export const AppsCategoryDesktop = ({
|
|||||||
}, [debouncedValue, categoryList]);
|
}, [debouncedValue, categoryList]);
|
||||||
|
|
||||||
const rowRenderer = (index) => {
|
const rowRenderer = (index) => {
|
||||||
let app = searchedList[index];
|
const app = searchedList[index];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppInfoSnippet
|
<AppInfoSnippet
|
||||||
key={`${app?.service}-${app?.name}`}
|
key={`${app?.service}-${app?.name}`}
|
||||||
@ -205,9 +192,6 @@ export const AppsCategoryDesktop = ({
|
|||||||
itemContent={rowRenderer}
|
itemContent={rowRenderer}
|
||||||
atBottomThreshold={50}
|
atBottomThreshold={50}
|
||||||
followOutput="smooth"
|
followOutput="smooth"
|
||||||
// components={{
|
|
||||||
// Scroller: ScrollerStyled, // Use the styled scroller component
|
|
||||||
// }}
|
|
||||||
/>
|
/>
|
||||||
</StyledVirtuosoContainer>
|
</StyledVirtuosoContainer>
|
||||||
</AppsWidthLimiter>
|
</AppsWidthLimiter>
|
||||||
|
@ -17,7 +17,6 @@ import { AppsCategoryDesktop } from './AppsCategoryDesktop';
|
|||||||
import { AppsNavBarDesktop } from './AppsNavBarDesktop';
|
import { AppsNavBarDesktop } from './AppsNavBarDesktop';
|
||||||
import { Box, ButtonBase, useTheme } from '@mui/material';
|
import { Box, ButtonBase, useTheme } from '@mui/material';
|
||||||
import { HomeIcon } from '../../assets/Icons/HomeIcon';
|
import { HomeIcon } from '../../assets/Icons/HomeIcon';
|
||||||
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 { enabledDevModeAtom } from '../../atoms/global';
|
import { enabledDevModeAtom } from '../../atoms/global';
|
||||||
@ -98,8 +97,6 @@ export const AppsDesktop = ({
|
|||||||
setCategories(responseData);
|
setCategories(responseData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
|
||||||
// dispatch(setIsLoadingGlobal(false))
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -135,8 +132,6 @@ export const AppsDesktop = ({
|
|||||||
setAvailableQapps(combine);
|
setAvailableQapps(combine);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
|
||||||
// dispatch(setIsLoadingGlobal(false))
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -338,13 +333,13 @@ export const AppsDesktop = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
backgroundColor: theme.palette.background.surface,
|
||||||
|
borderRight: `1px solid ${theme.palette.border.subtle}`,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: '25px',
|
gap: '25px',
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
width: '60px',
|
width: '60px',
|
||||||
backgroundColor: theme.palette.background.surface,
|
|
||||||
borderRight: `1px solid ${theme.palette.border.subtle}`,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
@ -407,6 +402,7 @@ export const AppsDesktop = ({
|
|||||||
/>
|
/>
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
<Save isDesktop disableWidth myName={myName} />
|
<Save isDesktop disableWidth myName={myName} />
|
||||||
{isEnabledDevMode && (
|
{isEnabledDevMode && (
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
@ -445,10 +441,10 @@ export const AppsDesktop = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100%',
|
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
@ -515,6 +511,7 @@ export const AppsDesktop = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
|
|
||||||
<AppsHomeDesktop
|
<AppsHomeDesktop
|
||||||
myName={myName}
|
myName={myName}
|
||||||
availableQapps={availableQapps}
|
availableQapps={availableQapps}
|
||||||
|
@ -310,7 +310,9 @@ export const AppsDevMode = ({
|
|||||||
/>
|
/>
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
<Save isDesktop disableWidth myName={myName} />
|
<Save isDesktop disableWidth myName={myName} />
|
||||||
|
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDesktopViewMode('dev');
|
setDesktopViewMode('dev');
|
||||||
@ -342,10 +344,10 @@ export const AppsDevMode = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100%',
|
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
@ -387,6 +389,7 @@ export const AppsDevMode = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
|
|
||||||
<AppsDevModeHome
|
<AppsDevModeHome
|
||||||
myName={myName}
|
myName={myName}
|
||||||
availableQapps={availableQapps}
|
availableQapps={availableQapps}
|
||||||
|
@ -446,7 +446,7 @@ export const AppsDevModeHome = ({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: '5px',
|
gap: '5px',
|
||||||
}}
|
}} // TODO translate
|
||||||
>
|
>
|
||||||
<Label>Domain</Label>
|
<Label>Domain</Label>
|
||||||
<Input
|
<Input
|
||||||
|
@ -80,13 +80,13 @@ export const AppsDevModeNavBar = () => {
|
|||||||
return (
|
return (
|
||||||
<AppsNavBarParent
|
<AppsNavBarParent
|
||||||
sx={{
|
sx={{
|
||||||
position: 'relative',
|
borderRadius: '0px 30px 30px 0px',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
width: '59px',
|
|
||||||
height: 'unset',
|
height: 'unset',
|
||||||
maxHeight: '70vh',
|
maxHeight: '70vh',
|
||||||
borderRadius: '0px 30px 30px 0px',
|
|
||||||
padding: '10px',
|
padding: '10px',
|
||||||
|
position: 'relative',
|
||||||
|
width: '59px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppsNavBarLeft
|
<AppsNavBarLeft
|
||||||
|
@ -51,7 +51,7 @@ export const AppsHomeDesktop = ({
|
|||||||
<AppLibrarySubTitle
|
<AppLibrarySubTitle
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: '30px',
|
fontSize: '30px',
|
||||||
}}
|
}} // TODO translate
|
||||||
>
|
>
|
||||||
Apps Dashboard
|
Apps Dashboard
|
||||||
</AppLibrarySubTitle>
|
</AppLibrarySubTitle>
|
||||||
|
@ -273,7 +273,7 @@ export const AppsLibraryDesktop = ({
|
|||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
executeEvent('navigateBack', {});
|
executeEvent('navigateBack', {});
|
||||||
}}
|
}} // TODO translate
|
||||||
>
|
>
|
||||||
<ReturnIcon />
|
<ReturnIcon />
|
||||||
<ComposeP>Return to Apps Dashboard</ComposeP>
|
<ComposeP>Return to Apps Dashboard</ComposeP>
|
||||||
|
@ -152,7 +152,6 @@ export const AppsPrivate = ({ myName }) => {
|
|||||||
const object64 = await objectToBase64(objectToSave);
|
const object64 = await objectToBase64(objectToSave);
|
||||||
const decryptedData = await window.sendMessage(
|
const decryptedData = await window.sendMessage(
|
||||||
'ENCRYPT_QORTAL_GROUP_DATA',
|
'ENCRYPT_QORTAL_GROUP_DATA',
|
||||||
|
|
||||||
{
|
{
|
||||||
base64: object64,
|
base64: object64,
|
||||||
groupId: selectedGroup,
|
groupId: selectedGroup,
|
||||||
@ -313,7 +312,7 @@ export const AppsPrivate = ({ myName }) => {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: '5px',
|
gap: '5px',
|
||||||
}}
|
}} // TODO translate
|
||||||
>
|
>
|
||||||
<Label>Select a group</Label>
|
<Label>Select a group</Label>
|
||||||
<Label>Only private groups will be shown</Label>
|
<Label>Only private groups will be shown</Label>
|
||||||
|
@ -157,7 +157,7 @@ export const DownloadWallet = ({
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('auth:download_account', { postProcess: 'capitalize' })}
|
{t('auth:action.download_account', { postProcess: 'capitalize' })}
|
||||||
</TextP>
|
</TextP>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -247,13 +247,13 @@ export const DownloadWallet = ({
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await saveFileToDiskFunc();
|
await saveFileToDiskFunc();
|
||||||
await showInfo({
|
await showInfo({
|
||||||
message: t('auth:keep_secure', {
|
message: t('auth:message.generic.keep_secure', {
|
||||||
postProcess: 'capitalize',
|
postProcess: 'capitalize',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('auth:download_account', {
|
{t('auth:action.download_account', {
|
||||||
postProcess: 'capitalize',
|
postProcess: 'capitalize',
|
||||||
})}
|
})}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
|
@ -15,7 +15,7 @@ import { base64ToUint8Array } from '../../qdn/encryption/group-encryption';
|
|||||||
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
||||||
import { formatTimestampForum } from '../../utils/time';
|
import { formatTimestampForum } from '../../utils/time';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
import { GroupAvatar } from '../GroupAvatar';
|
import { GroupAvatar } from './GroupAvatar';
|
||||||
|
|
||||||
export const getPublishesFromAdminsAdminSpace = async (
|
export const getPublishesFromAdminsAdminSpace = async (
|
||||||
admins: string[],
|
admins: string[],
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { useCallback, useContext, useEffect, useState } from 'react';
|
import { useCallback, useContext, useEffect, useState } from 'react';
|
||||||
import Logo2 from '../assets/svgs/Logo2.svg';
|
import Logo2 from '../../assets/svgs/Logo2.svg';
|
||||||
import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from '../App';
|
import {
|
||||||
|
MyContext,
|
||||||
|
getArbitraryEndpointReact,
|
||||||
|
getBaseApiReact,
|
||||||
|
} from '../../App';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
@ -10,10 +14,10 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Spacer } from '../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
import ImageUploader from '../common/ImageUploader';
|
import ImageUploader from '../../common/ImageUploader';
|
||||||
import { getFee } from '../background';
|
import { getFee } from '../../background';
|
||||||
import { fileToBase64 } from '../utils/fileReading';
|
import { fileToBase64 } from '../../utils/fileReading';
|
||||||
import { LoadingButton } from '@mui/lab';
|
import { LoadingButton } from '@mui/lab';
|
||||||
import ErrorIcon from '@mui/icons-material/Error';
|
import ErrorIcon from '@mui/icons-material/Error';
|
||||||
|
|
@ -371,7 +371,6 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>
|
<Label>
|
||||||
{' '}
|
|
||||||
{t('group:group.type', {
|
{t('group:group.type', {
|
||||||
postProcess: 'capitalize',
|
postProcess: 'capitalize',
|
||||||
})}
|
})}
|
||||||
@ -428,6 +427,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
postProcess: 'capitalize',
|
postProcess: 'capitalize',
|
||||||
})}
|
})}
|
||||||
</Label>
|
</Label>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
labelId="demo-simple-select-label"
|
labelId="demo-simple-select-label"
|
||||||
id="demo-simple-select"
|
id="demo-simple-select"
|
||||||
@ -536,7 +536,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
{t('core:time.hour', { count: 1 })}
|
{t('core:time.hour', { count: 1 })}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value={180}>
|
<MenuItem value={180}>
|
||||||
3{t('core:time.hour', { count: 3 })}
|
{t('core:time.hour', { count: 3 })}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value={300}>
|
<MenuItem value={300}>
|
||||||
{t('core:time.hour', { count: 5 })}
|
{t('core:time.hour', { count: 5 })}
|
||||||
|
@ -15,6 +15,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
const inviteMember = async () => {
|
const inviteMember = async () => {
|
||||||
try {
|
try {
|
||||||
const fee = await getFee('GROUP_INVITE');
|
const fee = await getFee('GROUP_INVITE');
|
||||||
|
|
||||||
await show({
|
await show({
|
||||||
message: t('group:question.perform_transaction', {
|
message: t('group:question.perform_transaction', {
|
||||||
action: 'GROUP_INVITE',
|
action: 'GROUP_INVITE',
|
||||||
@ -22,7 +23,9 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
}),
|
}),
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsLoadingInvite(true);
|
setIsLoadingInvite(true);
|
||||||
|
|
||||||
if (!expiryTime || !value) return;
|
if (!expiryTime || !value) return;
|
||||||
new Promise((res, rej) => {
|
new Promise((res, rej) => {
|
||||||
window
|
window
|
||||||
@ -42,7 +45,6 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
res(response);
|
res(response);
|
||||||
|
|
||||||
setValue('');
|
setValue('');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -56,7 +58,11 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: error?.message || 'An error occurred',
|
message:
|
||||||
|
error?.message ||
|
||||||
|
t('core:message.error.generic', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
rej(error);
|
rej(error);
|
||||||
@ -81,16 +87,21 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('group:action.invite_member', { postProcess: 'capitalize' })}
|
{t('group:action.invite_member', { postProcess: 'capitalize' })}
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
value={value}
|
value={value}
|
||||||
placeholder="Name or address"
|
placeholder="Name or address"
|
||||||
onChange={(e) => setValue(e.target.value)}
|
onChange={(e) => setValue(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
<Label>
|
<Label>
|
||||||
{t('group:invitation_expiry', { postProcess: 'capitalize' })}
|
{t('group:invitation_expiry', { postProcess: 'capitalize' })}
|
||||||
</Label>
|
</Label>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
labelId="demo-simple-select-label"
|
labelId="demo-simple-select-label"
|
||||||
id="demo-simple-select"
|
id="demo-simple-select"
|
||||||
@ -109,7 +120,9 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
<MenuItem value={1296000}>{t('core:time.day', { count: 15 })}</MenuItem>
|
<MenuItem value={1296000}>{t('core:time.day', { count: 15 })}</MenuItem>
|
||||||
<MenuItem value={2592000}>{t('core:time.day', { count: 30 })}</MenuItem>
|
<MenuItem value={2592000}>{t('core:time.day', { count: 30 })}</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
variant="contained"
|
variant="contained"
|
||||||
loadingPosition="start"
|
loadingPosition="start"
|
||||||
|
@ -34,7 +34,6 @@ 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 {
|
import {
|
||||||
myGroupsWhereIAmAdminAtom,
|
myGroupsWhereIAmAdminAtom,
|
||||||
promotionTimeIntervalAtom,
|
promotionTimeIntervalAtom,
|
||||||
@ -89,9 +88,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
const [promotionTimeInterval, setPromotionTimeInterval] = useAtom(
|
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);
|
||||||
const [infoSnack, setInfoSnack] = useState(null);
|
const [infoSnack, setInfoSnack] = useState(null);
|
||||||
const [fee, setFee] = useState(null);
|
const [fee, setFee] = useState(null);
|
||||||
@ -99,7 +96,6 @@ export const ListOfGroupPromotions = () => {
|
|||||||
const [isLoadingPublish, setIsLoadingPublish] = useState(false);
|
const [isLoadingPublish, setIsLoadingPublish] = useState(false);
|
||||||
const { show } = useContext(MyContext);
|
const { show } = useContext(MyContext);
|
||||||
const setTxList = useSetAtom(txListAtom);
|
const setTxList = useSetAtom(txListAtom);
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation(['core', 'group']);
|
const { t } = useTranslation(['core', 'group']);
|
||||||
const listRef = useRef(null);
|
const listRef = useRef(null);
|
||||||
@ -250,8 +246,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
});
|
});
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message:
|
message: t('group:message.success.group_promotion', {
|
||||||
'Successfully published promotion. It may take a couple of minutes for the promotion to appear',
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
setText('');
|
setText('');
|
||||||
@ -261,7 +258,10 @@ export const ListOfGroupPromotions = () => {
|
|||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message:
|
message:
|
||||||
error?.message || 'Error publishing the promotion. Please try again',
|
error?.message ||
|
||||||
|
t('group:message.error.group_promotion', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
} finally {
|
} finally {
|
||||||
@ -290,8 +290,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message:
|
message: t('group:message.success.group_join', {
|
||||||
'Successfully requested to join group. It may take a couple of minutes for the changes to propagate',
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
@ -299,8 +300,14 @@ export const ListOfGroupPromotions = () => {
|
|||||||
{
|
{
|
||||||
...response,
|
...response,
|
||||||
type: 'joined-group',
|
type: 'joined-group',
|
||||||
label: `Joined Group ${group?.groupName}: awaiting confirmation`,
|
label: t('group:message.success.group_join_label', {
|
||||||
labelDone: `Joined Group ${group?.groupName}: success!`,
|
group_name: group?.groupName,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
|
labelDone: t('group:message.success.group_join_label', {
|
||||||
|
group_name: group?.groupName,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
done: false,
|
done: false,
|
||||||
groupId,
|
groupId,
|
||||||
},
|
},
|
||||||
@ -311,15 +318,20 @@ export const ListOfGroupPromotions = () => {
|
|||||||
{
|
{
|
||||||
...response,
|
...response,
|
||||||
type: 'joined-group-request',
|
type: 'joined-group-request',
|
||||||
label: `Requested to join Group ${group?.groupName}: awaiting confirmation`,
|
label: t('group:message.success.group_join_request', {
|
||||||
labelDone: `Requested to join Group ${group?.groupName}: success!`,
|
group_name: group?.groupName,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
|
labelDone: t('group:message.success.group_join_outcome', {
|
||||||
|
group_name: group?.groupName,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
done: false,
|
done: false,
|
||||||
groupId,
|
groupId,
|
||||||
},
|
},
|
||||||
...prev,
|
...prev,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
handlePopoverClose();
|
handlePopoverClose();
|
||||||
res(response);
|
res(response);
|
||||||
@ -336,7 +348,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: error.message || 'An error occurred',
|
message:
|
||||||
|
error.message ||
|
||||||
|
t('core:message.error.generic', { postProcess: 'capitalize' }),
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
rej(error);
|
rej(error);
|
||||||
@ -385,7 +399,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
fontSize: '1rem',
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Group promotions{' '}
|
{t('group:group.promotions', { postProcess: 'capitalize' })}{' '}
|
||||||
{promotions.length > 0 && ` (${promotions.length})`}
|
{promotions.length > 0 && ` (${promotions.length})`}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
@ -444,7 +458,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add Promotion
|
{t('group.action.add_promotion', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -490,7 +504,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
color: 'rgba(255, 255, 255, 0.2)',
|
color: 'rgba(255, 255, 255, 0.2)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Nothing to display
|
{t('group.message.generic.no_display', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@ -537,23 +553,25 @@ export const ListOfGroupPromotions = () => {
|
|||||||
ref={rowVirtualizer.measureElement} //measure dynamic row height
|
ref={rowVirtualizer.measureElement} //measure dynamic row height
|
||||||
key={promotion?.identifier}
|
key={promotion?.identifier}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: '50%', // Move to the center horizontally
|
|
||||||
transform: `translateY(${virtualRow.start}px) translateX(-50%)`, // Adjust for centering
|
|
||||||
width: '100%', // Control width (90% of the parent)
|
|
||||||
padding: '10px 0',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
overscrollBehavior: 'none',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: '5px',
|
gap: '5px',
|
||||||
|
left: '50%', // Move to the center horizontally
|
||||||
|
overscrollBehavior: 'none',
|
||||||
|
padding: '10px 0',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
transform: `translateY(${virtualRow.start}px) translateX(-50%)`, // Adjust for centering
|
||||||
|
width: '100%', // Control width (90% of the parent)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ErrorBoundary
|
<ErrorBoundary
|
||||||
fallback={
|
fallback={
|
||||||
<Typography>
|
<Typography>
|
||||||
Error loading content: Invalid Data
|
{t('group.message.generic.invalid_data', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -568,7 +586,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
<Popover
|
<Popover
|
||||||
open={openPopoverIndex === promotion?.groupId}
|
open={openPopoverIndex === promotion?.groupId}
|
||||||
anchorEl={popoverAnchor}
|
anchorEl={popoverAnchor}
|
||||||
onClose={(event, reason) => {
|
onClose={(reason) => {
|
||||||
if (reason === 'backdropClick') {
|
if (reason === 'backdropClick') {
|
||||||
// Prevent closing on backdrop click
|
// Prevent closing on backdrop click
|
||||||
return;
|
return;
|
||||||
@ -603,7 +621,10 @@ export const ListOfGroupPromotions = () => {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Group name: {` ${promotion?.groupName}`}
|
{t('group:group.name', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
: {` ${promotion?.groupName}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
@ -612,8 +633,10 @@ export const ListOfGroupPromotions = () => {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Number of members:{' '}
|
{t('group:group.member_number', {
|
||||||
{` ${promotion?.memberCount}`}
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
: {` ${promotion?.memberCount}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{promotion?.description && (
|
{promotion?.description && (
|
||||||
@ -634,9 +657,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
*This is a closed/private group, so you
|
{t('group:message.generic.closed_group', {
|
||||||
will need to wait until an admin accepts
|
postProcess: 'capitalize',
|
||||||
your request
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -657,7 +680,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={handlePopoverClose}
|
onClick={handlePopoverClose}
|
||||||
>
|
>
|
||||||
Close
|
{t('core:action.close', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
|
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
@ -671,7 +696,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Join
|
{t('core:action.join', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@ -755,8 +782,12 @@ export const ListOfGroupPromotions = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{promotion?.isOpen
|
{promotion?.isOpen
|
||||||
? 'Public group'
|
? t('group:group.public', {
|
||||||
: 'Private group'}
|
postProcess: 'capitalize',
|
||||||
|
})
|
||||||
|
: t('group:group.private', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -790,7 +821,10 @@ export const ListOfGroupPromotions = () => {
|
|||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Join Group: {` ${promotion?.groupName}`}
|
{t('group:action.join_group', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
: {` ${promotion?.groupName}`}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@ -810,90 +844,114 @@ export const ListOfGroupPromotions = () => {
|
|||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
{isShowModal && (
|
<Dialog
|
||||||
<Dialog
|
open={isShowModal}
|
||||||
open={isShowModal}
|
aria-labelledby="alert-dialog-title"
|
||||||
aria-labelledby="alert-dialog-title"
|
aria-describedby="alert-dialog-description"
|
||||||
aria-describedby="alert-dialog-description"
|
>
|
||||||
>
|
<DialogTitle id="alert-dialog-title">
|
||||||
<DialogTitle id="alert-dialog-title">
|
{t('group:action.promote_group', { postProcess: 'capitalize' })}
|
||||||
{'Promote your group to non-members'}
|
</DialogTitle>
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
Only the latest promotion from the week will be shown for your
|
{t('group:message.generic.latest_promotion', {
|
||||||
group.
|
postProcess: 'capitalize',
|
||||||
</DialogContentText>
|
})}
|
||||||
<DialogContentText id="alert-dialog-description2">
|
</DialogContentText>
|
||||||
Max 200 characters. Publish Fee: {fee && fee} {' QORT'}
|
|
||||||
</DialogContentText>
|
<DialogContentText id="alert-dialog-description2">
|
||||||
<Spacer height="20px" />
|
{t('group:message.generic.max_chars', {
|
||||||
<Box
|
postProcess: 'capitalize',
|
||||||
sx={{
|
})}
|
||||||
display: 'flex',
|
: {fee && fee} {' QORT'}
|
||||||
flexDirection: 'column',
|
</DialogContentText>
|
||||||
gap: '5px',
|
|
||||||
}}
|
<Spacer height="20px" />
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '5px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Label>
|
||||||
|
{t('group:action.select_group', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<Label>
|
||||||
|
{t('group:message.generic.admin_only', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
labelId="demo-simple-select-label"
|
||||||
|
id="demo-simple-select"
|
||||||
|
value={selectedGroup}
|
||||||
|
label="Groups where you are an admin"
|
||||||
|
onChange={(e) => setSelectedGroup(e.target.value)}
|
||||||
|
variant="outlined"
|
||||||
>
|
>
|
||||||
<Label>Select a group</Label>
|
{myGroupsWhereIAmAdmin?.map((group) => {
|
||||||
<Label>Only groups where you are an admin will be shown</Label>
|
return (
|
||||||
<Select
|
<MenuItem key={group?.groupId} value={group?.groupId}>
|
||||||
labelId="demo-simple-select-label"
|
{group?.groupName}
|
||||||
id="demo-simple-select"
|
</MenuItem>
|
||||||
value={selectedGroup}
|
);
|
||||||
label="Groups where you are an admin"
|
})}
|
||||||
onChange={(e) => setSelectedGroup(e.target.value)}
|
</Select>
|
||||||
variant="outlined"
|
</Box>
|
||||||
>
|
|
||||||
{myGroupsWhereIAmAdmin?.map((group) => {
|
<Spacer height="20px" />
|
||||||
return (
|
|
||||||
<MenuItem key={group?.groupId} value={group?.groupId}>
|
<TextField
|
||||||
{group?.groupName}
|
label="Promotion text"
|
||||||
</MenuItem>
|
variant="filled"
|
||||||
);
|
fullWidth
|
||||||
})}
|
value={text}
|
||||||
</Select>
|
onChange={(e) => setText(e.target.value)}
|
||||||
</Box>
|
inputProps={{
|
||||||
<Spacer height="20px" />
|
maxLength: 200,
|
||||||
<TextField
|
}}
|
||||||
label="Promotion text"
|
multiline={true}
|
||||||
variant="filled"
|
sx={{
|
||||||
fullWidth
|
'& .MuiFormLabel-root': {
|
||||||
value={text}
|
color: theme.palette.text.primary,
|
||||||
onChange={(e) => setText(e.target.value)}
|
},
|
||||||
inputProps={{
|
'& .MuiFormLabel-root.Mui-focused': {
|
||||||
maxLength: 200,
|
color: theme.palette.text.primary,
|
||||||
}}
|
},
|
||||||
multiline={true}
|
}}
|
||||||
sx={{
|
/>
|
||||||
'& .MuiFormLabel-root': {
|
</DialogContent>
|
||||||
color: theme.palette.text.primary,
|
|
||||||
},
|
<DialogActions>
|
||||||
'& .MuiFormLabel-root.Mui-focused': {
|
<Button
|
||||||
color: theme.palette.text.primary,
|
disabled={isLoadingPublish}
|
||||||
},
|
variant="contained"
|
||||||
}}
|
onClick={() => setIsShowModal(false)}
|
||||||
/>
|
>
|
||||||
</DialogContent>
|
{t('core:action.close', {
|
||||||
<DialogActions>
|
postProcess: 'capitalize',
|
||||||
<Button
|
})}
|
||||||
disabled={isLoadingPublish}
|
</Button>
|
||||||
variant="contained"
|
<Button
|
||||||
onClick={() => setIsShowModal(false)}
|
disabled={!text.trim() || !selectedGroup || isLoadingPublish}
|
||||||
>
|
variant="contained"
|
||||||
Close
|
onClick={publishPromo}
|
||||||
</Button>
|
autoFocus
|
||||||
<Button
|
>
|
||||||
disabled={!text.trim() || !selectedGroup || isLoadingPublish}
|
{t('core:action.publish', {
|
||||||
variant="contained"
|
postProcess: 'capitalize',
|
||||||
onClick={publishPromo}
|
})}
|
||||||
autoFocus
|
</Button>
|
||||||
>
|
</DialogActions>
|
||||||
Publish
|
</Dialog>
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
)}
|
|
||||||
<CustomizedSnackbars
|
<CustomizedSnackbars
|
||||||
open={openSnack}
|
open={openSnack}
|
||||||
setOpen={setOpenSnack}
|
setOpen={setOpenSnack}
|
||||||
|
@ -36,7 +36,7 @@ export const NewUsersCTA = ({ balance }) => {
|
|||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('core:question.new_user', { postProcess: 'capitalize' })}
|
{t('core:message.question.new_user', { postProcess: 'capitalize' })}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
@ -21,12 +21,13 @@ import {
|
|||||||
subscribeToEvent,
|
subscribeToEvent,
|
||||||
unsubscribeFromEvent,
|
unsubscribeFromEvent,
|
||||||
} from '../../utils/events';
|
} from '../../utils/events';
|
||||||
import { getFee, getNameOrAddress } from '../../background';
|
import { getFee } from '../../background';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
import { FidgetSpinner } from 'react-loader-spinner';
|
import { FidgetSpinner } from 'react-loader-spinner';
|
||||||
import { useModal } from '../../common/useModal';
|
import { useModal } from '../../common/useModal';
|
||||||
import { useAtom, useSetAtom } from 'jotai';
|
import { useAtom, useSetAtom } from 'jotai';
|
||||||
import { memberGroupsAtom, txListAtom } from '../../atoms/global';
|
import { memberGroupsAtom, txListAtom } from '../../atoms/global';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
||||||
const setTxList = useSetAtom(txListAtom);
|
const setTxList = useSetAtom(txListAtom);
|
||||||
@ -44,7 +45,7 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
const { show: showKey, message } = useModal();
|
const { show: showKey, message } = useModal();
|
||||||
const { isShow: isShowNext, onOk, show: showNext } = useModal();
|
const { isShow: isShowNext, onOk, show: showNext } = useModal();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const { t } = useTranslation(['core', 'auth', 'group']);
|
||||||
const [info, setInfo] = useState(null);
|
const [info, setInfo] = useState(null);
|
||||||
const [names, setNames] = useState({});
|
const [names, setNames] = useState({});
|
||||||
const [accountInfos, setAccountInfos] = useState({});
|
const [accountInfos, setAccountInfos] = useState({});
|
||||||
@ -223,13 +224,21 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
rej({ message: response.error });
|
rej({ message: response.error });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej({ message: error.message || 'An error occurred' });
|
rej({
|
||||||
|
message:
|
||||||
|
error.message ||
|
||||||
|
t('core:message.error.generic', { postProcess: 'capitalize' }),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setInfo({
|
setInfo({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: error?.message || 'Unable to add minting account',
|
message:
|
||||||
|
error?.message ||
|
||||||
|
t('core:message.error.minting_account_add', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
} finally {
|
} finally {
|
||||||
@ -263,13 +272,21 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
rej({ message: response.error });
|
rej({ message: response.error });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej({ message: error.message || 'An error occurred' });
|
rej({
|
||||||
|
message:
|
||||||
|
error.message ||
|
||||||
|
t('core:message.error.generic', { postProcess: 'capitalize' }),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setInfo({
|
setInfo({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: error?.message || 'Unable to remove minting account',
|
message:
|
||||||
|
error?.message ||
|
||||||
|
t('core:message.error.minting_account_remove', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
} finally {
|
} finally {
|
||||||
@ -278,9 +295,13 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const createRewardShare = useCallback(async (publicKey, recipient) => {
|
const createRewardShare = useCallback(async (publicKey, recipient) => {
|
||||||
const fee = await getFee('REWARD_SHARE'); // TODO translate
|
const fee = await getFee('REWARD_SHARE');
|
||||||
await show({
|
await show({
|
||||||
message: 'Would you like to perform an REWARD_SHARE transaction?',
|
message: t('group:question.perform_transaction', {
|
||||||
|
// TODO move from group into core namespace
|
||||||
|
action: 'REWARD_SHARE',
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
return await new Promise((res, rej) => {
|
return await new Promise((res, rej) => {
|
||||||
@ -295,8 +316,12 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
recipient,
|
recipient,
|
||||||
...response,
|
...response,
|
||||||
type: 'add-rewardShare',
|
type: 'add-rewardShare',
|
||||||
label: `Add rewardshare: awaiting confirmation`,
|
label: t('group:message.success.rewardshare_add', {
|
||||||
labelDone: `Add rewardshare: success!`,
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
|
labelDone: t('group:message.success.rewardshare_add_label', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
done: false,
|
done: false,
|
||||||
},
|
},
|
||||||
...prev,
|
...prev,
|
||||||
@ -307,7 +332,11 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
rej({ message: response.error });
|
rej({ message: response.error });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej({ message: error.message || 'An error occurred' });
|
rej({
|
||||||
|
message:
|
||||||
|
error.message ||
|
||||||
|
t('core:message.error.generic', { postProcess: 'capitalize' }),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
@ -326,7 +355,11 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
rej({ message: response.error });
|
rej({ message: response.error });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej({ message: error.message || 'An error occurred' });
|
rej({
|
||||||
|
message:
|
||||||
|
error.message ||
|
||||||
|
t('core:message.error.generic', { postProcess: 'capitalize' }),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
@ -350,7 +383,9 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
await sleep(pollingInterval); // Wait before the next poll
|
await sleep(pollingInterval); // Wait before the next poll
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Timeout waiting for reward share confirmation');
|
throw new Error(
|
||||||
|
t('group:message.error.timeout_reward', { postProcess: 'capitalize' })
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const startMinting = async () => {
|
const startMinting = async () => {
|
||||||
@ -382,7 +417,11 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
setShowWaitDialog(false);
|
setShowWaitDialog(false);
|
||||||
setInfo({
|
setInfo({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: error?.message || 'Unable to start minting',
|
message:
|
||||||
|
error?.message ||
|
||||||
|
t('group:message.error.unable_minting', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
} finally {
|
} finally {
|
||||||
@ -420,8 +459,12 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
...rewardShare,
|
...rewardShare,
|
||||||
...response,
|
...response,
|
||||||
type: 'remove-rewardShare',
|
type: 'remove-rewardShare',
|
||||||
label: `Remove rewardshare: awaiting confirmation`,
|
label: t('group:message.success.rewardshare_remove', {
|
||||||
labelDone: `Remove rewardshare: success!`,
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
|
labelDone: t('group:message.success.rewardshare_remove_label', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
done: false,
|
done: false,
|
||||||
},
|
},
|
||||||
...prev,
|
...prev,
|
||||||
@ -431,59 +474,65 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
rej({ message: response.error });
|
rej({ message: response.error });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej({ message: error.message || 'An error occurred' });
|
rej({
|
||||||
|
message:
|
||||||
|
error.message ||
|
||||||
|
t('core:message.error.generic', { postProcess: 'capitalize' }),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleRemoveRewardShare = async (rewardShare) => {
|
// TODO unused functions. Remove??
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
|
|
||||||
const privateRewardShare = await removeRewardShare(rewardShare);
|
// const handleRemoveRewardShare = async (rewardShare) => {
|
||||||
} catch (error) {
|
// try {
|
||||||
setInfo({
|
// setIsLoading(true);
|
||||||
type: 'error',
|
|
||||||
message: error?.message || 'Unable to remove reward share',
|
|
||||||
});
|
|
||||||
setOpenSnack(true);
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const createRewardShareForPotentialMinter = async (receiver) => {
|
// const privateRewardShare = await removeRewardShare(rewardShare);
|
||||||
try {
|
// } catch (error) {
|
||||||
setIsLoading(true);
|
// setInfo({
|
||||||
const confirmReceiver = await getNameOrAddress(receiver);
|
// type: 'error',
|
||||||
if (confirmReceiver.error)
|
// message: error?.message || 'Unable to remove reward share',
|
||||||
throw new Error('Invalid receiver address or name');
|
// });
|
||||||
const isInMinterGroup = await checkIfMinterGroup(confirmReceiver);
|
// setOpenSnack(true);
|
||||||
if (!isInMinterGroup) throw new Error('Account not in Minter Group');
|
// } finally {
|
||||||
const publicKey = await getPublicKeyFromAddress(confirmReceiver);
|
// setIsLoading(false);
|
||||||
const findRewardShare = rewardShares?.find(
|
// }
|
||||||
(item) =>
|
// };
|
||||||
item?.recipient === confirmReceiver &&
|
|
||||||
item?.mintingAccount === myAddress
|
// const createRewardShareForPotentialMinter = async (receiver) => {
|
||||||
);
|
// try {
|
||||||
if (findRewardShare) {
|
// setIsLoading(true);
|
||||||
const privateRewardShare = await getRewardSharePrivateKey(publicKey);
|
// const confirmReceiver = await getNameOrAddress(receiver);
|
||||||
setRewardsharekey(privateRewardShare);
|
// if (confirmReceiver.error)
|
||||||
} else {
|
// throw new Error('Invalid receiver address or name');
|
||||||
await createRewardShare(publicKey, confirmReceiver);
|
// const isInMinterGroup = await checkIfMinterGroup(confirmReceiver);
|
||||||
const privateRewardShare = await getRewardSharePrivateKey(publicKey);
|
// if (!isInMinterGroup) throw new Error('Account not in Minter Group');
|
||||||
setRewardsharekey(privateRewardShare);
|
// const publicKey = await getPublicKeyFromAddress(confirmReceiver);
|
||||||
}
|
// const findRewardShare = rewardShares?.find(
|
||||||
} catch (error) {
|
// (item) =>
|
||||||
setInfo({
|
// item?.recipient === confirmReceiver &&
|
||||||
type: 'error',
|
// item?.mintingAccount === myAddress
|
||||||
message: error?.message || 'Unable to create reward share',
|
// );
|
||||||
});
|
// if (findRewardShare) {
|
||||||
setOpenSnack(true);
|
// const privateRewardShare = await getRewardSharePrivateKey(publicKey);
|
||||||
} finally {
|
// setRewardsharekey(privateRewardShare);
|
||||||
setIsLoading(false);
|
// } else {
|
||||||
}
|
// await createRewardShare(publicKey, confirmReceiver);
|
||||||
};
|
// const privateRewardShare = await getRewardSharePrivateKey(publicKey);
|
||||||
|
// setRewardsharekey(privateRewardShare);
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// setInfo({
|
||||||
|
// type: 'error',
|
||||||
|
// message: error?.message || 'Unable to create reward share',
|
||||||
|
// });
|
||||||
|
// setOpenSnack(true);
|
||||||
|
// } finally {
|
||||||
|
// setIsLoading(false);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getNodeInfos();
|
getNodeInfos();
|
||||||
@ -558,7 +607,12 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle id="alert-dialog-title">{'Manage your minting'}</DialogTitle>
|
<DialogTitle id="alert-dialog-title">
|
||||||
|
{t('group:message.generic.manage_minting', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -606,19 +660,37 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
padding: '10px',
|
padding: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>Account: {handleNames(accountInfo?.address)}</Typography>
|
|
||||||
|
|
||||||
<Typography>Level: {accountInfo?.level}</Typography>
|
|
||||||
|
|
||||||
<Typography>
|
<Typography>
|
||||||
blocks remaining until next level: {_levelUpBlocks()}
|
{t('auth:account.account_one', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
: {handleNames(accountInfo?.address)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography>
|
<Typography>
|
||||||
This node is minting: {nodeInfos?.isMintingPossible?.toString()}
|
{t('core:level', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
: {accountInfo?.level}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography>
|
||||||
|
{t('group:message.generic.next_level', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}{' '}
|
||||||
|
{_levelUpBlocks()}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography>
|
||||||
|
{t('group:message.generic.node_minting', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}{' '}
|
||||||
|
{nodeInfos?.isMintingPossible?.toString()}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
|
|
||||||
{isPartOfMintingGroup && !accountIsMinting && (
|
{isPartOfMintingGroup && !accountIsMinting && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -650,19 +722,29 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
}}
|
}}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Start minting
|
{t('core:action.start_minting', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{mintingAccounts?.length > 1 && (
|
{mintingAccounts?.length > 1 && (
|
||||||
<Typography>
|
<Typography>
|
||||||
Only 2 minting keys are allowed per node. Please remove one if
|
{t('group:message.generic.minting_keys_per_node', {
|
||||||
you would like to mint with this account.
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
|
|
||||||
{mintingAccounts?.length > 0 && (
|
{mintingAccounts?.length > 0 && (
|
||||||
<Typography>Node's minting accounts</Typography>
|
<Typography>
|
||||||
|
{t('group:message.generic.node_minting_account', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
)}
|
)}
|
||||||
<Card
|
<Card
|
||||||
sx={{
|
sx={{
|
||||||
@ -679,12 +761,15 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>
|
<Typography>
|
||||||
You currently have a minting key for this account attached to
|
{t('group:message.generic.node_minting_key', {
|
||||||
this node
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
|
|
||||||
{mintingAccounts?.map((acct) => (
|
{mintingAccounts?.map((acct) => (
|
||||||
<Box
|
<Box
|
||||||
key={acct?.mintingAccount}
|
key={acct?.mintingAccount}
|
||||||
@ -695,8 +780,12 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>
|
<Typography>
|
||||||
Minting account: {handleNames(acct?.mintingAccount)}
|
{t('group:message.generic.minting_account', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}{' '}
|
||||||
|
{handleNames(acct?.mintingAccount)}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
sx={{
|
sx={{
|
||||||
@ -717,7 +806,9 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
}}
|
}}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Remove minting account
|
{t('group:action.remove_minting_account', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
@ -728,13 +819,15 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
|
|
||||||
{mintingAccounts?.length > 1 && (
|
{mintingAccounts?.length > 1 && (
|
||||||
<Typography>
|
<Typography>
|
||||||
Only 2 minting keys are allowed per node. Please remove one if you
|
{t('group:message.generic.minting_keys_per_node_different', {
|
||||||
would like to add a different account.
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
{!isPartOfMintingGroup && (
|
{!isPartOfMintingGroup && (
|
||||||
<Card
|
<Card
|
||||||
sx={{
|
sx={{
|
||||||
@ -752,12 +845,19 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>
|
<Typography>
|
||||||
You are currently not part of the MINTER group
|
{t('group:message.generic.minter_group', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography>
|
<Typography>
|
||||||
Visit the Q-Mintership app to apply to be a minter
|
{t('group:message.generic.mintership_app', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
sx={{
|
sx={{
|
||||||
@ -781,7 +881,9 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
}}
|
}}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Visit Q-Mintership
|
{t('group:action.visit_q_mintership', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Card>
|
</Card>
|
||||||
@ -800,13 +902,16 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
<DialogContent>
|
<DialogContent>
|
||||||
{!isShowNext && (
|
{!isShowNext && (
|
||||||
<Typography>
|
<Typography>
|
||||||
Confirming creation of rewardshare on chain. Please be
|
{t('group:message.success.rewardshare_creation', {
|
||||||
patient, this could take up to 90 seconds.
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
{isShowNext && (
|
{isShowNext && (
|
||||||
<Typography>
|
<Typography>
|
||||||
Rewardshare confirmed. Please click Next.
|
{t('group:message.success.rewardshare_confirmed', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
@ -818,21 +923,23 @@ export const Minting = ({ setIsOpenMinting, myAddress, show }) => {
|
|||||||
onClick={onOk}
|
onClick={onOk}
|
||||||
autoFocus
|
autoFocus
|
||||||
>
|
>
|
||||||
Next
|
{t('core:page.next', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
// disabled={isLoadingPublish}
|
// disabled={isLoadingPublish}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => setIsOpenMinting(false)}
|
onClick={() => setIsOpenMinting(false)}
|
||||||
>
|
>
|
||||||
Close
|
{t('core:action.close', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
|
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||||
open={openSnack}
|
open={openSnack}
|
||||||
|
@ -25,6 +25,7 @@ import CheckIcon from '@mui/icons-material/Check';
|
|||||||
import ErrorIcon from '@mui/icons-material/Error';
|
import ErrorIcon from '@mui/icons-material/Error';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { txListAtom } from '../atoms/global';
|
import { txListAtom } from '../atoms/global';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
enum Availability {
|
enum Availability {
|
||||||
NULL = 'null',
|
NULL = 'null',
|
||||||
@ -50,6 +51,7 @@ export const RegisterName = ({
|
|||||||
);
|
);
|
||||||
const [nameFee, setNameFee] = useState(null);
|
const [nameFee, setNameFee] = useState(null);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const { t } = useTranslation(['core', 'auth', 'group']);
|
||||||
const checkIfNameExisits = async (name) => {
|
const checkIfNameExisits = async (name) => {
|
||||||
if (!name?.trim()) {
|
if (!name?.trim()) {
|
||||||
setIsNameAvailable(Availability.NULL);
|
setIsNameAvailable(Availability.NULL);
|
||||||
@ -110,12 +112,24 @@ export const RegisterName = ({
|
|||||||
|
|
||||||
const registerName = async () => {
|
const registerName = async () => {
|
||||||
try {
|
try {
|
||||||
if (!userInfo?.address) throw new Error('Your address was not found');
|
if (!userInfo?.address)
|
||||||
if (!registerNameValue) throw new Error('Enter a name');
|
throw new Error(
|
||||||
|
t('core:message.error.address_not_found', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (!registerNameValue)
|
||||||
|
throw new Error(
|
||||||
|
t('core:action.enter_name', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const fee = await getFee('REGISTER_NAME');
|
const fee = await getFee('REGISTER_NAME');
|
||||||
await show({
|
await show({
|
||||||
message: 'Would you like to register this name?',
|
message: t('group:question.register_name', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
publishFee: fee.fee + ' QORT',
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
setIsLoadingRegisterName(true);
|
setIsLoadingRegisterName(true);
|
||||||
@ -130,8 +144,9 @@ export const RegisterName = ({
|
|||||||
setIsLoadingRegisterName(false);
|
setIsLoadingRegisterName(false);
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message:
|
message: t('group:message.success.registered_name', {
|
||||||
'Successfully registered. It may take a couple of minutes for the changes to propagate',
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
setRegisterNameValue('');
|
setRegisterNameValue('');
|
||||||
@ -140,8 +155,15 @@ export const RegisterName = ({
|
|||||||
{
|
{
|
||||||
...response,
|
...response,
|
||||||
type: 'register-name',
|
type: 'register-name',
|
||||||
label: `Registered name: awaiting confirmation. This may take a couple minutes.`,
|
label: t('group:message.success.registered_name_label', {
|
||||||
labelDone: `Registered name: success!`,
|
postProcess: 'capitalize',
|
||||||
|
}),
|
||||||
|
labelDone: t(
|
||||||
|
'group:message.success.registered_name_success',
|
||||||
|
{
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
}
|
||||||
|
),
|
||||||
done: false,
|
done: false,
|
||||||
},
|
},
|
||||||
...prev.filter((item) => !item.done),
|
...prev.filter((item) => !item.done),
|
||||||
@ -158,7 +180,9 @@ export const RegisterName = ({
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: error.message || 'An error occurred',
|
message:
|
||||||
|
error.message ||
|
||||||
|
t('core:message.error.generic', { postProcess: 'capitalize' }),
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
rej(error);
|
rej(error);
|
||||||
@ -199,7 +223,9 @@ export const RegisterName = ({
|
|||||||
width: '400px',
|
width: '400px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>Choose a name</Label> // TODO: translate
|
<Label>
|
||||||
|
{t('core:action.choose_name', { postProcess: 'capitalize' })}
|
||||||
|
</Label>
|
||||||
<TextField
|
<TextField
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoFocus
|
autoFocus
|
||||||
@ -210,6 +236,7 @@ export const RegisterName = ({
|
|||||||
{(!balance || (nameFee && balance && balance < nameFee)) && (
|
{(!balance || (nameFee && balance && balance < nameFee)) && (
|
||||||
<>
|
<>
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -223,15 +250,20 @@ export const RegisterName = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography>
|
<Typography>
|
||||||
Your balance is {balance ?? 0} QORT. A name registration
|
{t('core:message.generic.name_registration', {
|
||||||
requires a {nameFee} QORT fee
|
balance: balance ?? 0,
|
||||||
|
fee: { nameFee },
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacer height="5px" />
|
<Spacer height="5px" />
|
||||||
|
|
||||||
{isNameAvailable === Availability.AVAILABLE && (
|
{isNameAvailable === Availability.AVAILABLE && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -245,9 +277,15 @@ export const RegisterName = ({
|
|||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography>{registerNameValue} is available</Typography>
|
<Typography>
|
||||||
|
{t('core:message.generic.name_available', {
|
||||||
|
name: registerNameValue,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isNameAvailable === Availability.NOT_AVAILABLE && (
|
{isNameAvailable === Availability.NOT_AVAILABLE && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -261,9 +299,15 @@ export const RegisterName = ({
|
|||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography>{registerNameValue} is unavailable</Typography>
|
<Typography>
|
||||||
|
{t('core:message.generic.name_unavailable', {
|
||||||
|
name: registerNameValue,
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isNameAvailable === Availability.LOADING && (
|
{isNameAvailable === Availability.LOADING && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -273,17 +317,27 @@ export const RegisterName = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BarSpinner width="16px" color={theme.palette.text.primary} />
|
<BarSpinner width="16px" color={theme.palette.text.primary} />
|
||||||
<Typography>Checking if name already existis</Typography>
|
|
||||||
|
<Typography>
|
||||||
|
{t('core:message.generic.name_checking', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacer height="25px" />
|
<Spacer height="25px" />
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
textDecoration: 'underline',
|
textDecoration: 'underline',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Benefits of a name
|
{t('core:message.generic.name_benefits', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<List
|
<List
|
||||||
sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}
|
sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}
|
||||||
aria-label="contacts"
|
aria-label="contacts"
|
||||||
@ -296,7 +350,11 @@ export const RegisterName = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary="Publish data to Qortal: anything from apps to videos. Fully decentralized!" />
|
<ListItemText
|
||||||
|
primary={t('core:message.generic.publish_data', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem disablePadding>
|
<ListItem disablePadding>
|
||||||
@ -307,7 +365,11 @@ export const RegisterName = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary="Secure ownership of data published by your name. You can even sell your name, along with your data to a third party." />
|
<ListItemText
|
||||||
|
primary={t('core:message.generic.secure_ownership', {
|
||||||
|
postProcess: 'capitalize',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
</Box>
|
</Box>
|
||||||
@ -322,7 +384,7 @@ export const RegisterName = ({
|
|||||||
setRegisterNameValue('');
|
setRegisterNameValue('');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Close
|
{t('core:action.close', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -337,7 +399,7 @@ export const RegisterName = ({
|
|||||||
onClick={registerName}
|
onClick={registerName}
|
||||||
autoFocus
|
autoFocus
|
||||||
>
|
>
|
||||||
Register Name
|
{t('core:action.register_name', { postProcess: 'capitalize' })}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
import * as React from 'react';
|
import Snackbar from '@mui/material/Snackbar';
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar';
|
|
||||||
import Alert from '@mui/material/Alert';
|
import Alert from '@mui/material/Alert';
|
||||||
|
|
||||||
export const LoadingSnackbar = ({open, info}) => {
|
export const LoadingSnackbar = ({ open, info }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Snackbar anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} open={open}>
|
<Snackbar
|
||||||
<Alert
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||||
severity="info"
|
open={open}
|
||||||
variant="filled"
|
>
|
||||||
sx={{ width: '100%' }}
|
<Alert severity="info" variant="filled" sx={{ width: '100%' }}>
|
||||||
>
|
|
||||||
{info?.message}
|
{info?.message}
|
||||||
</Alert>
|
</Alert>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar';
|
import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar';
|
||||||
import Alert from '@mui/material/Alert';
|
import Alert from '@mui/material/Alert';
|
||||||
|
|
||||||
|
@ -198,9 +198,11 @@ export const TaskManager = ({ getUserInfo }) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
|
|
||||||
<ListItemText primary="Ongoing Transactions" />
|
<ListItemText primary="Ongoing Transactions" />
|
||||||
{open ? <ExpandLess /> : <ExpandMore />}
|
{open ? <ExpandLess /> : <ExpandMore />}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
|
||||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||||
<List component="div" disablePadding>
|
<List component="div" disablePadding>
|
||||||
{txList.map((item) => (
|
{txList.map((item) => (
|
||||||
|
@ -7,11 +7,9 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events';
|
|||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
|
|
||||||
export const useHandlePaymentNotification = (address) => {
|
export const useHandlePaymentNotification = (address) => {
|
||||||
const [latestTx, setLatestTx] = useState(null);
|
|
||||||
|
|
||||||
const nameAddressOfSender = useRef({});
|
const nameAddressOfSender = useRef({});
|
||||||
const isFetchingName = useRef({});
|
const isFetchingName = useRef({});
|
||||||
|
const [latestTx, setLatestTx] = useState(null);
|
||||||
const [lastEnteredTimestampPayment, setLastEnteredTimestampPayment] = useAtom(
|
const [lastEnteredTimestampPayment, setLastEnteredTimestampPayment] = useAtom(
|
||||||
lastPaymentSeenTimestampAtom
|
lastPaymentSeenTimestampAtom
|
||||||
);
|
);
|
||||||
@ -63,6 +61,7 @@ export const useHandlePaymentNotification = (address) => {
|
|||||||
const key = `last-seen-payment-${address}`;
|
const key = `last-seen-payment-${address}`;
|
||||||
|
|
||||||
const res = await getData<any>(key).catch(() => null);
|
const res = await getData<any>(key).catch(() => null);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
setLastEnteredTimestampPayment(res);
|
setLastEnteredTimestampPayment(res);
|
||||||
}
|
}
|
||||||
@ -76,6 +75,7 @@ export const useHandlePaymentNotification = (address) => {
|
|||||||
const latestTx = responseData.filter(
|
const latestTx = responseData.filter(
|
||||||
(tx) => tx?.creatorAddress !== address && tx?.recipient === address
|
(tx) => tx?.creatorAddress !== address && tx?.recipient === address
|
||||||
)[0];
|
)[0];
|
||||||
|
|
||||||
if (!latestTx) {
|
if (!latestTx) {
|
||||||
return; // continue to the next group
|
return; // continue to the next group
|
||||||
}
|
}
|
||||||
@ -128,6 +128,7 @@ export const useHandlePaymentNotification = (address) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
}, [setLastEnteredTimestampPaymentEventFunc]);
|
}, [setLastEnteredTimestampPaymentEventFunc]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
latestTx,
|
latestTx,
|
||||||
getNameOrAddressOfSenderMiddle,
|
getNameOrAddressOfSenderMiddle,
|
||||||
|
@ -5,6 +5,7 @@ interface NameListItem {
|
|||||||
name: string;
|
name: string;
|
||||||
address: string;
|
address: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useNameSearch = (value: string, limit = 20) => {
|
export const useNameSearch = (value: string, limit = 20) => {
|
||||||
const [nameList, setNameList] = useState<NameListItem[]>([]);
|
const [nameList, setNameList] = useState<NameListItem[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@ -48,6 +49,7 @@ export const useNameSearch = (value: string, limit = 20) => {
|
|||||||
clearTimeout(handler);
|
clearTimeout(handler);
|
||||||
};
|
};
|
||||||
}, [value, limit, checkIfNameExisits]);
|
}, [value, limit, checkIfNameExisits]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLoading,
|
isLoading,
|
||||||
results: nameList,
|
results: nameList,
|
||||||
|
@ -2,7 +2,19 @@
|
|||||||
"account": {
|
"account": {
|
||||||
"your": "your account",
|
"your": "your account",
|
||||||
"account_many": "accounts",
|
"account_many": "accounts",
|
||||||
"account_one": "account"
|
"account_one": "account",
|
||||||
|
"selected": "selected account"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"add": {
|
||||||
|
"account": "add account",
|
||||||
|
"seed_phrase": "add seed-phrase"
|
||||||
|
},
|
||||||
|
"authenticate": "authenticate",
|
||||||
|
"create_account": "create account",
|
||||||
|
"choose_password": "choose new password",
|
||||||
|
"download_account": "download account",
|
||||||
|
"return_to_list": "return to list"
|
||||||
},
|
},
|
||||||
"advanced_users": "for advanced users",
|
"advanced_users": "for advanced users",
|
||||||
"apikey": {
|
"apikey": {
|
||||||
@ -13,11 +25,19 @@
|
|||||||
"key": "API key",
|
"key": "API key",
|
||||||
"select_valid": "select a valid apikey"
|
"select_valid": "select a valid apikey"
|
||||||
},
|
},
|
||||||
"authenticate": "authenticate",
|
|
||||||
"build_version": "build version",
|
"build_version": "build version",
|
||||||
"create_account": "create account",
|
"message": {
|
||||||
"download_account": "download account",
|
"error": {
|
||||||
"keep_secure": "keep your account file secure",
|
"account_creation": "could not create account."
|
||||||
|
},
|
||||||
|
"generic": {
|
||||||
|
"no_account": "No accounts saved",
|
||||||
|
"keep_secure": "keep your account file secure",
|
||||||
|
"type_seed": "type or paste in your seed-phrase",
|
||||||
|
"your_accounts": "your saved accounts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "name",
|
||||||
"node": {
|
"node": {
|
||||||
"choose": "choose custom node",
|
"choose": "choose custom node",
|
||||||
"custom_many": "custom nodes",
|
"custom_many": "custom nodes",
|
||||||
@ -26,11 +46,14 @@
|
|||||||
"using": "using node",
|
"using": "using node",
|
||||||
"using_public": "using public node"
|
"using_public": "using public node"
|
||||||
},
|
},
|
||||||
|
"note": "note",
|
||||||
"password": "password",
|
"password": "password",
|
||||||
"password_confirmation": "confirm password",
|
"password_confirmation": "confirm password",
|
||||||
"return_to_list": "return to list",
|
"seed": "seed phrase",
|
||||||
"tips": {
|
"tips": {
|
||||||
|
"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.",
|
"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.",
|
||||||
"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_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!"
|
"new_users": "new users start here!"
|
||||||
},
|
},
|
||||||
|
@ -13,13 +13,16 @@
|
|||||||
"continue": "continue",
|
"continue": "continue",
|
||||||
"continue_logout": "continue to logout",
|
"continue_logout": "continue to logout",
|
||||||
"create_thread": "create thread",
|
"create_thread": "create thread",
|
||||||
|
"choose_name": "choose a name",
|
||||||
"decline": "decline",
|
"decline": "decline",
|
||||||
"decrypt": "decrypt",
|
"decrypt": "decrypt",
|
||||||
"edit": "edit",
|
"edit": "edit",
|
||||||
|
"enter_name": "enter a name",
|
||||||
"export": "export",
|
"export": "export",
|
||||||
"import": "import",
|
"import": "import",
|
||||||
"invite": "invite",
|
"invite": "invite",
|
||||||
"join": "join",
|
"join": "join",
|
||||||
|
"login": "login",
|
||||||
"logout": "logout",
|
"logout": "logout",
|
||||||
"new": {
|
"new": {
|
||||||
"post": "new post",
|
"post": "new post",
|
||||||
@ -27,7 +30,12 @@
|
|||||||
},
|
},
|
||||||
"notify": "notify",
|
"notify": "notify",
|
||||||
"post": "post",
|
"post": "post",
|
||||||
"post_message": "post message"
|
"post_message": "post message",
|
||||||
|
"publish": "publish",
|
||||||
|
"register_name": "register name",
|
||||||
|
"remove": "remove",
|
||||||
|
"save": "save",
|
||||||
|
"start_minting": "start minting"
|
||||||
},
|
},
|
||||||
"admin": "admin",
|
"admin": "admin",
|
||||||
"core": {
|
"core": {
|
||||||
@ -51,6 +59,7 @@
|
|||||||
},
|
},
|
||||||
"general_settings": "general settings",
|
"general_settings": "general settings",
|
||||||
"last_height": "last height",
|
"last_height": "last height",
|
||||||
|
"level": "level",
|
||||||
"list": {
|
"list": {
|
||||||
"invite": "invite list",
|
"invite": "invite list",
|
||||||
"join_request": "join request list",
|
"join_request": "join request list",
|
||||||
@ -61,11 +70,26 @@
|
|||||||
"message_us": "please message us on Telegram or Discord if you need 4 QORT to start chatting without any limitations",
|
"message_us": "please message us on Telegram or Discord if you need 4 QORT to start chatting without any limitations",
|
||||||
"message": {
|
"message": {
|
||||||
"error": {
|
"error": {
|
||||||
|
"address_not_found": "your address was not found",
|
||||||
"generic": "an error occurred",
|
"generic": "an error occurred",
|
||||||
"incorrect_password": "incorrect password",
|
"incorrect_password": "incorrect password",
|
||||||
|
"minting_account_add": "unable to add minting account",
|
||||||
|
"minting_account_remove": "unable to remove minting account",
|
||||||
"missing_field": "missing: {{ field }}",
|
"missing_field": "missing: {{ field }}",
|
||||||
"save_qdn": "unable to save to QDN"
|
"save_qdn": "unable to save to QDN"
|
||||||
},
|
},
|
||||||
|
"generic": {
|
||||||
|
"name_available": "{{ name }} is available",
|
||||||
|
"name_benefits": "benefits of a name",
|
||||||
|
"name_checking": "checking if name already exists",
|
||||||
|
"name_registration": "your balance is {{ balance }} QORT. A name registration requires a {{ fee }} QORT fee",
|
||||||
|
"name_unavailable": "{{ name }} is unavailable",
|
||||||
|
"publish_data": "publish data to Qortal: anything from apps to videos. Fully decentralized!",
|
||||||
|
"secure_ownership": "secure ownership of data published by your name. You can even sell your name, along with your data to a third party."
|
||||||
|
},
|
||||||
|
"question": {
|
||||||
|
"new_user": "are you a new user?"
|
||||||
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"minting": "(minting)",
|
"minting": "(minting)",
|
||||||
"not_minting": "(not minting)",
|
"not_minting": "(not minting)",
|
||||||
@ -89,9 +113,6 @@
|
|||||||
"payment_notification": "payment notification",
|
"payment_notification": "payment notification",
|
||||||
"price": "price",
|
"price": "price",
|
||||||
"q_mail": "q-mail",
|
"q_mail": "q-mail",
|
||||||
"question": {
|
|
||||||
"new_user": "are you a new user?"
|
|
||||||
},
|
|
||||||
"save_options": {
|
"save_options": {
|
||||||
"no_pinned_changes": "you currently do not have any changes to your pinned apps",
|
"no_pinned_changes": "you currently do not have any changes to your pinned apps",
|
||||||
"overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?",
|
"overwrite_changes": "the app was unable to download your existing QDN-saved pinned apps. Would you like to overwrite those changes?",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"action": {
|
"action": {
|
||||||
|
"add_promotion": "add promotion",
|
||||||
"ban": "ban member from group",
|
"ban": "ban member from group",
|
||||||
"cancel_ban": "cancel ban",
|
"cancel_ban": "cancel ban",
|
||||||
"copy_private_key": "copy private key",
|
"copy_private_key": "copy private key",
|
||||||
@ -16,9 +17,13 @@
|
|||||||
"load_members": "load members with names",
|
"load_members": "load members with names",
|
||||||
"make_admin": "make an admin",
|
"make_admin": "make an admin",
|
||||||
"manage_members": "manage members",
|
"manage_members": "manage members",
|
||||||
|
"promote_group": "promote your group to non-members",
|
||||||
"refetch_page": "refetch page",
|
"refetch_page": "refetch page",
|
||||||
"remove_admin": "remove as admin",
|
"remove_admin": "remove as admin",
|
||||||
"return_to_thread": "return to threads"
|
"remove_minting_account": "remove minting account",
|
||||||
|
"return_to_thread": "return to threads",
|
||||||
|
"select_group": "select a group",
|
||||||
|
"visit_q_mintership": "visit Q-Mintership"
|
||||||
},
|
},
|
||||||
"advanced_options": "advanced options",
|
"advanced_options": "advanced options",
|
||||||
"approval_threshold": "group Approval Threshold (number / percentage of Admins that must approve a transaction)",
|
"approval_threshold": "group Approval Threshold (number / percentage of Admins that must approve a transaction)",
|
||||||
@ -36,6 +41,9 @@
|
|||||||
"member_number": "number of members",
|
"member_number": "number of members",
|
||||||
"name": "group name",
|
"name": "group name",
|
||||||
"open": "open (public)",
|
"open": "open (public)",
|
||||||
|
"private": "private group",
|
||||||
|
"promotions": "group promotions",
|
||||||
|
"public": "public group",
|
||||||
"type": "group type"
|
"type": "group type"
|
||||||
},
|
},
|
||||||
"invitation_expiry": "invitation Expiry Time",
|
"invitation_expiry": "invitation Expiry Time",
|
||||||
@ -46,12 +54,26 @@
|
|||||||
"latest_mails": "latest Q-Mails",
|
"latest_mails": "latest Q-Mails",
|
||||||
"message": {
|
"message": {
|
||||||
"generic": {
|
"generic": {
|
||||||
|
"admin_only": "only groups where you are an admin will be shown",
|
||||||
"already_in_group": "you are already in this group!",
|
"already_in_group": "you are already in this group!",
|
||||||
"closed_group": "this is a closed/private group, so you will need to wait until an admin accepts your request",
|
"closed_group": "this is a closed/private group, so you will need to wait until an admin accepts your request",
|
||||||
"descrypt_wallet": "decrypting wallet...",
|
"descrypt_wallet": "decrypting wallet...",
|
||||||
"encryption_key": "the group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes...",
|
"encryption_key": "the group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes...",
|
||||||
"group_invited_you": "{{group}} has invited you",
|
"group_invited_you": "{{group}} has invited you",
|
||||||
|
"invalid_data": "error loading content: Invalid Data",
|
||||||
|
"latest_promotion": "only the latest promotion from the week will be shown for your group.",
|
||||||
"loading_members": "loading member list with names... please wait.",
|
"loading_members": "loading member list with names... please wait.",
|
||||||
|
"max_chars": "max 200 characters. Publish Fee",
|
||||||
|
"manage_minting": "manage your minting",
|
||||||
|
"minter_group": "you are currently not part of the MINTER group",
|
||||||
|
"mintership_app": "visit the Q-Mintership app to apply to be a minter",
|
||||||
|
"minting_account": "minting account:",
|
||||||
|
"minting_keys_per_node": "only 2 minting keys are allowed per node. Please remove one if you would like to mint with this account.",
|
||||||
|
"minting_keys_per_node_different": "only 2 minting keys are allowed per node. Please remove one if you would like to add a different account.",
|
||||||
|
"next_level": "blocks remaining until next level:",
|
||||||
|
"node_minting": "This node is minting:",
|
||||||
|
"node_minting_account": "node's minting accounts",
|
||||||
|
"node_minting_key": "you currently have a minting key for this account attached to this node",
|
||||||
"no_display": "nothing to display",
|
"no_display": "nothing to display",
|
||||||
"no_selection": "no group selected",
|
"no_selection": "no group selected",
|
||||||
"not_part_group": "you are not part of the encrypted group of members. Wait until an admin re-encrypts the keys.",
|
"not_part_group": "you are not part of the encrypted group of members. Wait until an admin re-encrypts the keys.",
|
||||||
@ -66,10 +88,13 @@
|
|||||||
"descrypt_wallet": "error decrypting wallet {{ :errorMessage }}",
|
"descrypt_wallet": "error decrypting wallet {{ :errorMessage }}",
|
||||||
"description_required": "please provide a description",
|
"description_required": "please provide a description",
|
||||||
"group_info": "cannot access group information",
|
"group_info": "cannot access group information",
|
||||||
|
"group_promotion": "error publishing the promotion. Please try again",
|
||||||
"group_secret_key": "cannot get group secret key",
|
"group_secret_key": "cannot get group secret key",
|
||||||
"name_required": "please provide a name",
|
"name_required": "please provide a name",
|
||||||
"notify_admins": "try notifying an admin from the list of admins below:",
|
"notify_admins": "try notifying an admin from the list of admins below:",
|
||||||
"thread_id": "unable to locate thread Id"
|
"timeout_reward": "timeout waiting for reward share confirmation",
|
||||||
|
"thread_id": "unable to locate thread Id",
|
||||||
|
"unable_minting": "unable to start minting"
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"group_ban": "successfully banned member from group. It may take a couple of minutes for the changes to propagate",
|
"group_ban": "successfully banned member from group. It may take a couple of minutes for the changes to propagate",
|
||||||
@ -87,11 +112,21 @@
|
|||||||
"group_leave_name": "left group {{group_name}}: awaiting confirmation",
|
"group_leave_name": "left group {{group_name}}: awaiting confirmation",
|
||||||
"group_leave_label": "left group {{name}}: success!",
|
"group_leave_label": "left group {{name}}: success!",
|
||||||
"group_member_admin": "successfully made member an admin. It may take a couple of minutes for the changes to propagate",
|
"group_member_admin": "successfully made member an admin. It may take a couple of minutes for the changes to propagate",
|
||||||
|
"group_promotion": "successfully published promotion. It may take a couple of minutes for the promotion to appear",
|
||||||
"group_remove_member": "successfully removed member as an admin. It may take a couple of minutes for the changes to propagate",
|
"group_remove_member": "successfully removed member as an admin. It may take a couple of minutes for the changes to propagate",
|
||||||
"invitation_cancellation": "successfully canceled invitation. It may take a couple of minutes for the changes to propagate",
|
"invitation_cancellation": "successfully canceled invitation. It may take a couple of minutes for the changes to propagate",
|
||||||
"invitation_request": "accepted join request: awaiting confirmation",
|
"invitation_request": "accepted join request: awaiting confirmation",
|
||||||
"loading_threads": "loading threads... please wait.",
|
"loading_threads": "loading threads... please wait.",
|
||||||
"post_creation": "successfully created post. It may take some time for the publish to propagate",
|
"post_creation": "successfully created post. It may take some time for the publish to propagate",
|
||||||
|
"registered_name": "successfully registered. It may take a couple of minutes for the changes to propagate",
|
||||||
|
"registered_name_label": "registered name: awaiting confirmation. This may take a couple minutes.",
|
||||||
|
"registered_name_success": "registered name: success!",
|
||||||
|
"rewardshare_add": "add rewardshare: awaiting confirmation",
|
||||||
|
"rewardshare_add_label": "add rewardshare: success!",
|
||||||
|
"rewardshare_creation": "confirming creation of rewardshare on chain. Please be patient, this could take up to 90 seconds.",
|
||||||
|
"rewardshare_confirmed": "rewardshare confirmed. Please click Next.",
|
||||||
|
"rewardshare_remove": "remove rewardshare: awaiting confirmation",
|
||||||
|
"rewardshare_remove_label": "remove rewardshare: success!",
|
||||||
"thread_creation": "successfully created thread. It may take some time for the publish to propagate",
|
"thread_creation": "successfully created thread. It may take some time for the publish to propagate",
|
||||||
"unbanned_user": "successfully unbanned user. It may take a couple of minutes for the changes to propagate",
|
"unbanned_user": "successfully unbanned user. It may take a couple of minutes for the changes to propagate",
|
||||||
"user_joined": "user successfully joined!"
|
"user_joined": "user successfully joined!"
|
||||||
@ -99,7 +134,8 @@
|
|||||||
},
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"perform_transaction": "would you like to perform a {{action}} transaction?",
|
"perform_transaction": "would you like to perform a {{action}} transaction?",
|
||||||
"provide_thread": "please provide a thread title"
|
"provide_thread": "please provide a thread title",
|
||||||
|
"register_name": "would you like to register this name?"
|
||||||
},
|
},
|
||||||
"thread_posts": "new thread posts"
|
"thread_posts": "new thread posts"
|
||||||
}
|
}
|
||||||
|
@ -7,22 +7,25 @@ const keysToEncrypt = ['keyPair'];
|
|||||||
|
|
||||||
async function initializeKeyAndIV() {
|
async function initializeKeyAndIV() {
|
||||||
if (!inMemoryKey) {
|
if (!inMemoryKey) {
|
||||||
inMemoryKey = await generateKey(); // Generates the key in memory
|
inMemoryKey = await generateKey(); // Generates the key in memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateKey(): Promise<CryptoKey> {
|
async function generateKey(): Promise<CryptoKey> {
|
||||||
return await crypto.subtle.generateKey(
|
return await crypto.subtle.generateKey(
|
||||||
{
|
{
|
||||||
name: "AES-GCM",
|
name: 'AES-GCM',
|
||||||
length: 256
|
length: 256,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
["encrypt", "decrypt"]
|
['encrypt', 'decrypt']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function encryptData(data: string, key: CryptoKey): Promise<{ iv: Uint8Array; encryptedData: ArrayBuffer }> {
|
async function encryptData(
|
||||||
|
data: string,
|
||||||
|
key: CryptoKey
|
||||||
|
): Promise<{ iv: Uint8Array; encryptedData: ArrayBuffer }> {
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const encodedData = encoder.encode(data);
|
const encodedData = encoder.encode(data);
|
||||||
|
|
||||||
@ -31,8 +34,8 @@ async function encryptData(data: string, key: CryptoKey): Promise<{ iv: Uint8Arr
|
|||||||
|
|
||||||
const encryptedData = await crypto.subtle.encrypt(
|
const encryptedData = await crypto.subtle.encrypt(
|
||||||
{
|
{
|
||||||
name: "AES-GCM",
|
name: 'AES-GCM',
|
||||||
iv: iv
|
iv: iv,
|
||||||
},
|
},
|
||||||
key,
|
key,
|
||||||
encodedData
|
encodedData
|
||||||
@ -41,11 +44,15 @@ async function encryptData(data: string, key: CryptoKey): Promise<{ iv: Uint8Arr
|
|||||||
return { iv, encryptedData };
|
return { iv, encryptedData };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function decryptData(encryptedData: ArrayBuffer, key: CryptoKey, iv: Uint8Array): Promise<string> {
|
async function decryptData(
|
||||||
|
encryptedData: ArrayBuffer,
|
||||||
|
key: CryptoKey,
|
||||||
|
iv: Uint8Array
|
||||||
|
): Promise<string> {
|
||||||
const decryptedData = await crypto.subtle.decrypt(
|
const decryptedData = await crypto.subtle.decrypt(
|
||||||
{
|
{
|
||||||
name: "AES-GCM",
|
name: 'AES-GCM',
|
||||||
iv: iv
|
iv: iv,
|
||||||
},
|
},
|
||||||
key,
|
key,
|
||||||
encryptedData
|
encryptedData
|
||||||
@ -83,7 +90,10 @@ export const storeData = async (key: string, payload: any): Promise<string> => {
|
|||||||
const { iv, encryptedData } = await encryptData(base64Data, inMemoryKey);
|
const { iv, encryptedData } = await encryptData(base64Data, inMemoryKey);
|
||||||
|
|
||||||
// Combine IV and encrypted data into a single Uint8Array
|
// Combine IV and encrypted data into a single Uint8Array
|
||||||
const combinedData = new Uint8Array([...iv, ...new Uint8Array(encryptedData)]);
|
const combinedData = new Uint8Array([
|
||||||
|
...iv,
|
||||||
|
...new Uint8Array(encryptedData),
|
||||||
|
]);
|
||||||
const encryptedBase64Data = btoa(String.fromCharCode(...combinedData));
|
const encryptedBase64Data = btoa(String.fromCharCode(...combinedData));
|
||||||
await SecureStoragePlugin.set({ key, value: encryptedBase64Data });
|
await SecureStoragePlugin.set({ key, value: encryptedBase64Data });
|
||||||
} else {
|
} else {
|
||||||
@ -91,10 +101,9 @@ export const storeData = async (key: string, payload: any): Promise<string> => {
|
|||||||
await SecureStoragePlugin.set({ key, value: base64Data });
|
await SecureStoragePlugin.set({ key, value: base64Data });
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Data saved successfully";
|
return 'Data saved successfully';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const getData = async <T = any>(key: string): Promise<T | null> => {
|
export const getData = async <T = any>(key: string): Promise<T | null> => {
|
||||||
await initializeKeyAndIV();
|
await initializeKeyAndIV();
|
||||||
|
|
||||||
@ -105,13 +114,17 @@ export const getData = async <T = any>(key: string): Promise<T | null> => {
|
|||||||
if (keysToEncrypt.includes(key) && inMemoryKey) {
|
if (keysToEncrypt.includes(key) && inMemoryKey) {
|
||||||
// Decode the Base64-encoded encrypted data
|
// Decode the Base64-encoded encrypted data
|
||||||
const combinedData = atob(storedDataBase64.value)
|
const combinedData = atob(storedDataBase64.value)
|
||||||
.split("")
|
.split('')
|
||||||
.map((c) => c.charCodeAt(0));
|
.map((c) => c.charCodeAt(0));
|
||||||
|
|
||||||
const iv = new Uint8Array(combinedData.slice(0, 12)); // First 12 bytes are the IV
|
const iv = new Uint8Array(combinedData.slice(0, 12)); // First 12 bytes are the IV
|
||||||
const encryptedData = new Uint8Array(combinedData.slice(12)).buffer;
|
const encryptedData = new Uint8Array(combinedData.slice(12)).buffer;
|
||||||
|
|
||||||
const decryptedBase64Data = await decryptData(encryptedData, inMemoryKey, iv);
|
const decryptedBase64Data = await decryptData(
|
||||||
|
encryptedData,
|
||||||
|
inMemoryKey,
|
||||||
|
iv
|
||||||
|
);
|
||||||
return base64ToJson(decryptedBase64Data);
|
return base64ToJson(decryptedBase64Data);
|
||||||
} else {
|
} else {
|
||||||
// Decode non-encrypted data
|
// Decode non-encrypted data
|
||||||
@ -121,21 +134,21 @@ export const getData = async <T = any>(key: string): Promise<T | null> => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Remove keys from storage and log out
|
// Remove keys from storage and log out
|
||||||
export async function removeKeysAndLogout(keys: string[], event: MessageEvent, request: any) {
|
export async function removeKeysAndLogout(
|
||||||
|
keys: string[],
|
||||||
|
event: MessageEvent,
|
||||||
|
request: any
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
try {
|
try {
|
||||||
await SecureStoragePlugin.remove({ key });
|
await SecureStoragePlugin.remove({ key });
|
||||||
await SecureStoragePlugin.remove({ key: `${key}_iv` }); // Remove associated IV
|
await SecureStoragePlugin.remove({ key: `${key}_iv` }); // Remove associated IV
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Key not found: ${key}`);
|
console.warn(`Key not found: ${key}`);
|
||||||
}
|
}
|
||||||
@ -144,13 +157,13 @@ export async function removeKeysAndLogout(keys: string[], event: MessageEvent, r
|
|||||||
event.source.postMessage(
|
event.source.postMessage(
|
||||||
{
|
{
|
||||||
requestId: request.requestId,
|
requestId: request.requestId,
|
||||||
action: "logout",
|
action: 'logout',
|
||||||
payload: true,
|
payload: true,
|
||||||
type: "backgroundMessageResponse",
|
type: 'backgroundMessageResponse',
|
||||||
},
|
},
|
||||||
event.origin
|
event.origin
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error removing keys:", error);
|
console.error('Error removing keys:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
export function decodeIfEncoded(input) {
|
export function decodeIfEncoded(input) {
|
||||||
try {
|
try {
|
||||||
// Check if input is URI-encoded by encoding and decoding
|
// Check if input is URI-encoded by encoding and decoding
|
||||||
const encoded = encodeURIComponent(decodeURIComponent(input));
|
const encoded = encodeURIComponent(decodeURIComponent(input));
|
||||||
if (encoded === input) {
|
if (encoded === input) {
|
||||||
// Input is URI-encoded, so decode it
|
// Input is URI-encoded, so decode it
|
||||||
return decodeURIComponent(input);
|
return decodeURIComponent(input);
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// decodeURIComponent throws an error if input is not encoded
|
|
||||||
console.error("Error decoding URI:", e);
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
// Return input as-is if not URI-encoded
|
// decodeURIComponent throws an error if input is not encoded
|
||||||
return input;
|
console.error('Error decoding URI:', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isValidBase64 = (str: string): boolean => {
|
// Return input as-is if not URI-encoded
|
||||||
if (typeof str !== "string" || str.length % 4 !== 0) return false;
|
return input;
|
||||||
|
}
|
||||||
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
||||||
return base64Regex.test(str);
|
export const isValidBase64 = (str: string): boolean => {
|
||||||
};
|
if (typeof str !== 'string' || str.length % 4 !== 0) return false;
|
||||||
|
|
||||||
export const isValidBase64WithDecode = (str: string): boolean => {
|
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
||||||
try {
|
return base64Regex.test(str);
|
||||||
return isValidBase64(str) && Boolean(atob(str));
|
};
|
||||||
} catch {
|
|
||||||
return false;
|
export const isValidBase64WithDecode = (str: string): boolean => {
|
||||||
}
|
try {
|
||||||
};
|
return isValidBase64(str) && Boolean(atob(str));
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -1,38 +1,59 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import Base58 from '../deps/Base58'
|
import Base58 from '../deps/Base58';
|
||||||
import ed2curve from '../deps/ed2curve'
|
import ed2curve from '../deps/ed2curve';
|
||||||
import nacl from '../deps/nacl-fast'
|
import nacl from '../deps/nacl-fast';
|
||||||
import {Sha256} from 'asmcrypto.js'
|
import { Sha256 } from 'asmcrypto.js';
|
||||||
|
|
||||||
|
export const decryptChatMessage = (
|
||||||
|
encryptedMessage,
|
||||||
|
privateKey,
|
||||||
|
recipientPublicKey,
|
||||||
|
lastReference
|
||||||
|
) => {
|
||||||
|
const test = encryptedMessage;
|
||||||
|
let _encryptedMessage = atob(encryptedMessage);
|
||||||
|
const binaryLength = _encryptedMessage.length;
|
||||||
|
const bytes = new Uint8Array(binaryLength);
|
||||||
|
|
||||||
export const decryptChatMessage = (encryptedMessage, privateKey, recipientPublicKey, lastReference) => {
|
for (let i = 0; i < binaryLength; i++) {
|
||||||
const test = encryptedMessage
|
bytes[i] = _encryptedMessage.charCodeAt(i);
|
||||||
let _encryptedMessage = atob(encryptedMessage)
|
}
|
||||||
const binaryLength = _encryptedMessage.length
|
|
||||||
const bytes = new Uint8Array(binaryLength)
|
|
||||||
|
|
||||||
for (let i = 0; i < binaryLength; i++) {
|
const _base58RecipientPublicKey =
|
||||||
bytes[i] = _encryptedMessage.charCodeAt(i)
|
recipientPublicKey instanceof Uint8Array
|
||||||
}
|
? Base58.encode(recipientPublicKey)
|
||||||
|
: recipientPublicKey;
|
||||||
|
const _recipientPublicKey = Base58.decode(_base58RecipientPublicKey);
|
||||||
|
|
||||||
|
const _lastReference =
|
||||||
const _base58RecipientPublicKey = recipientPublicKey instanceof Uint8Array ? Base58.encode(recipientPublicKey) : recipientPublicKey
|
lastReference instanceof Uint8Array
|
||||||
const _recipientPublicKey = Base58.decode(_base58RecipientPublicKey)
|
? lastReference
|
||||||
|
: Base58.decode(lastReference);
|
||||||
|
|
||||||
const _lastReference = lastReference instanceof Uint8Array ? lastReference : Base58.decode(lastReference)
|
const convertedPrivateKey = ed2curve.convertSecretKey(privateKey);
|
||||||
|
const convertedPublicKey = ed2curve.convertPublicKey(_recipientPublicKey);
|
||||||
|
const sharedSecret = new Uint8Array(32);
|
||||||
|
nacl.lowlevel.crypto_scalarmult(
|
||||||
|
sharedSecret,
|
||||||
|
convertedPrivateKey,
|
||||||
|
convertedPublicKey
|
||||||
|
);
|
||||||
|
|
||||||
const convertedPrivateKey = ed2curve.convertSecretKey(privateKey)
|
const _chatEncryptionSeed = new Sha256()
|
||||||
const convertedPublicKey = ed2curve.convertPublicKey(_recipientPublicKey)
|
.process(sharedSecret)
|
||||||
const sharedSecret = new Uint8Array(32);
|
.finish().result;
|
||||||
nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedPublicKey)
|
const _decryptedMessage = nacl.secretbox.open(
|
||||||
|
bytes,
|
||||||
|
_lastReference.slice(0, 24),
|
||||||
|
_chatEncryptionSeed
|
||||||
|
);
|
||||||
|
|
||||||
const _chatEncryptionSeed = new Sha256().process(sharedSecret).finish().result
|
let decryptedMessage = '';
|
||||||
const _decryptedMessage = nacl.secretbox.open(bytes, _lastReference.slice(0, 24), _chatEncryptionSeed)
|
|
||||||
|
|
||||||
let decryptedMessage = ''
|
_decryptedMessage === false
|
||||||
|
? decryptedMessage
|
||||||
|
: (decryptedMessage = new TextDecoder('utf-8').decode(_decryptedMessage));
|
||||||
|
|
||||||
_decryptedMessage === false ? decryptedMessage : decryptedMessage = new TextDecoder('utf-8').decode(_decryptedMessage)
|
return decryptedMessage;
|
||||||
|
};
|
||||||
return decryptedMessage
|
|
||||||
}
|
|
||||||
|
@ -1,33 +1,38 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { crypto } from '../constants/decryptWallet'
|
import { crypto } from '../constants/decryptWallet';
|
||||||
import Base58 from '../deps/Base58'
|
import Base58 from '../deps/Base58';
|
||||||
import {AES_CBC, HmacSha512} from 'asmcrypto.js'
|
import { AES_CBC, HmacSha512 } from 'asmcrypto.js';
|
||||||
import { doInitWorkers, kdf } from '../deps/kdf'
|
import { doInitWorkers, kdf } from '../deps/kdf';
|
||||||
|
|
||||||
|
|
||||||
export const decryptStoredWallet = async (password, wallet) => {
|
export const decryptStoredWallet = async (password, wallet) => {
|
||||||
const threads = doInitWorkers(crypto.kdfThreads)
|
const threads = doInitWorkers(crypto.kdfThreads);
|
||||||
const encryptedSeedBytes = Base58.decode(wallet.encryptedSeed)
|
const encryptedSeedBytes = Base58.decode(wallet.encryptedSeed);
|
||||||
const iv = Base58.decode(wallet.iv)
|
const iv = Base58.decode(wallet.iv);
|
||||||
const salt = Base58.decode(wallet.salt)
|
const salt = Base58.decode(wallet.salt);
|
||||||
|
|
||||||
const key = await kdf(password, salt, threads)
|
const key = await kdf(password, salt, threads);
|
||||||
const encryptionKey = key.slice(0, 32)
|
const encryptionKey = key.slice(0, 32);
|
||||||
const macKey = key.slice(32, 63)
|
const macKey = key.slice(32, 63);
|
||||||
const mac = new HmacSha512(macKey).process(encryptedSeedBytes).finish().result
|
const mac = new HmacSha512(macKey)
|
||||||
if (Base58.encode(mac) !== wallet.mac) {
|
.process(encryptedSeedBytes)
|
||||||
throw new Error("Incorrect password")
|
.finish().result;
|
||||||
}
|
if (Base58.encode(mac) !== wallet.mac) {
|
||||||
const decryptedBytes = AES_CBC.decrypt(encryptedSeedBytes, encryptionKey, false, iv)
|
throw new Error('Incorrect password');
|
||||||
return decryptedBytes
|
}
|
||||||
}
|
const decryptedBytes = AES_CBC.decrypt(
|
||||||
|
encryptedSeedBytes,
|
||||||
|
encryptionKey,
|
||||||
|
false,
|
||||||
|
iv
|
||||||
|
);
|
||||||
|
return decryptedBytes;
|
||||||
|
};
|
||||||
|
|
||||||
export const decryptStoredWalletFromSeedPhrase = async (password) => {
|
export const decryptStoredWalletFromSeedPhrase = async (password) => {
|
||||||
const threads = doInitWorkers(crypto.kdfThreads)
|
const threads = doInitWorkers(crypto.kdfThreads);
|
||||||
const salt = new Uint8Array(void 0)
|
const salt = new Uint8Array(void 0);
|
||||||
|
|
||||||
|
const seed = await kdf(password, salt, threads);
|
||||||
const seed = await kdf(password, salt, threads)
|
return seed;
|
||||||
return seed
|
};
|
||||||
}
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
export const executeEvent = (eventName: string, data: any)=> {
|
export const executeEvent = (eventName: string, data: any) => {
|
||||||
const event = new CustomEvent(eventName, {detail: data})
|
const event = new CustomEvent(eventName, { detail: data });
|
||||||
document.dispatchEvent(event)
|
document.dispatchEvent(event);
|
||||||
}
|
};
|
||||||
export const subscribeToEvent = (eventName: string, listener: any)=> {
|
|
||||||
document.addEventListener(eventName, listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const unsubscribeFromEvent = (eventName: string, listener: any)=> {
|
export const subscribeToEvent = (eventName: string, listener: any) => {
|
||||||
document.removeEventListener(eventName, listener)
|
document.addEventListener(eventName, listener);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export const unsubscribeFromEvent = (eventName: string, listener: any) => {
|
||||||
|
document.removeEventListener(eventName, listener);
|
||||||
|
};
|
||||||
|
@ -1,34 +1,28 @@
|
|||||||
import moment from "moment";
|
export const delay = (time: number) =>
|
||||||
|
new Promise((_, reject) =>
|
||||||
export const delay = (time: number) => new Promise((_, reject) =>
|
|
||||||
setTimeout(() => reject(new Error('Request timed out')), time)
|
setTimeout(() => reject(new Error('Request timed out')), time)
|
||||||
);
|
);
|
||||||
|
|
||||||
// const originalHtml = `<p>---------- Forwarded message ---------</p><p>From: Alex</p><p>Date: Mon, Jun 9 2014 9:32 PM</p><p>Subject: Batteries </p><p>To: Jessica</p><p><br></p><p><br></p>`;
|
|
||||||
|
|
||||||
|
|
||||||
// export function updateMessageDetails(newFrom: string, newDateMillis: number, newTo: string) {
|
|
||||||
// let htmlString = originalHtml
|
|
||||||
// // Use Moment.js to format the date from milliseconds
|
|
||||||
// const formattedDate = moment(newDateMillis).format('ddd, MMM D YYYY h:mm A');
|
|
||||||
|
|
||||||
// // Replace the From, Date, and To fields in the HTML string
|
|
||||||
// htmlString = htmlString.replace(/<p>From:.*?<\/p>/, `<p>From: ${newFrom}</p>`);
|
|
||||||
// htmlString = htmlString.replace(/<p>Date:.*?<\/p>/, `<p>Date: ${formattedDate}</p>`);
|
|
||||||
// htmlString = htmlString.replace(/<p>To:.*?<\/p>/, `<p>To: ${newTo}</p>`);
|
|
||||||
|
|
||||||
// return htmlString;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const originalHtml = `<p>---------- Forwarded message ---------</p><p>From: Alex</p><p>Subject: Batteries </p><p>To: Jessica</p><p><br></p><p><br></p>`;
|
const originalHtml = `<p>---------- Forwarded message ---------</p><p>From: Alex</p><p>Subject: Batteries </p><p>To: Jessica</p><p><br></p><p><br></p>`;
|
||||||
|
|
||||||
|
export function updateMessageDetails(
|
||||||
|
newFrom: string,
|
||||||
|
newSubject: string,
|
||||||
|
newTo: string
|
||||||
|
) {
|
||||||
|
let htmlString = originalHtml;
|
||||||
|
|
||||||
export function updateMessageDetails(newFrom: string, newSubject: string, newTo: string) {
|
htmlString = htmlString.replace(
|
||||||
let htmlString = originalHtml
|
/<p>From:.*?<\/p>/,
|
||||||
|
`<p>From: ${newFrom}</p>`
|
||||||
|
);
|
||||||
|
|
||||||
htmlString = htmlString.replace(/<p>From:.*?<\/p>/, `<p>From: ${newFrom}</p>`);
|
htmlString = htmlString.replace(
|
||||||
htmlString = htmlString.replace(/<p>Subject:.*?<\/p>/, `<p>Subject: ${newSubject}</p>`);
|
/<p>Subject:.*?<\/p>/,
|
||||||
htmlString = htmlString.replace(/<p>To:.*?<\/p>/, `<p>To: ${newTo}</p>`);
|
`<p>Subject: ${newSubject}</p>`
|
||||||
|
);
|
||||||
|
|
||||||
return htmlString;
|
htmlString = htmlString.replace(/<p>To:.*?<\/p>/, `<p>To: ${newTo}</p>`);
|
||||||
}
|
|
||||||
|
return htmlString;
|
||||||
|
}
|
||||||
|
@ -1,90 +1,107 @@
|
|||||||
import { openIndexedDB } from "../components/Apps/useQortalMessageListener";
|
import { openIndexedDB } from '../components/Apps/useQortalMessageListener';
|
||||||
import { fileToBase64 } from "./fileReading";
|
import { fileToBase64 } from './fileReading';
|
||||||
|
|
||||||
export async function handleGetFileFromIndexedDB(event) {
|
export async function handleGetFileFromIndexedDB(event) {
|
||||||
try {
|
try {
|
||||||
const { fileId, requestId } = event.data;
|
const { fileId, requestId } = event.data;
|
||||||
const db = await openIndexedDB();
|
const db = await openIndexedDB();
|
||||||
const transaction = db.transaction(["files"], "readonly");
|
const transaction = db.transaction(['files'], 'readonly');
|
||||||
const objectStore = transaction.objectStore("files");
|
const objectStore = transaction.objectStore('files');
|
||||||
|
|
||||||
const getRequest = objectStore.get(fileId);
|
|
||||||
|
|
||||||
getRequest.onsuccess = async function (event) {
|
|
||||||
if (getRequest.result) {
|
|
||||||
const file = getRequest.result.data;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const base64String = await fileToBase64(file);
|
|
||||||
|
|
||||||
// Create a new transaction to delete the file
|
|
||||||
const deleteTransaction = db.transaction(["files"], "readwrite");
|
|
||||||
const deleteObjectStore = deleteTransaction.objectStore("files");
|
|
||||||
const deleteRequest = deleteObjectStore.delete(fileId);
|
|
||||||
|
|
||||||
deleteRequest.onsuccess = function () {
|
|
||||||
try {
|
|
||||||
const targetOrigin = window.location.origin;
|
|
||||||
|
|
||||||
window.postMessage(
|
const getRequest = objectStore.get(fileId);
|
||||||
{ action: "getFileFromIndexedDBResponse", requestId, result: base64String },
|
|
||||||
targetOrigin
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.log('error', error)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteRequest.onerror = function () {
|
|
||||||
console.error(`Error deleting file with ID ${fileId} from IndexedDB`);
|
|
||||||
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error converting file to Base64:", error);
|
|
||||||
event.ports[0].postMessage({
|
|
||||||
result: null,
|
|
||||||
error: "Failed to convert file to Base64",
|
|
||||||
});
|
|
||||||
const targetOrigin = window.location.origin;
|
|
||||||
|
|
||||||
window.postMessage(
|
getRequest.onsuccess = async function (event) {
|
||||||
{ action: "getFileFromIndexedDBResponse", requestId, result: null,
|
if (getRequest.result) {
|
||||||
error: "Failed to convert file to Base64"
|
const file = getRequest.result.data;
|
||||||
},
|
|
||||||
targetOrigin
|
try {
|
||||||
|
const base64String = await fileToBase64(file);
|
||||||
|
|
||||||
|
// Create a new transaction to delete the file
|
||||||
|
const deleteTransaction = db.transaction(['files'], 'readwrite');
|
||||||
|
const deleteObjectStore = deleteTransaction.objectStore('files');
|
||||||
|
const deleteRequest = deleteObjectStore.delete(fileId);
|
||||||
|
|
||||||
|
deleteRequest.onsuccess = function () {
|
||||||
|
try {
|
||||||
|
const targetOrigin = window.location.origin;
|
||||||
|
|
||||||
|
window.postMessage(
|
||||||
|
{
|
||||||
|
action: 'getFileFromIndexedDBResponse',
|
||||||
|
requestId,
|
||||||
|
result: base64String,
|
||||||
|
},
|
||||||
|
targetOrigin
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log('error', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
deleteRequest.onerror = function () {
|
||||||
|
console.error(
|
||||||
|
`Error deleting file with ID ${fileId} from IndexedDB`
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
} else {
|
} catch (error) {
|
||||||
console.error(`File with ID ${fileId} not found in IndexedDB`);
|
console.error('Error converting file to Base64:', error);
|
||||||
|
event.ports[0].postMessage({
|
||||||
|
result: null,
|
||||||
|
error: 'Failed to convert file to Base64',
|
||||||
|
});
|
||||||
const targetOrigin = window.location.origin;
|
const targetOrigin = window.location.origin;
|
||||||
|
|
||||||
window.postMessage(
|
window.postMessage(
|
||||||
{ action: "getFileFromIndexedDBResponse", requestId, result: null,
|
{
|
||||||
error: 'File not found in IndexedDB'
|
action: 'getFileFromIndexedDBResponse',
|
||||||
},
|
requestId,
|
||||||
targetOrigin
|
result: null,
|
||||||
|
error: 'Failed to convert file to Base64',
|
||||||
|
},
|
||||||
|
targetOrigin
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
} else {
|
||||||
|
console.error(`File with ID ${fileId} not found in IndexedDB`);
|
||||||
getRequest.onerror = function () {
|
const targetOrigin = window.location.origin;
|
||||||
console.error(`Error retrieving file with ID ${fileId} from IndexedDB`);
|
|
||||||
|
window.postMessage(
|
||||||
event.source.postMessage(
|
{
|
||||||
{ action: "getFileFromIndexedDBResponse", requestId, result: null,
|
action: 'getFileFromIndexedDBResponse',
|
||||||
error: 'Error retrieving file from IndexedDB'
|
requestId,
|
||||||
},
|
result: null,
|
||||||
event.origin
|
error: 'File not found in IndexedDB',
|
||||||
|
},
|
||||||
|
targetOrigin
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
} catch (error) {
|
};
|
||||||
const { requestId } = event.data;
|
|
||||||
console.error("Error opening IndexedDB:", error);
|
getRequest.onerror = function () {
|
||||||
|
console.error(`Error retrieving file with ID ${fileId} from IndexedDB`);
|
||||||
|
|
||||||
event.source.postMessage(
|
event.source.postMessage(
|
||||||
{ action: "getFileFromIndexedDBResponse", requestId, result: null,
|
{
|
||||||
error: 'Error opening IndexedDB'
|
action: 'getFileFromIndexedDBResponse',
|
||||||
},
|
requestId,
|
||||||
|
result: null,
|
||||||
|
error: 'Error retrieving file from IndexedDB',
|
||||||
|
},
|
||||||
event.origin
|
event.origin
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
} catch (error) {
|
||||||
|
const { requestId } = event.data;
|
||||||
|
console.error('Error opening IndexedDB:', error);
|
||||||
|
event.source.postMessage(
|
||||||
|
{
|
||||||
|
action: 'getFileFromIndexedDBResponse',
|
||||||
|
requestId,
|
||||||
|
result: null,
|
||||||
|
error: 'Error opening IndexedDB',
|
||||||
|
},
|
||||||
|
event.origin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,123 +1,125 @@
|
|||||||
export const mimeToExtensionMap = {
|
export const mimeToExtensionMap = {
|
||||||
// Documents
|
// Documents
|
||||||
"application/pdf": ".pdf",
|
'application/pdf': '.pdf',
|
||||||
"application/msword": ".doc",
|
'application/msword': '.doc',
|
||||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
|
||||||
"application/vnd.ms-excel": ".xls",
|
'.docx',
|
||||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
|
'application/vnd.ms-excel': '.xls',
|
||||||
"application/vnd.ms-powerpoint": ".ppt",
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx',
|
||||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
|
'application/vnd.ms-powerpoint': '.ppt',
|
||||||
"application/vnd.oasis.opendocument.text": ".odt",
|
'application/vnd.openxmlformats-officedocument.presentationml.presentation':
|
||||||
"application/vnd.oasis.opendocument.spreadsheet": ".ods",
|
'.pptx',
|
||||||
"application/vnd.oasis.opendocument.presentation": ".odp",
|
'application/vnd.oasis.opendocument.text': '.odt',
|
||||||
"text/plain": ".txt",
|
'application/vnd.oasis.opendocument.spreadsheet': '.ods',
|
||||||
"text/csv": ".csv",
|
'application/vnd.oasis.opendocument.presentation': '.odp',
|
||||||
"application/xhtml+xml": ".xhtml",
|
'text/plain': '.txt',
|
||||||
"application/xml": ".xml",
|
'text/csv': '.csv',
|
||||||
"application/rtf": ".rtf",
|
'application/xhtml+xml': '.xhtml',
|
||||||
"application/vnd.apple.pages": ".pages",
|
'application/xml': '.xml',
|
||||||
"application/vnd.google-apps.document": ".gdoc",
|
'application/rtf': '.rtf',
|
||||||
"application/vnd.google-apps.spreadsheet": ".gsheet",
|
'application/vnd.apple.pages': '.pages',
|
||||||
"application/vnd.google-apps.presentation": ".gslides",
|
'application/vnd.google-apps.document': '.gdoc',
|
||||||
|
'application/vnd.google-apps.spreadsheet': '.gsheet',
|
||||||
|
'application/vnd.google-apps.presentation': '.gslides',
|
||||||
|
|
||||||
// Images
|
// Images
|
||||||
"image/jpeg": ".jpg",
|
'image/jpeg': '.jpg',
|
||||||
"image/png": ".png",
|
'image/png': '.png',
|
||||||
"image/gif": ".gif",
|
'image/gif': '.gif',
|
||||||
"image/webp": ".webp",
|
'image/webp': '.webp',
|
||||||
"image/svg+xml": ".svg",
|
'image/svg+xml': '.svg',
|
||||||
"image/tiff": ".tif",
|
'image/tiff': '.tif',
|
||||||
"image/bmp": ".bmp",
|
'image/bmp': '.bmp',
|
||||||
"image/x-icon": ".ico",
|
'image/x-icon': '.ico',
|
||||||
"image/heic": ".heic",
|
'image/heic': '.heic',
|
||||||
"image/heif": ".heif",
|
'image/heif': '.heif',
|
||||||
"image/apng": ".apng",
|
'image/apng': '.apng',
|
||||||
"image/avif": ".avif",
|
'image/avif': '.avif',
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
"audio/mpeg": ".mp3",
|
'audio/mpeg': '.mp3',
|
||||||
"audio/ogg": ".ogg",
|
'audio/ogg': '.ogg',
|
||||||
"audio/wav": ".wav",
|
'audio/wav': '.wav',
|
||||||
"audio/webm": ".weba",
|
'audio/webm': '.weba',
|
||||||
"audio/aac": ".aac",
|
'audio/aac': '.aac',
|
||||||
"audio/flac": ".flac",
|
'audio/flac': '.flac',
|
||||||
"audio/x-m4a": ".m4a",
|
'audio/x-m4a': '.m4a',
|
||||||
"audio/x-ms-wma": ".wma",
|
'audio/x-ms-wma': '.wma',
|
||||||
"audio/midi": ".midi",
|
'audio/midi': '.midi',
|
||||||
"audio/x-midi": ".mid",
|
'audio/x-midi': '.mid',
|
||||||
|
|
||||||
// Video
|
// Video
|
||||||
"video/mp4": ".mp4",
|
'video/mp4': '.mp4',
|
||||||
"video/webm": ".webm",
|
'video/webm': '.webm',
|
||||||
"video/ogg": ".ogv",
|
'video/ogg': '.ogv',
|
||||||
"video/x-msvideo": ".avi",
|
'video/x-msvideo': '.avi',
|
||||||
"video/quicktime": ".mov",
|
'video/quicktime': '.mov',
|
||||||
"video/x-ms-wmv": ".wmv",
|
'video/x-ms-wmv': '.wmv',
|
||||||
"video/mpeg": ".mpeg",
|
'video/mpeg': '.mpeg',
|
||||||
"video/3gpp": ".3gp",
|
'video/3gpp': '.3gp',
|
||||||
"video/3gpp2": ".3g2",
|
'video/3gpp2': '.3g2',
|
||||||
"video/x-matroska": ".mkv",
|
'video/x-matroska': '.mkv',
|
||||||
"video/x-flv": ".flv",
|
'video/x-flv': '.flv',
|
||||||
"video/x-ms-asf": ".asf",
|
'video/x-ms-asf': '.asf',
|
||||||
|
|
||||||
// Archives
|
// Archives
|
||||||
"application/zip": ".zip",
|
'application/zip': '.zip',
|
||||||
"application/x-rar-compressed": ".rar",
|
'application/x-rar-compressed': '.rar',
|
||||||
"application/x-tar": ".tar",
|
'application/x-tar': '.tar',
|
||||||
"application/x-7z-compressed": ".7z",
|
'application/x-7z-compressed': '.7z',
|
||||||
"application/x-gzip": ".gz",
|
'application/x-gzip': '.gz',
|
||||||
"application/x-bzip2": ".bz2",
|
'application/x-bzip2': '.bz2',
|
||||||
"application/x-apple-diskimage": ".dmg",
|
'application/x-apple-diskimage': '.dmg',
|
||||||
"application/vnd.android.package-archive": ".apk",
|
'application/vnd.android.package-archive': '.apk',
|
||||||
"application/x-iso9660-image": ".iso",
|
'application/x-iso9660-image': '.iso',
|
||||||
|
|
||||||
// Code Files
|
// Code Files
|
||||||
"text/javascript": ".js",
|
'text/javascript': '.js',
|
||||||
"text/css": ".css",
|
'text/css': '.css',
|
||||||
"text/html": ".html",
|
'text/html': '.html',
|
||||||
"application/json": ".json",
|
'application/json': '.json',
|
||||||
"text/xml": ".xml",
|
'text/xml': '.xml',
|
||||||
"application/x-sh": ".sh",
|
'application/x-sh': '.sh',
|
||||||
"application/x-csh": ".csh",
|
'application/x-csh': '.csh',
|
||||||
"text/x-python": ".py",
|
'text/x-python': '.py',
|
||||||
"text/x-java-source": ".java",
|
'text/x-java-source': '.java',
|
||||||
"application/java-archive": ".jar",
|
'application/java-archive': '.jar',
|
||||||
"application/vnd.microsoft.portable-executable": ".exe",
|
'application/vnd.microsoft.portable-executable': '.exe',
|
||||||
"application/x-msdownload": ".msi",
|
'application/x-msdownload': '.msi',
|
||||||
"text/x-c": ".c",
|
'text/x-c': '.c',
|
||||||
"text/x-c++": ".cpp",
|
'text/x-c++': '.cpp',
|
||||||
"text/x-go": ".go",
|
'text/x-go': '.go',
|
||||||
"application/x-perl": ".pl",
|
'application/x-perl': '.pl',
|
||||||
"text/x-php": ".php",
|
'text/x-php': '.php',
|
||||||
"text/x-ruby": ".rb",
|
'text/x-ruby': '.rb',
|
||||||
"text/x-sql": ".sql",
|
'text/x-sql': '.sql',
|
||||||
"application/x-httpd-php": ".php",
|
'application/x-httpd-php': '.php',
|
||||||
"application/x-python-code": ".pyc",
|
'application/x-python-code': '.pyc',
|
||||||
|
|
||||||
// ROM Files
|
// ROM Files
|
||||||
"application/x-nintendo-nes-rom": ".nes",
|
'application/x-nintendo-nes-rom': '.nes',
|
||||||
"application/x-snes-rom": ".smc",
|
'application/x-snes-rom': '.smc',
|
||||||
"application/x-gameboy-rom": ".gb",
|
'application/x-gameboy-rom': '.gb',
|
||||||
"application/x-gameboy-advance-rom": ".gba",
|
'application/x-gameboy-advance-rom': '.gba',
|
||||||
"application/x-n64-rom": ".n64",
|
'application/x-n64-rom': '.n64',
|
||||||
"application/x-sega-genesis-rom": ".gen",
|
'application/x-sega-genesis-rom': '.gen',
|
||||||
"application/x-sega-master-system-rom": ".sms",
|
'application/x-sega-master-system-rom': '.sms',
|
||||||
"application/x-psx-rom": ".iso", // PlayStation ROMs
|
'application/x-psx-rom': '.iso', // PlayStation ROMs
|
||||||
"application/x-bios-rom": ".rom",
|
'application/x-bios-rom': '.rom',
|
||||||
"application/x-flash-rom": ".bin",
|
'application/x-flash-rom': '.bin',
|
||||||
"application/x-eeprom": ".eep",
|
'application/x-eeprom': '.eep',
|
||||||
"application/x-c64-rom": ".prg",
|
'application/x-c64-rom': '.prg',
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
"application/octet-stream": ".bin", // General binary files
|
'application/octet-stream': '.bin', // General binary files
|
||||||
"application/x-shockwave-flash": ".swf",
|
'application/x-shockwave-flash': '.swf',
|
||||||
"application/x-silverlight-app": ".xap",
|
'application/x-silverlight-app': '.xap',
|
||||||
"application/x-ms-shortcut": ".lnk",
|
'application/x-ms-shortcut': '.lnk',
|
||||||
"application/vnd.ms-fontobject": ".eot",
|
'application/vnd.ms-fontobject': '.eot',
|
||||||
"font/woff": ".woff",
|
'font/woff': '.woff',
|
||||||
"font/woff2": ".woff2",
|
'font/woff2': '.woff2',
|
||||||
"font/ttf": ".ttf",
|
'font/ttf': '.ttf',
|
||||||
"font/otf": ".otf",
|
'font/otf': '.otf',
|
||||||
"application/vnd.visio": ".vsd",
|
'application/vnd.visio': '.vsd',
|
||||||
"application/vnd.ms-project": ".mpp",
|
'application/vnd.ms-project': '.mpp',
|
||||||
};
|
};
|
||||||
|
@ -1,56 +1,58 @@
|
|||||||
export class RequestQueueWithPromise {
|
export class RequestQueueWithPromise {
|
||||||
constructor(maxConcurrent = 5) {
|
constructor(maxConcurrent = 5) {
|
||||||
this.queue = [];
|
this.queue = [];
|
||||||
this.maxConcurrent = maxConcurrent;
|
this.maxConcurrent = maxConcurrent;
|
||||||
this.currentlyProcessing = 0;
|
this.currentlyProcessing = 0;
|
||||||
this.isPaused = false; // Flag to track whether the queue is paused
|
this.isPaused = false; // Flag to track whether the queue is paused
|
||||||
}
|
|
||||||
|
|
||||||
// Add a request to the queue and return a promise
|
|
||||||
enqueue(request) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Push the request and its resolve and reject callbacks to the queue
|
|
||||||
this.queue.push({ request, resolve, reject });
|
|
||||||
this.process();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process requests in the queue
|
|
||||||
async process() {
|
|
||||||
// Process requests only if the queue is not paused
|
|
||||||
if (this.isPaused) return;
|
|
||||||
|
|
||||||
while (this.queue.length > 0 && this.currentlyProcessing < this.maxConcurrent) {
|
|
||||||
this.currentlyProcessing++;
|
|
||||||
|
|
||||||
const { request, resolve, reject } = this.queue.shift();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await request();
|
|
||||||
resolve(response);
|
|
||||||
} catch (error) {
|
|
||||||
reject(error);
|
|
||||||
} finally {
|
|
||||||
this.currentlyProcessing--;
|
|
||||||
await this.process();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pause the queue processing
|
|
||||||
pause() {
|
|
||||||
this.isPaused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resume the queue processing
|
|
||||||
resume() {
|
|
||||||
this.isPaused = false;
|
|
||||||
this.process(); // Continue processing when resumed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear pending requests in the queue
|
|
||||||
clear() {
|
|
||||||
this.queue.length = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a request to the queue and return a promise
|
||||||
|
enqueue(request) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Push the request and its resolve and reject callbacks to the queue
|
||||||
|
this.queue.push({ request, resolve, reject });
|
||||||
|
this.process();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process requests in the queue
|
||||||
|
async process() {
|
||||||
|
// Process requests only if the queue is not paused
|
||||||
|
if (this.isPaused) return;
|
||||||
|
|
||||||
|
while (
|
||||||
|
this.queue.length > 0 &&
|
||||||
|
this.currentlyProcessing < this.maxConcurrent
|
||||||
|
) {
|
||||||
|
this.currentlyProcessing++;
|
||||||
|
|
||||||
|
const { request, resolve, reject } = this.queue.shift();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await request();
|
||||||
|
resolve(response);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
} finally {
|
||||||
|
this.currentlyProcessing--;
|
||||||
|
await this.process();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause the queue processing
|
||||||
|
pause() {
|
||||||
|
this.isPaused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume the queue processing
|
||||||
|
resume() {
|
||||||
|
this.isPaused = false;
|
||||||
|
this.process(); // Continue processing when resumed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear pending requests in the queue
|
||||||
|
clear() {
|
||||||
|
this.queue.length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,56 +1,57 @@
|
|||||||
import moment from "moment"
|
import moment from 'moment';
|
||||||
|
|
||||||
export function formatTimestamp(timestamp: number): string {
|
export function formatTimestamp(timestamp: number): string {
|
||||||
const now = moment()
|
const now = moment();
|
||||||
const timestampMoment = moment(timestamp)
|
const timestampMoment = moment(timestamp);
|
||||||
const elapsedTime = now.diff(timestampMoment, 'minutes')
|
const elapsedTime = now.diff(timestampMoment, 'minutes');
|
||||||
|
|
||||||
if (elapsedTime < 1) {
|
if (elapsedTime < 1) {
|
||||||
return 'Just now'
|
return 'Just now';
|
||||||
} else if (elapsedTime < 60) {
|
} else if (elapsedTime < 60) {
|
||||||
return `${elapsedTime}m ago`
|
return `${elapsedTime}m ago`;
|
||||||
} else if (elapsedTime < 1440) {
|
} else if (elapsedTime < 1440) {
|
||||||
return `${Math.floor(elapsedTime / 60)}h ago`
|
return `${Math.floor(elapsedTime / 60)}h ago`;
|
||||||
} else {
|
} else {
|
||||||
return timestampMoment.format('MMM D, YYYY')
|
return timestampMoment.format('MMM D, YYYY');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export function formatTimestampForum(timestamp: number): string {
|
|
||||||
const now = moment();
|
|
||||||
const timestampMoment = moment(timestamp);
|
|
||||||
const elapsedTime = now.diff(timestampMoment, 'minutes');
|
|
||||||
|
|
||||||
if (elapsedTime < 1) {
|
|
||||||
return `Just now - ${timestampMoment.format('h:mm A')}`;
|
|
||||||
} else if (elapsedTime < 60) {
|
|
||||||
return `${elapsedTime}m ago - ${timestampMoment.format('h:mm A')}`;
|
|
||||||
} else if (elapsedTime < 1440) {
|
|
||||||
return `${Math.floor(elapsedTime / 60)}h ago - ${timestampMoment.format('h:mm A')}`;
|
|
||||||
} else {
|
|
||||||
return timestampMoment.format('MMM D, YYYY - h:mm A');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const formatDate = (unixTimestamp: number): string => {
|
export function formatTimestampForum(timestamp: number): string {
|
||||||
const date = moment(unixTimestamp, 'x').fromNow()
|
const now = moment();
|
||||||
|
const timestampMoment = moment(timestamp);
|
||||||
return date
|
const elapsedTime = now.diff(timestampMoment, 'minutes');
|
||||||
}
|
|
||||||
|
|
||||||
export function sortArrayByTimestampAndGroupName(array) {
|
if (elapsedTime < 1) {
|
||||||
return array.sort((a, b) => {
|
return `Just now - ${timestampMoment.format('h:mm A')}`;
|
||||||
if (a.timestamp && b.timestamp) {
|
} else if (elapsedTime < 60) {
|
||||||
// Both have timestamp, sort by timestamp descending
|
return `${elapsedTime}m ago - ${timestampMoment.format('h:mm A')}`;
|
||||||
return b.timestamp - a.timestamp;
|
} else if (elapsedTime < 1440) {
|
||||||
} else if (a.timestamp) {
|
return `${Math.floor(elapsedTime / 60)}h ago - ${timestampMoment.format('h:mm A')}`;
|
||||||
// Only `a` has timestamp, it comes first
|
} else {
|
||||||
return -1;
|
return timestampMoment.format('MMM D, YYYY - h:mm A');
|
||||||
} else if (b.timestamp) {
|
}
|
||||||
// Only `b` has timestamp, it comes first
|
}
|
||||||
return 1;
|
|
||||||
} else {
|
export const formatDate = (unixTimestamp: number): string => {
|
||||||
// Neither has timestamp, sort alphabetically by groupName
|
const date = moment(unixTimestamp, 'x').fromNow();
|
||||||
return a.groupName.localeCompare(b.groupName);
|
|
||||||
}
|
return date;
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
export function sortArrayByTimestampAndGroupName(array) {
|
||||||
|
return array.sort((a, b) => {
|
||||||
|
if (a.timestamp && b.timestamp) {
|
||||||
|
// Both have timestamp, sort by timestamp descending
|
||||||
|
return b.timestamp - a.timestamp;
|
||||||
|
} else if (a.timestamp) {
|
||||||
|
// Only `a` has timestamp, it comes first
|
||||||
|
return -1;
|
||||||
|
} else if (b.timestamp) {
|
||||||
|
// Only `b` has timestamp, it comes first
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
// Neither has timestamp, sort alphabetically by groupName
|
||||||
|
return a.groupName.localeCompare(b.groupName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -1,55 +1,55 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
const utils = {
|
const utils = {
|
||||||
int32ToBytes (word) {
|
int32ToBytes(word) {
|
||||||
var byteArray = []
|
var byteArray = [];
|
||||||
for (var b = 0; b < 32; b += 8) {
|
for (var b = 0; b < 32; b += 8) {
|
||||||
byteArray.push((word >>> (24 - b % 32)) & 0xFF)
|
byteArray.push((word >>> (24 - (b % 32))) & 0xff);
|
||||||
}
|
|
||||||
return byteArray
|
|
||||||
},
|
|
||||||
|
|
||||||
stringtoUTF8Array (message) {
|
|
||||||
if (typeof message === 'string') {
|
|
||||||
var s = unescape(encodeURIComponent(message)) // UTF-8
|
|
||||||
message = new Uint8Array(s.length)
|
|
||||||
for (var i = 0; i < s.length; i++) {
|
|
||||||
message[i] = s.charCodeAt(i) & 0xff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
},
|
|
||||||
// ...buffers then buffers.foreach and append to buffer1
|
|
||||||
appendBuffer (buffer1, buffer2) {
|
|
||||||
buffer1 = new Uint8Array(buffer1)
|
|
||||||
buffer2 = new Uint8Array(buffer2)
|
|
||||||
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength)
|
|
||||||
tmp.set(buffer1, 0)
|
|
||||||
tmp.set(buffer2, buffer1.byteLength)
|
|
||||||
return tmp
|
|
||||||
},
|
|
||||||
|
|
||||||
int64ToBytes (int64) {
|
|
||||||
// we want to represent the input as a 8-bytes array
|
|
||||||
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]
|
|
||||||
|
|
||||||
for (var index = 0; index < byteArray.length; index++) {
|
|
||||||
var byte = int64 & 0xff
|
|
||||||
byteArray[byteArray.length - index - 1] = byte
|
|
||||||
int64 = (int64 - byte) / 256
|
|
||||||
}
|
|
||||||
|
|
||||||
return byteArray
|
|
||||||
},
|
|
||||||
|
|
||||||
equal (buf1, buf2) {
|
|
||||||
if (buf1.byteLength != buf2.byteLength) return false
|
|
||||||
var dv1 = new Uint8Array(buf1)
|
|
||||||
var dv2 = new Uint8Array(buf2)
|
|
||||||
for (var i = 0; i != buf1.byteLength; i++) {
|
|
||||||
if (dv1[i] != dv2[i]) return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
return byteArray;
|
||||||
|
},
|
||||||
|
|
||||||
export default utils
|
stringtoUTF8Array(message) {
|
||||||
|
if (typeof message === 'string') {
|
||||||
|
var s = unescape(encodeURIComponent(message)); // UTF-8
|
||||||
|
message = new Uint8Array(s.length);
|
||||||
|
for (var i = 0; i < s.length; i++) {
|
||||||
|
message[i] = s.charCodeAt(i) & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
// ...buffers then buffers.foreach and append to buffer1
|
||||||
|
appendBuffer(buffer1, buffer2) {
|
||||||
|
buffer1 = new Uint8Array(buffer1);
|
||||||
|
buffer2 = new Uint8Array(buffer2);
|
||||||
|
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
|
||||||
|
tmp.set(buffer1, 0);
|
||||||
|
tmp.set(buffer2, buffer1.byteLength);
|
||||||
|
return tmp;
|
||||||
|
},
|
||||||
|
|
||||||
|
int64ToBytes(int64) {
|
||||||
|
// we want to represent the input as a 8-bytes array
|
||||||
|
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
|
||||||
|
for (var index = 0; index < byteArray.length; index++) {
|
||||||
|
var byte = int64 & 0xff;
|
||||||
|
byteArray[byteArray.length - index - 1] = byte;
|
||||||
|
int64 = (int64 - byte) / 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteArray;
|
||||||
|
},
|
||||||
|
|
||||||
|
equal(buf1, buf2) {
|
||||||
|
if (buf1.byteLength != buf2.byteLength) return false;
|
||||||
|
var dv1 = new Uint8Array(buf1);
|
||||||
|
var dv2 = new Uint8Array(buf2);
|
||||||
|
for (var i = 0; i != buf1.byteLength; i++) {
|
||||||
|
if (dv1[i] != dv2[i]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default utils;
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import Base58 from "../deps/Base58"
|
import Base58 from '../deps/Base58';
|
||||||
|
|
||||||
export const validateAddress = (address) => {
|
export const validateAddress = (address) => {
|
||||||
let isAddress = false
|
let isAddress = false;
|
||||||
try {
|
try {
|
||||||
const decodePubKey = Base58.decode(address)
|
const decodePubKey = Base58.decode(address);
|
||||||
|
|
||||||
if (!(decodePubKey instanceof Uint8Array && decodePubKey.length == 25)) {
|
if (!(decodePubKey instanceof Uint8Array && decodePubKey.length == 25)) {
|
||||||
isAddress = false
|
isAddress = false;
|
||||||
} else {
|
} else {
|
||||||
isAddress = true
|
isAddress = true;
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
} catch (error) {
|
console.log(error);
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return isAddress
|
return isAddress;
|
||||||
}
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user