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
36 changes: 25 additions & 11 deletions pascals-ledger/src/app/verify/[hashId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useState, useEffect } from 'react';
import { use, useState, useEffect } from 'react';
import Link from 'next/link';
import Image from 'next/image';

Expand All @@ -16,18 +16,19 @@ interface VerificationData {
verified: boolean;
}

export default function VerifyPage({ params }: { params: { hashId: string } }) {
export default function VerifyPage({ params }: { params: Promise<{ hashId: string }> }) {
const { hashId } = use(params);
Comment on lines +19 to +20

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop wrapping route params in React.use()

The verify page is a 'use client' component, so its params prop is sent from the server as a fully resolved, serializable object. Changing the signature to params: Promise<{ hashId: string }> and calling const { hashId } = use(params); passes that plain object into React’s use() hook, which immediately throws Invalid use() call because use() only accepts promises/contexts. As a result /verify/[hashId] now crashes before issuing the API request. Keep params typed as { hashId: string } and read the value directly instead of calling use().

Useful? React with 👍 / 👎.

const [data, setData] = useState<VerificationData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');

useEffect(() => {
fetchVerificationData();
}, [params.hashId]);
}, [hashId]);
Comment on lines 24 to +27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While the current implementation works, it doesn't fully align with the rules of hooks, which can make the code more fragile. The useEffect hook implicitly depends on fetchVerificationData, which is defined outside of it. The react-hooks/exhaustive-deps ESLint rule would require fetchVerificationData to be in the dependency array.

To address this without causing an infinite re-render loop, you can wrap fetchVerificationData in a useCallback hook, with hashId as its dependency. Then, you can safely add fetchVerificationData to the useEffect's dependency array.

Here's an example of how you could refactor it:

const fetchVerificationData = useCallback(async () => {
  // ... fetch logic using hashId
}, [hashId]);

useEffect(() => {
  fetchVerificationData();
}, [fetchVerificationData]);

This change would make your component more robust and strictly follow React's best practices for handling side effects.


const fetchVerificationData = async () => {
try {
const response = await fetch(`/api/verify/${params.hashId}`);
const response = await fetch(`/api/verify/${hashId}`);
const result = await response.json();

if (result.success) {
Expand Down Expand Up @@ -154,17 +155,30 @@ export default function VerifyPage({ params }: { params: { hashId: string } }) {
<h3 className="text-sm font-semibold text-blue-300 mb-1">
Environmental Entropy
</h3>
<div className="text-sm text-blue-200">
{data?.entropyMetadata?.weather && (
<div className="text-sm text-blue-200 space-y-1">
{data?.entropyMetadata?.weather ? (
<p>
Weather: {data.entropyMetadata.weather.temperature}°C,{' '}
{data.entropyMetadata.weather.conditions}
</p>
)}
{data?.entropyMetadata?.location && (
<p>
Location: {data.entropyMetadata.location.city},{' '}
{data.entropyMetadata.location.country}
) : null}
{data?.entropyMetadata?.location ? (
<>
<p>
Location: {data.entropyMetadata.location.city}
{data.entropyMetadata.location.country && `, ${data.entropyMetadata.location.country}`}
</p>
{data.entropyMetadata.location.coordinates && (
<p className="text-slate-400 text-xs">
Approx. coordinates: {Math.floor(data.entropyMetadata.location.coordinates.lat)}°, {Math.floor(data.entropyMetadata.location.coordinates.lon)}°
<span className="italic"> (truncated for privacy - precise coordinates used in hash)</span>
</p>
)}
</>
) : null}
{!data?.entropyMetadata?.weather && !data?.entropyMetadata?.location && (
<p className="text-slate-400 italic">
No environmental data collected for this hash
</p>
)}
</div>
Expand Down
14 changes: 14 additions & 0 deletions pascals-ledger/src/lib/entropy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,20 @@ export async function generateEntropyMetadata(
location = {
city: options.city,
};
} else {
// Fallback to IP-based geolocation
const ipLocation = await getIPLocation();
if (ipLocation) {
weather = await fetchWeatherData(ipLocation.lat, ipLocation.lon);
location = {
city: ipLocation.city,
country: ipLocation.country,
coordinates: {
lat: ipLocation.lat,
lon: ipLocation.lon,
},
};
}
}

const metadata: EntropyMetadata = {
Expand Down