some fixes

This commit is contained in:
kandrusyak
2022-12-06 16:16:12 +03:00
parent 8bf83aaa5b
commit 78f48b240b
12 changed files with 238 additions and 73 deletions

View File

@@ -19,6 +19,7 @@ import { observer } from 'mobx-react-lite';
import Delegates from './pages/services/Delegates'; import Delegates from './pages/services/Delegates';
import VPNServers from './pages/services/VPNServers'; import VPNServers from './pages/services/VPNServers';
import VPNPrepare from './pages/services/VPNPrepare'; import VPNPrepare from './pages/services/VPNPrepare';
import { NotificationContainer } from './components/NotificationContainer';
const App = observer(() => { const App = observer(() => {
const location = useLocation(); const location = useLocation();
@@ -39,6 +40,7 @@ const App = observer(() => {
return ( return (
<> <>
{store.loading && <LoadingOverlay />} {store.loading && <LoadingOverlay />}
<NotificationContainer />
<Routes> <Routes>
<Route exact path="/" element={<Onboarding1 />} /> <Route exact path="/" element={<Onboarding1 />} />
<Route path="/digitalId/profile-id" element={<ProfileId />} /> <Route path="/digitalId/profile-id" element={<ProfileId />} />

View File

@@ -89,9 +89,9 @@ function DropdownTransaction({ align, value, onChange }) {
<li> <li>
<span <span
className="font-medium text-sm text-slate-600 hover:text-slate-800 flex items-center py-1 px-3 cursor-pointer" className="font-medium text-sm text-slate-600 hover:text-slate-800 flex items-center py-1 px-3 cursor-pointer"
onClick={() => changeValue('Unvoiting')} onClick={() => changeValue('Unlocking')}
> >
Unvoiting Unlocking
</span> </span>
</li> </li>
</ul> </ul>

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import { Toast } from './Toast2';
import { store } from '../store/store';
export const NotificationContainer = observer(() => (
<div className="absolute z-50 top-2 right-2 gap-2">
{store.notifications.map((e) => (
<Toast type={e.type} key={e.id} id={e.id}>
{e.message}
</Toast>
))}
</div>
));

90
src/components/Toast2.jsx Normal file
View File

@@ -0,0 +1,90 @@
import React, { useEffect } from 'react';
import { store } from '../store/store';
import { observer } from 'mobx-react-lite';
export const Toast = observer(({ children, className, type, id }) => {
const typeIcon = (type) => {
switch (type) {
case 'warning':
return (
<svg
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
viewBox="0 0 16 16"
>
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
</svg>
);
case 'error':
return (
<svg
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
viewBox="0 0 16 16"
>
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3.5 10.1l-1.4 1.4L8 9.4l-2.1 2.1-1.4-1.4L6.6 8 4.5 5.9l1.4-1.4L8 6.6l2.1-2.1 1.4 1.4L9.4 8l2.1 2.1z" />
</svg>
);
case 'success':
return (
<svg
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
viewBox="0 0 16 16"
>
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zM7 11.4L3.6 8 5 6.6l2 2 4-4L12.4 6 7 11.4z" />
</svg>
);
default:
return (
<svg
className="w-4 h-4 shrink-0 fill-current opacity-80 mt-[3px] mr-3"
viewBox="0 0 16 16"
>
<path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm1 12H7V7h2v5zM8 6c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z" />
</svg>
);
}
};
const typeColor = (type) => {
switch (type) {
case 'warning':
return 'bg-amber-100 border-amber-200 text-amber-600';
case 'error':
return 'bg-rose-100 border-rose-200 text-rose-600';
case 'success':
return 'bg-emerald-100 border-emerald-200 text-emerald-600';
default:
return 'bg-indigo-100 border-indigo-200 text-indigo-500';
}
};
useEffect(() => {
const timerId = setTimeout(() => store.removeNotification(id), 3000);
return () => clearTimeout(timerId);
}, []);
return (
<div className={className}>
<div
className={`inline-flex min-w-80 px-4 py-2 rounded-sm text-sm border ${typeColor(
type
)}`}
>
<div className="flex w-full justify-between items-start">
<div className="flex">
{typeIcon(type)}
<div>{children}</div>
</div>
<button
className="opacity-70 hover:opacity-80 ml-3 mt-[3px]"
onClick={() => store.removeNotification(id)}
>
<div className="sr-only">Close</div>
<svg className="w-4 h-4 fill-current">
<path d="M7.95 6.536l4.242-4.243a1 1 0 111.415 1.414L9.364 7.95l4.243 4.242a1 1 0 11-1.415 1.415L7.95 9.364l-4.243 4.243a1 1 0 01-1.414-1.415L6.536 7.95 2.293 3.707a1 1 0 011.414-1.414L7.95 6.536z" />
</svg>
</button>
</div>
</div>
</div>
);
});

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import Sidebar from '../../partials/Sidebar'; import Sidebar from '../../partials/Sidebar';
import Header from '../../partials/Header'; import Header from '../../partials/Header';
@@ -10,7 +10,7 @@ import TransactionPanel from '../../partials/finance/TransactionPanel';
import { Filters } from '../../components/Filters'; import { Filters } from '../../components/Filters';
const filters = { const filters = {
'View All': '', 'View All': 'all',
Voted: 'Unvote', Voted: 'Unvote',
Pending: 'Pending', Pending: 'Pending',
}; };
@@ -22,17 +22,24 @@ const Delegates = observer(() => {
const [lockedFor, setLockedFor] = useState('Voiting'); const [lockedFor, setLockedFor] = useState('Voiting');
const lockedLabel = Object.values( const delegates = useMemo(() => {
lockedFor === 'Voiting' ? store.processedVotes : store.accountLockedVotes if (filters[currentFilter] === 'all') return store.delegates;
return store.delegates.filter((e) => e.status === filters[currentFilter]);
}, [store.delegates, currentFilter]);
const lockedLabel = (
lockedFor === 'Voiting'
? Object.values(store.accountSentVotes)
: Object.values(store.accountLockedVotesCanReturn)
.flat()
.map((e) => e.amount)
) )
.reduce((sum, e) => sum + e, 0n) .reduce((sum, e) => sum + e, 0n)
.toString(); .toString();
const [fetchOffset, setFetchOffset] = useState(0);
useEffect(() => { useEffect(() => {
store.fetchDelegates(fetchOffset); store.fetchDelegates(0);
}, [fetchOffset]); }, []);
return ( return (
<div className="flex h-screen overflow-hidden"> <div className="flex h-screen overflow-hidden">
@@ -72,20 +79,21 @@ const Delegates = observer(() => {
{/* Table */} {/* Table */}
<TransactionsTable <TransactionsTable
data={store.delegates} data={delegates}
lockedFor={lockedFor}
rowClick={(delegate) => setCurrentDelegate(delegate)} rowClick={(delegate) => setCurrentDelegate(delegate)}
/> />
{/* Pagination */} {/*/!* Pagination *!/*/}
<div className="mt-8"> {/*<div className="mt-8">*/}
<PaginationClassic {/* <PaginationClassic*/}
count={store.delegatesMeta.count} {/* count={store.delegatesMeta.count}*/}
limit={store.delegatesMeta.limit} {/* limit={store.delegatesMeta.limit}*/}
offset={store.delegatesMeta.offset} {/* offset={store.delegatesMeta.offset}*/}
onNextPage={() => setFetchOffset((prev) => prev + 10)} {/* onNextPage={() => setFetchOffset((prev) => prev + 10)}*/}
onPrevPage={() => setFetchOffset((prev) => prev - 10)} {/* onPrevPage={() => setFetchOffset((prev) => prev - 10)}*/}
/> {/* />*/}
</div> {/*</div>*/}
<TransactionPanel <TransactionPanel
transactionPanelOpen={!!currentDelegate} transactionPanelOpen={!!currentDelegate}

View File

@@ -26,6 +26,8 @@ const VPNPrepare = observer(() => {
if (store.canVPN) navigate('/services/vpn/servers'); if (store.canVPN) navigate('/services/vpn/servers');
}, [store.canVPN]); }, [store.canVPN]);
if (store.loading && !store.canVPN) return null;
return ( return (
<div className="flex h-screen overflow-hidden"> <div className="flex h-screen overflow-hidden">
{/* Sidebar */} {/* Sidebar */}

View File

@@ -95,7 +95,7 @@ PersistentKeepalive = 25
<label <label
className="relative block cursor-pointer text-left w-full" className="relative block cursor-pointer text-left w-full"
onClick={() => setSelectedServer(i)} onClick={() => setSelectedServer(i)}
key={e.publicKey} key={e.serverPublickKey}
> >
<input <input
type="radio" type="radio"
@@ -107,7 +107,7 @@ PersistentKeepalive = 25
<div className="p-4 rounded border border-slate-200 hover:border-slate-300 shadow-sm duration-150 ease-in-out"> <div className="p-4 rounded border border-slate-200 hover:border-slate-300 shadow-sm duration-150 ease-in-out">
<div className="grid grid-cols-12 items-center gap-x-2"> <div className="grid grid-cols-12 items-center gap-x-2">
{/* Card */} {/* Card */}
<div className="col-span-6 order-1 sm:order-none sm:col-span-3 flex items-center space-x-4 lg:sidebar-expanded:col-span-6 xl:sidebar-expanded:col-span-3"> <div className="col-span-6 order-1 sm:order-none flex items-center space-x-4 ">
<ReactCountryFlag <ReactCountryFlag
countryCode={e.country} countryCode={e.country}
svg svg
@@ -121,13 +121,13 @@ PersistentKeepalive = 25
</div> </div>
</div> </div>
{/* Card limits */} {/* Card limits */}
<div className="col-span-6 order-1 sm:order-none sm:col-span-4 text-right sm:text-center lg:sidebar-expanded:col-span-6 xl:sidebar-expanded:col-span-6"> <div className="col-span-3 order-1 sm:order-none text-right sm:text-center">
<div className="text-sm"> <div className="text-sm">
{e.transferSum} / 50.00 GB {e.transferSum} / 50.00 GB
</div> </div>
</div> </div>
{/* Card status */} {/* Card status */}
<div className="col-span-6 order-2 sm:order-none sm:col-span-2 text-right lg:sidebar-expanded:hidden xl:sidebar-expanded:block"> <div className="col-span-3 order-2 sm:order-none text-right">
{e?.state ? ( {e?.state ? (
<div className="text-xs inline-flex font-medium bg-emerald-100 text-emerald-600 rounded-full text-center px-2.5 py-1"> <div className="text-xs inline-flex font-medium bg-emerald-100 text-emerald-600 rounded-full text-center px-2.5 py-1">
Active Active
@@ -150,7 +150,7 @@ PersistentKeepalive = 25
</div> </div>
{/* Sidebar */} {/* Sidebar */}
{servers?.length > 0 && ( {servers?.length > 0 && servers?.[selectedServer]?.state && (
<div> <div>
<div className="lg:sticky lg:top-16 bg-slate-50 lg:overflow-x-hidden lg:overflow-y-auto no-scrollbar lg:shrink-0 border-t lg:border-t-0 lg:border-l border-slate-200 lg:w-[390px] lg:h-[calc(100vh-64px)]"> <div className="lg:sticky lg:top-16 bg-slate-50 lg:overflow-x-hidden lg:overflow-y-auto no-scrollbar lg:shrink-0 border-t lg:border-t-0 lg:border-l border-slate-200 lg:w-[390px] lg:h-[calc(100vh-64px)]">
<div className="py-8 px-4 lg:px-8 h-full"> <div className="py-8 px-4 lg:px-8 h-full">

View File

@@ -4,7 +4,7 @@ import TransactionItem from './TransactionsTableItem';
import Image01 from '../../images/transactions-image-01.svg'; import Image01 from '../../images/transactions-image-01.svg';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
const TransactionsTable = observer(({ data, rowClick }) => { const TransactionsTable = observer(({ data, rowClick, lockedFor }) => {
return ( return (
<div className="bg-white"> <div className="bg-white">
<div> <div>
@@ -30,7 +30,9 @@ const TransactionsTable = observer(({ data, rowClick }) => {
<div className="font-semibold text-center"></div> <div className="font-semibold text-center"></div>
</th> </th>
<th className="px-4 first:pl-5 last:pr-5 py-3 whitespace-nowrap"> <th className="px-4 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="font-semibold text-center">Sent Votes</div> <div className="font-semibold text-center">
{lockedFor === 'Voiting' ? 'Sent Votes' : 'Blocked votes'}
</div>
</th> </th>
</tr> </tr>
</thead> </thead>
@@ -42,14 +44,25 @@ const TransactionsTable = observer(({ data, rowClick }) => {
key={delegate.address} key={delegate.address}
id={delegate.address} id={delegate.address}
image={Image01} image={Image01}
balance={delegate.token.balance} balance={delegate?.token?.balance}
name={delegate.dpos.delegate.username || delegate.address} name={delegate.dpos?.delegate?.username || delegate.address}
total={delegate.dpos.delegate.totalVotesReceived} total={delegate.dpos?.delegate?.totalVotesReceived || ''}
hasSevice={false} hasSevice={
delegate.dpos?.delegate?.username === 'delegate_0'
}
date={delegate.date} date={delegate.date}
displayReturn={lockedFor === 'Unlocking'}
status={delegate.status} status={delegate.status}
amount={store.accountSentVotes[delegate.address] || 0} amount={
handleClick={() => rowClick(delegate)} (lockedFor === 'Voiting'
? store.accountSentVotes[delegate.address]
: store.accountLockedVotesCanReturnSum[
delegate.address
]) || ''
}
handleClick={() =>
lockedFor === 'Voiting' && rowClick(delegate)
}
/> />
); );
})} })}

View File

@@ -43,14 +43,6 @@ const TransactionsTableItem = observer((props) => {
/> />
</div> </div>
<div className="font-medium text-slate-800">{props.name}</div> <div className="font-medium text-slate-800">{props.name}</div>
{store.accountLockedVotesCanReturn[props.id] && (
<span
className="truncate font-medium text-indigo-500 group-hover:text-indigo-600 ml-2"
onClick={clickReturn}
>
Return votes
</span>
)}
</div> </div>
</td> </td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap"> <td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
@@ -72,21 +64,36 @@ const TransactionsTableItem = observer((props) => {
<div className="text-center">{props.balance}/idn</div> <div className="text-center">{props.balance}/idn</div>
</td> </td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap"> <td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="text-center">{props.total}/idn</div> <div className="text-center">
{props.total}
{props.total && '/idn'}
</div>
</td> </td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap"> <td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap">
<div className="text-center"> <div className="text-center">
<div {props.displayReturn ? (
className={`text-xs inline-flex font-medium rounded-full justify-center px-2.5 py-1 ${statusColor()}`} store.accountLockedVotesCanReturn[props.id] && (
style={{ minWidth: '84px' }} <span
> className="truncate font-medium text-indigo-500 group-hover:text-indigo-600 ml-2"
{props.status} onClick={clickReturn}
</div> >
Return votes
</span>
)
) : (
<div
className={`text-xs inline-flex font-medium rounded-full justify-center px-2.5 py-1 ${statusColor()}`}
style={{ minWidth: '84px' }}
>
{props.status}
</div>
)}
</div> </div>
</td> </td>
<td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap w-px"> <td className="px-2 first:pl-5 last:pr-5 py-3 whitespace-nowrap w-px">
<div className={`text-center font-medium ${amountColor()}`}> <div className={`text-center font-medium ${amountColor()}`}>
{props.amount.toString()}/idn {props.amount.toString()}
{props.amount && '/idn'}
</div> </div>
</td> </td>
</tr> </tr>

View File

@@ -64,7 +64,12 @@ function request(method, url, headers = {}, token, body) {
requestOptions.headers.Authorization = null; requestOptions.headers.Authorization = null;
} }
setTimeout(() => controller.abort(), 15000); setTimeout(() => controller.abort(), 15000);
return fetch(prepareUrl(url), requestOptions); return fetch(prepareUrl(url), requestOptions).then((res) => {
if (res.status > 400) {
store.addNotification(new Date().getTime(), 'error', res.statusText);
}
return res;
});
} }
function get(url, headers, attempts = 3) { function get(url, headers, attempts = 3) {

View File

@@ -13,7 +13,6 @@ import { statusMap } from '../shared/statusMap';
import { generateSvgAvatar } from '../images/GenerateOnboardingSvg/GenerateSvg'; import { generateSvgAvatar } from '../images/GenerateOnboardingSvg/GenerateSvg';
import { decryptedData } from '../utils/decryptedData'; import { decryptedData } from '../utils/decryptedData';
import { import {
bytesToMbytes,
encryptAccountData, encryptAccountData,
encryptSharedData, encryptSharedData,
formatBytes, formatBytes,
@@ -55,6 +54,8 @@ class Store {
}, },
}; };
_notifications = [];
constructor() { constructor() {
makeAutoObservable(this, {}); makeAutoObservable(this, {});
@@ -93,7 +94,7 @@ class Store {
.then((r) => this.fetchVPNServersSuccess(r)); .then((r) => this.fetchVPNServersSuccess(r));
} }
fetchDelegates(offset = 0, limit = 10) { fetchDelegates(offset = 0, limit = 100) {
fetchWrapper fetchWrapper
.getAuth(`delegates?limit=${limit}&offset=${offset}`) .getAuth(`delegates?limit=${limit}&offset=${offset}`)
.then((r) => this.fetchDelegatesSuccess(r)); .then((r) => this.fetchDelegatesSuccess(r));
@@ -253,10 +254,9 @@ class Store {
this.passPhrase this.passPhrase
); );
const { amount, unvoteHeight } = const unlockObjects = this.accountLockedVotesCanReturn[delegateAddress];
this.accountLockedVotesCanReturn[delegateAddress];
const signedTx = builder.unlock(delegateAddress, amount, unvoteHeight); const signedTx = builder.unlock(delegateAddress, unlockObjects);
if (signedTx) { if (signedTx) {
fetchWrapper fetchWrapper
@@ -507,15 +507,27 @@ class Store {
.reduce( .reduce(
(acc, e) => ({ (acc, e) => ({
...acc, ...acc,
[e.delegateAddress]: { [e.delegateAddress]: [
amount: BigInt(e.amount || 0) / 100000000n, {
unvoteHeight: e.unvoteHeight, ...acc[e.delegateAddress],
}, amount: BigInt(e.amount || 0) / 100000000n,
unvoteHeight: e.unvoteHeight,
},
],
}), }),
{} {}
); );
} }
get accountLockedVotesCanReturnSum() {
return Object.fromEntries(
Object.entries(this.accountLockedVotesCanReturn).map(([key, val]) => [
key,
val.reduce((sum, e) => sum + e.amount, 0n),
])
);
}
get accountFeaturesMap() { get accountFeaturesMap() {
return this.accountFeatures.reduce( return this.accountFeatures.reduce(
(acc, item) => ({ (acc, item) => ({
@@ -644,7 +656,7 @@ class Store {
...delegate.dpos.delegate, ...delegate.dpos.delegate,
totalVotesReceived: Number( totalVotesReceived: Number(
BigInt(delegate.dpos.delegate.totalVotesReceived) / 100000000n BigInt(delegate.dpos.delegate.totalVotesReceived) / 100000000n
).toString(), ),
}, },
}, },
})); }));
@@ -653,6 +665,22 @@ class Store {
get delegatesMeta() { get delegatesMeta() {
return this._delegates.meta; return this._delegates.meta;
} }
get notifications() {
return this._notifications;
}
addNotification(id, type, message) {
this._notifications.push({
id,
type,
message,
});
}
removeNotification(id) {
this._notifications = this._notifications.filter((e) => e.id !== id);
}
} }
export const store = new Store(); export const store = new Store();

View File

@@ -197,7 +197,7 @@ export const generateTransaction = (
delegateAddress, delegateAddress,
amount amount
), ),
unlock: (delegateAddress, amount, unvoteHeight) => unlock: (delegateAddress, unlockObjects) =>
generateUnlockTransaction( generateUnlockTransaction(
nonce, nonce,
senderPublicKey, senderPublicKey,
@@ -205,8 +205,7 @@ export const generateTransaction = (
passPhrase, passPhrase,
fee, fee,
delegateAddress, delegateAddress,
amount, unlockObjects
unvoteHeight
), ),
}; };
}; };
@@ -379,8 +378,7 @@ const generateUnlockTransaction = (
passPhrase = '', passPhrase = '',
fee = BigInt(500000), fee = BigInt(500000),
delegateAddress = '', delegateAddress = '',
amount = 0n, unlockObjects = []
unvoteHeight = 0
) => { ) => {
const tx = { const tx = {
moduleID: 5, moduleID: 5,
@@ -389,13 +387,11 @@ const generateUnlockTransaction = (
senderPublicKey: Buffer.from(senderPublicKey, 'hex'), senderPublicKey: Buffer.from(senderPublicKey, 'hex'),
fee, fee,
asset: { asset: {
unlockObjects: [ unlockObjects: unlockObjects.map((e) => ({
{ delegateAddress: Buffer.from(delegateAddress, 'hex'),
delegateAddress: Buffer.from(delegateAddress, 'hex'), amount: e.amount * 100000000n,
amount: amount * 100000000n, unvoteHeight: e.unvoteHeight,
unvoteHeight, })),
},
],
}, },
}; };