Skip to content
Open
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
1 change: 1 addition & 0 deletions dongle/app/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export default function AdminDashboard() {
publicKey={gate.publicKey}
onConnect={gate.connectWallet}
onDisconnect={gate.disconnectWallet}
onRetry={gate.retryAccountLoad}
/>
)}
</div>
Expand Down
1 change: 1 addition & 0 deletions dongle/app/profile/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export default function ProfilePage() {
publicKey={gate.publicKey}
onConnect={gate.connectWallet}
onDisconnect={gate.disconnectWallet}
onRetry={gate.retryAccountLoad}
/>
)}
</div>
Expand Down
1 change: 1 addition & 0 deletions dongle/app/projects/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export default function EditProjectPage() {
publicKey={gate.publicKey}
onConnect={gate.connectWallet}
onDisconnect={gate.disconnectWallet}
onRetry={gate.retryAccountLoad}
/>
)}
</div>
Expand Down
31 changes: 29 additions & 2 deletions dongle/app/projects/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export default function ProjectDetailPage() {
const [reportingReview, setReportingReview] = useState<Review | null>(null);
const [reviewSort, setReviewSort] = useState<"newest" | "highest" | "lowest" | "mine">("newest");
const [verificationStatus, setVerificationStatus] = useState<"NONE" | "PENDING" | "VERIFIED" | "REJECTED" | null>(null);
const [verificationError, setVerificationError] = useState<string | null>(null);
const [updates, setUpdates] = useState<ProjectUpdate[]>([]);
const [isAddingUpdate, setIsAddingUpdate] = useState(false);
const [editingUpdate, setEditingUpdate] = useState<ProjectUpdate | null>(null);
Expand Down Expand Up @@ -117,6 +118,7 @@ export default function ProjectDetailPage() {

// Fetch verification status with cancellation support
const fetchVerification = async () => {
if (!cancelled) setVerificationError(null);
try {
const status = await sorobanService.getVerificationStatus(projectId, abortController.signal);
if (!cancelled) {
Expand All @@ -125,7 +127,9 @@ export default function ProjectDetailPage() {
} catch (error) {
if (!cancelled) {
console.error("Failed to fetch verification status:", error);
setVerificationStatus("NONE");
setVerificationError(
error instanceof Error ? error.message : "Failed to load verification status",
);
}
}
};
Expand All @@ -144,6 +148,21 @@ export default function ProjectDetailPage() {
};
}, [projectId, gate.publicKey]);

const retryVerification = React.useCallback(() => {
setVerificationError(null);
setVerificationStatus(null);
const abortController = new AbortController();
void sorobanService
.getVerificationStatus(projectId, abortController.signal)
.then(setVerificationStatus)
.catch((err) => {
console.error("Failed to fetch verification status:", err);
setVerificationError(
err instanceof Error ? err.message : "Failed to load verification status",
);
});
}, [projectId]);

const actualRating = React.useMemo(() => {
if (reviews.length === 0) return project?.rating || 0;
const sum = reviews.reduce((acc, r) => acc + r.rating, 0);
Expand Down Expand Up @@ -794,6 +813,7 @@ export default function ProjectDetailPage() {
publicKey={gate.publicKey}
onConnect={gate.connectWallet}
onDisconnect={gate.disconnectWallet}
onRetry={gate.retryAccountLoad}
compact
/>
</div>
Expand Down Expand Up @@ -825,7 +845,14 @@ export default function ProjectDetailPage() {
<div className="space-y-6">
{/* Verification Status */}
<div className="bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-3xl p-6">
<h3 className="text-lg font-bold mb-4">Verification Status</h3>
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-bold">Verification Status</h3>
{verificationError && (
<Button variant="outline" size="sm" onClick={retryVerification}>
Retry
</Button>
)}
</div>
<VerificationStatus initialProjectId={project.id} />
</div>

Expand Down
1 change: 1 addition & 0 deletions dongle/app/projects/new/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default function NewProjectPage() {
publicKey={gate.publicKey}
onConnect={gate.connectWallet}
onDisconnect={gate.disconnectWallet}
onRetry={gate.retryAccountLoad}
/>
)}

Expand Down
1 change: 1 addition & 0 deletions dongle/app/reviews/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ export default function ReviewsPage() {
publicKey={gate.publicKey}
onConnect={gate.connectWallet}
onDisconnect={gate.disconnectWallet}
onRetry={gate.retryAccountLoad}
compact
/>
</div>
Expand Down
1 change: 1 addition & 0 deletions dongle/app/verify/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default function VerifyPage() {
publicKey={gate.publicKey}
onConnect={gate.connectWallet}
onDisconnect={gate.disconnectWallet}
onRetry={gate.retryAccountLoad}
/>
</div>
)
Expand Down
29 changes: 25 additions & 4 deletions dongle/components/verify/VerificationStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,23 @@ export default function VerificationStatus({ initialProjectId }: VerificationSta
const [searchInput, setSearchInput] = useState(initialProjectId ?? "");
const [status, setStatus] = useState<Status>("NONE");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const isMountedRef = React.useRef(true);

const fetchStatus = async (id: string) => {
if (!id) return;
setIsLoading(true);
setError(null);
try {
const result = await sorobanService.getVerificationStatus(id);
if (!isMountedRef.current) return;
setStatus(result);
} catch (error) {
} catch (err) {
if (!isMountedRef.current) return;
console.error(error);
setStatus("NONE");
const msg = err instanceof Error ? err.message : "Failed to check verification status";
console.error(err);
setError(msg);
} finally {
if (isMountedRef.current) {
setIsLoading(false);
Expand Down Expand Up @@ -107,7 +110,25 @@ export default function VerificationStatus({ initialProjectId }: VerificationSta
</form>
</Card>

{projectId && (
{projectId && error && (
<Card className="transition-all duration-300 border-2 border-red-500/50 bg-red-500/5" padding="lg">
<div className="flex flex-col items-center text-center gap-4">
<AlertCircle className="w-12 h-12 text-red-500" />
<h3 className="text-xl font-bold">Check Failed</h3>
<p className="text-zinc-500 dark:text-zinc-400 text-sm">{error}</p>
<Button
variant="secondary"
size="sm"
onClick={() => fetchStatus(projectId)}
isLoading={isLoading}
>
Try Again
</Button>
</div>
</Card>
)}

{projectId && !error && (
<Card className={`transition-all duration-300 border-2 ${config.color}`} padding="lg">
<div className="flex flex-col items-center text-center gap-4">
{isLoading ? (
Expand Down
9 changes: 8 additions & 1 deletion dongle/components/wallet/WalletStatePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface WalletStatePanelProps {
publicKey?: string | null;
onConnect?: () => void;
onDisconnect?: () => void;
onRetry?: () => void;
compact?: boolean;
className?: string;
}
Expand All @@ -36,6 +37,7 @@ const STATE_ICONS: Record<
disconnected: <Wallet className="w-16 h-16 text-zinc-300 dark:text-zinc-700" />,
"wrong-network": <AlertTriangle className="w-16 h-16 text-amber-500" />,
"account-not-funded": <AlertCircle className="w-16 h-16 text-amber-500" />,
"account-error": <AlertCircle className="w-16 h-16 text-red-500" />,
};

export default function WalletStatePanel({
Expand All @@ -45,6 +47,7 @@ export default function WalletStatePanel({
publicKey,
onConnect,
onDisconnect,
onRetry,
compact = false,
className = "",
}: WalletStatePanelProps) {
Expand Down Expand Up @@ -116,7 +119,11 @@ export default function WalletStatePanel({
renderAction(
content.primaryAction,
"primary",
state === "disconnected" ? onConnect : undefined,
state === "disconnected"
? onConnect
: state === "account-error"
? onRetry
: undefined,
)}
{content.secondaryAction &&
renderAction(
Expand Down
12 changes: 11 additions & 1 deletion dongle/components/wallet/wallet-states.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export type WalletPageState =
| "disconnected"
| "wrong-network"
| "account-loading"
| "account-not-funded";
| "account-not-funded"
| "account-error";

export interface WalletStateContent {
title: string;
Expand Down Expand Up @@ -104,6 +105,15 @@ export function getWalletStateContent(
label: "Disconnect Wallet",
},
};
case "account-error":
return {
title: "Failed to Load Account",
description:
"Could not retrieve your Stellar account data. This is usually a temporary network issue.",
primaryAction: {
label: "Try Again",
},
};
default:
return {
title: "Wallet Required",
Expand Down
22 changes: 21 additions & 1 deletion dongle/hooks/useWalletPageGate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface WalletPageGateResult {
connectWallet: () => Promise<void>;
disconnectWallet: () => void;
isConnecting: boolean;
retryAccountLoad: (() => Promise<void>) | undefined;
}

/**
Expand All @@ -41,7 +42,7 @@ export function useWalletPageGate(
disconnectWallet,
} = useWallet();

const { loading: accountLoading, error: accountError } = useStellarAccount();
const { loading: accountLoading, error: accountError, refetch: refetchAccount } = useStellarAccount();

if (isFreighterAvailable === false) {
return {
Expand All @@ -51,6 +52,7 @@ export function useWalletPageGate(
connectWallet,
disconnectWallet,
isConnecting,
retryAccountLoad: undefined,
};
}

Expand All @@ -62,6 +64,7 @@ export function useWalletPageGate(
connectWallet,
disconnectWallet,
isConnecting,
retryAccountLoad: undefined,
};
}

Expand All @@ -73,6 +76,7 @@ export function useWalletPageGate(
connectWallet,
disconnectWallet,
isConnecting,
retryAccountLoad: undefined,
};
}

Expand All @@ -84,6 +88,7 @@ export function useWalletPageGate(
connectWallet,
disconnectWallet,
isConnecting,
retryAccountLoad: undefined,
};
}

Expand All @@ -96,6 +101,7 @@ export function useWalletPageGate(
connectWallet,
disconnectWallet,
isConnecting,
retryAccountLoad: undefined,
};
}

Expand All @@ -107,6 +113,19 @@ export function useWalletPageGate(
connectWallet,
disconnectWallet,
isConnecting,
retryAccountLoad: undefined,
};
}

if (accountError) {
return {
state: "account-error",
publicKey,
walletNetworkLabel,
connectWallet,
disconnectWallet,
isConnecting,
retryAccountLoad: refetchAccount,
};
}
}
Expand All @@ -118,5 +137,6 @@ export function useWalletPageGate(
connectWallet,
disconnectWallet,
isConnecting,
retryAccountLoad: undefined,
};
}