Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ WORKDIR /app

# Set to production
ENV NODE_ENV=production \
PORT=3000 \
PORT=8080 \
HOSTNAME="0.0.0.0"

# Copy built files from builder
Expand All @@ -68,11 +68,11 @@ RUN chown -R nextjs:nodejs /app
USER nextjs

# Expose port
EXPOSE 3000
EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD wget -q --spider http://localhost:3000/api/health || exit 1
CMD wget -q --spider http://localhost:8080/api/health || exit 1

# Use dumb-init to handle signals properly
ENTRYPOINT ["dumb-init", "--"]
Expand Down
6 changes: 3 additions & 3 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ services:
restart: unless-stopped

ports:
- "${PORT:-3000}:3000"
- "${HOST_PORT:-3000}:8080"

environment:
# Application
- NODE_ENV=production
- PORT=3000
- PORT=8080

# Steem Blockchain
- STEEM_RPC_URL=${STEEM_RPC_URL:-https://api.steemit.com}
Expand Down Expand Up @@ -57,7 +57,7 @@ services:

# Health check
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/health"]
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/api/health"]
interval: 30s
timeout: 10s
retries: 3
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"dev": "next dev --hostname 0.0.0.0 --port 3000",
"build": "next build",
"start": "next start",
"lint": "eslint",
Expand Down
4 changes: 2 additions & 2 deletions src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ const geistMono = Geist_Mono({
});

export const metadata: Metadata = {
title: 'Steem Wallet',
description: 'Modern Steem blockchain wallet',
title: 'Steemit Wallet',
description: 'Steemit Wallet is an online wallet for managing Steem accounts.',
icons: {
icon: [
{ url: '/favicon.ico' },
Expand Down
9 changes: 5 additions & 4 deletions src/components/wallet/balance-rows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,7 @@ export function BalanceRows({
},
{
label: 'Power Up',
walletAction: 'transfer',
asset: 'STEEM',
type: 'power_up',
walletAction: 'powerUp',
},
{ label: 'Trade', external: true, href: 'https://www.poloniex.com/zh-CN/trade/STEEM_USDT?type=spot' },
{ label: 'Market', link: '/market' },
Expand Down Expand Up @@ -205,6 +203,9 @@ export function BalanceRows({
items={[
{ label: 'Delegate', walletAction: 'delegate' },
{ label: 'Power Down', walletAction: 'powerDown' },
...(isPoweringDown
? [{ label: 'Cancel Power Down', walletAction: 'cancelPowerDown' as const }]
: []),
{ label: 'Advanced Routes', walletAction: 'advanced' },
]}
/>
Expand Down Expand Up @@ -334,7 +335,7 @@ export function BalanceRows({
? new Date(balance.next_vesting_withdrawal).toLocaleDateString()
: '---'}
</span>{' '}
(~{powerDownRate} STEEM).
(~{powerDownRate} SP).
</div>
</WalletBalanceRowShell>
)}
Expand Down
60 changes: 60 additions & 0 deletions src/components/wallet/cancel-power-down-handler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use client';

import { useEffect, useRef, useState } from 'react';
import { useTranslations } from 'next-intl';
import { useSelector } from 'react-redux';
import type { RootState } from '@/lib/store';
import { useActiveSigningKey } from '@/hooks/use-auth';
import { SteemSigner, apiClient } from '@/lib/steem/client';

export interface CancelPowerDownHandlerProps {
onSuccess: () => void;
onCancel: () => void;
}

/** Immediately broadcasts cancel power down (0 VESTS), matching wallet-legacy menu behavior. */
export function CancelPowerDownHandler({ onSuccess, onCancel }: CancelPowerDownHandlerProps) {
const t = useTranslations('powerDown');
const tCommon = useTranslations('common');
const username = useSelector((state: RootState) => state.auth.username);
const signingKey = useActiveSigningKey();
const [error, setError] = useState<string | null>(null);
const started = useRef(false);

useEffect(() => {
if (started.current || !username || !signingKey) return;
started.current = true;

void (async () => {
try {
const signedTx = await SteemSigner.signPowerDown(username, '0.000000 VESTS', signingKey);
const response = await apiClient.broadcastPowerDown(signedTx, username);
if (!response.success) {
setError(response.error || t('cancelError'));
return;
}
onSuccess();
} catch (err) {
console.error('Cancel power down error:', err);
setError(t('cancelError'));
}
})();
}, [username, signingKey, onSuccess, t]);

if (error) {
return (
<div className="px-1 py-4">
<p className="text-destructive text-sm">{error}</p>
<button
type="button"
className="text-muted-foreground mt-4 text-sm underline"
onClick={onCancel}
>
{tCommon('cancel')}
</button>
</div>
);
}

return <p className="text-muted-foreground px-1 py-4 text-sm">{tCommon('loading')}</p>;
}
38 changes: 24 additions & 14 deletions src/components/wallet/delegate-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { useTranslations } from 'next-intl';
import { useSelector } from 'react-redux';
import type { RootState } from '@/lib/store';
import { useActiveSigningKey } from '@/hooks/use-auth';
import { useGlobalProps } from '@/hooks/use-global-props';
import { SteemSigner, apiClient } from '@/lib/steem/client';
import { steemPowerToVestsAsset } from '@/lib/wallet/vest-steem';
import { Button } from '@/components/ui/button';
import {
ModalFormActions,
Expand All @@ -31,11 +33,13 @@ export function DelegateForm({
onCancel,
}: DelegateFormProps) {
const t = useTranslations('wallet');
const tDelegations = useTranslations('delegations');
const tCommon = useTranslations('common');
const router = useRouter();
const username = useSelector((state: RootState) => state.auth.username);
const signingKey = useActiveSigningKey();
const [isPending, startTransition] = useTransition();
const { globalProps, loading: globalPropsLoading } = useGlobalProps();

const [delegatee, setDelegatee] = useState('');
const [shares, setShares] = useState('');
Expand All @@ -53,26 +57,32 @@ export function DelegateForm({
return;
}

if (!globalProps) {
setError('Unable to load chain properties. Please try again.');
setIsLoading(false);
return;
}

try {
if (!delegatee.trim()) {
setError('Please enter a delegatee username');
setError(tDelegations('invalidDelegatee'));
setIsLoading(false);
return;
}

const shareValue = parseFloat(shares);
if (!shares || isNaN(shareValue) || shareValue <= 0) {
setError('Please enter a valid amount');
setError(tDelegations('invalidAmount'));
setIsLoading(false);
return;
}

const vests = `${shareValue.toFixed(6)} VESTS`;
const vests = steemPowerToVestsAsset(shareValue, globalProps);
const signedTx = await SteemSigner.signDelegate(username, delegatee.trim(), vests, signingKey);
const response = await apiClient.broadcastDelegate(signedTx, username);

if (!response.success) {
setError(response.error || 'Failed to delegate');
setError(response.error || tDelegations('delegateError'));
setIsLoading(false);
return;
}
Expand All @@ -86,7 +96,7 @@ export function DelegateForm({
});
} catch (err) {
console.error('Delegate error:', err);
setError('Failed to process delegation');
setError(tDelegations('delegateError'));
setIsLoading(false);
}
};
Expand All @@ -100,35 +110,35 @@ export function DelegateForm({
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
<div className="flex flex-col gap-2">
<Label htmlFor="delegatee" className="text-base">
Delegatee Username
{tDelegations('delegateeUsername')}
</Label>
<Input
type="text"
id="delegatee"
value={delegatee}
onChange={(e) => setDelegatee(e.target.value)}
required
placeholder="Enter username to delegate to"
placeholder={tDelegations('delegateePlaceholder')}
disabled={isLoading || isPending}
/>
</div>

<div className="flex flex-col gap-2">
<Label htmlFor="shares" className="text-base">
VESTS to Delegate
{tDelegations('spToDelegate')}
</Label>
<Input
type="number"
id="shares"
value={shares}
onChange={(e) => setShares(e.target.value)}
step="0.000001"
step="0.001"
min="0"
required
placeholder="Enter VESTS amount"
disabled={isLoading || isPending}
placeholder={tDelegations('spPlaceholder')}
disabled={isLoading || isPending || globalPropsLoading}
/>
<p className="text-muted-foreground text-sm">Use format: 6 decimal places (e.g., 1000000.000000)</p>
<p className="text-muted-foreground text-sm">{tDelegations('formatHint')}</p>
</div>

{error && (
Expand All @@ -140,10 +150,10 @@ export function DelegateForm({
<ModalFormActions className="pt-4">
<Button
type="submit"
disabled={isLoading || isPending}
disabled={isLoading || isPending || globalPropsLoading || !globalProps}
className={modalFormActionButtonClassName}
>
{isLoading || isPending ? tCommon('loading') : 'Delegate'}
{isLoading || isPending ? tCommon('loading') : tDelegations('delegateButton')}
</Button>
<Button
type="button"
Expand Down
19 changes: 10 additions & 9 deletions src/components/wallet/delegations-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { SteemSigner, apiClient } from '@/lib/steem/client';
import { useVestingDelegations, useExpiringVestingDelegations } from '@/hooks/use-delegations';
import {
formatSteemPowerFromVestsString,
STEEM_POWER_TICKER,
} from '@/lib/wallet/vest-steem';
import { parseAssetAmount } from '@/lib/wallet/parse-asset-amount';
import { formatTimeAgo } from '@/lib/wallet/format-time-ago';
Expand Down Expand Up @@ -40,7 +41,13 @@ type SortField = 'delegatee' | 'date' | 'amount';
type SortDir = 'asc' | 'desc';
const PAGE_SIZE = 20;

function formatVestsDisplay(vestsAsset: string): string {
function formatSpDisplay(
vestsAsset: string,
globalProps: GlobalPropsData | null
): string {
if (globalProps) {
return `${formatSteemPowerFromVestsString(vestsAsset, globalProps)} ${STEEM_POWER_TICKER}`;
}
const n = parseAssetAmount(vestsAsset);
return `${n.toLocaleString('en-US', { maximumFractionDigits: 6 })} VESTS`;
}
Expand Down Expand Up @@ -323,7 +330,7 @@ function OutgoingDelegationsTable({
: item.vesting_shares
)
}
title={formatVestsDisplay(item.vesting_shares)}
title={formatSpDisplay(item.vesting_shares, globalProps)}
>
{globalPropsLoading ? (
<Skeleton className="ml-auto h-4 w-20" />
Expand Down Expand Up @@ -394,9 +401,6 @@ function OutgoingDelegationsTable({
: globalProps
? `${formatSteemPowerFromVestsString(item.vesting_shares, globalProps)} SP`
: item.vesting_shares}
<span className="ml-1 text-xs text-muted-foreground">
({formatVestsDisplay(item.vesting_shares)})
</span>
</div>
</div>
))}
Expand Down Expand Up @@ -556,7 +560,7 @@ function ExpiringDelegationsTable({
: item.vesting_shares
)
}
title={formatVestsDisplay(item.vesting_shares)}
title={formatSpDisplay(item.vesting_shares, globalProps)}
>
{globalPropsLoading ? (
<Skeleton className="ml-auto h-4 w-20" />
Expand Down Expand Up @@ -603,9 +607,6 @@ function ExpiringDelegationsTable({
? `${formatSteemPowerFromVestsString(item.vesting_shares, globalProps)} SP`
: item.vesting_shares}
</div>
<span className="text-xs text-muted-foreground">
{formatVestsDisplay(item.vesting_shares)}
</span>
</div>
</div>
))}
Expand Down
Loading
Loading