This commit is contained in:
Danish Arora 2025-09-09 13:15:57 +05:30
parent f8aed8e199
commit 27a5e5c500
No known key found for this signature in database
GPG Key ID: 1C6EF37CDAE1426E
9 changed files with 161 additions and 88 deletions

View File

@ -1,2 +1,3 @@
VITE_REOWN_SECRET=
VITE_OPCHAN_MOCK_ORDINAL_CHECK=
VITE_OPCHAN_MOCK_ORDINAL_CHECK=
VITE_ORDISCAN_API_KEY=

19
package-lock.json generated
View File

@ -53,6 +53,7 @@
"input-otp": "^1.2.4",
"lucide-react": "^0.462.0",
"next-themes": "^0.3.0",
"ordiscan": "^1.3.0",
"react": "^18.3.1",
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
@ -12582,6 +12583,18 @@
"node": ">= 0.8.0"
}
},
"node_modules/ordiscan": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/ordiscan/-/ordiscan-1.3.0.tgz",
"integrity": "sha512-IV8yayKGIRtfkI3rQ1gu+aKQ6UmNHXB910qUuWrraCcuk6U/YkE+6X8Bh+jW2P8L8/lOfYA6DLVeKCVPkj8c6g==",
"license": "MIT",
"dependencies": {
"zod": "^3.24.1"
},
"engines": {
"node": ">=14"
}
},
"node_modules/ox": {
"version": "0.6.9",
"resolved": "https://registry.npmjs.org/ox/-/ox-0.6.9.tgz",
@ -15870,9 +15883,9 @@
}
},
"node_modules/zod": {
"version": "3.23.8",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"

View File

@ -60,6 +60,7 @@
"input-otp": "^1.2.4",
"lucide-react": "^0.462.0",
"next-themes": "^0.3.0",
"ordiscan": "^1.3.0",
"react": "^18.3.1",
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",

View File

@ -26,6 +26,7 @@ import Dashboard from './pages/Dashboard';
import Index from './pages/Index';
import ProfilePage from './pages/ProfilePage';
import BookmarksPage from './pages/BookmarksPage';
import OrdiscanPage from './pages/OrdiscanPage';
import { appkitConfig } from './lib/wallet/config';
import { WagmiProvider } from 'wagmi';
import { config } from './lib/wallet/config';
@ -52,6 +53,7 @@ const App = () => (
<Route path="/post/:postId" element={<PostPage />} />
<Route path="/profile" element={<ProfilePage />} />
<Route path="/bookmarks" element={<BookmarksPage />} />
<Route path="/ordiscan" element={<OrdiscanPage />} />
<Route path="*" element={<NotFound />} />
</Routes>
</TooltipProvider>

View File

@ -1,54 +0,0 @@
import { OrdinalApiResponse } from './types';
const BASE_URL = 'https://dashboard.logos.co/api/operators/wallet';
export class OrdinalAPI {
/**
* Fetches Ordinal operator details for a given Bitcoin address.
* @param address - The Bitcoin address to query.
* @returns A promise that resolves with the API response.
*/
async getOperatorDetails(address: string): Promise<OrdinalApiResponse> {
if (import.meta.env.VITE_OPCHAN_MOCK_ORDINAL_CHECK === 'true') {
console.log(
`[DEV] Bypassing ordinal verification for address: ${address}`
);
return {
has_operators: true,
error_message: '',
data: [],
};
}
const url = `${BASE_URL}/${address}/detail/`;
try {
const response = await fetch(url, {
method: 'GET',
headers: { Accept: 'application/json' },
});
if (!response.ok) {
const errorBody = await response.text().catch(() => '');
throw new Error(
`HTTP error! status: ${response.status}, message: ${errorBody || response.statusText}`
);
}
const data: OrdinalApiResponse = await response.json();
if (data.error_message) {
console.warn(
`API returned an error message for address ${address}: ${data.error_message}`
);
}
return data;
} catch (error) {
console.error(
`Failed to fetch ordinal details for address ${address}:`,
error
);
throw error;
}
}
}

View File

@ -1,25 +0,0 @@
export interface OrdinalDetail {
name: string;
archetype_name: string;
comp: string;
background: string;
skin: string;
helmet: string;
jacket: string;
image_200_url: string;
image_200_jpeg_url: string;
image_400_url: string;
image_400_jpeg_url: string;
image_1024_url: string;
image_1024_jpeg_url: string;
image_2048_url: string;
image_2048_jpeg_url: string;
image_pixalated_url: string;
mp4_url: string;
}
export interface OrdinalApiResponse {
has_operators: boolean;
error_message: string;
data: OrdinalDetail[];
}

View File

@ -0,0 +1,17 @@
import { Ordiscan } from 'ordiscan';
console.log(import.meta.env.VITE_ORDISCAN_API_KEY);
const ordiscan = new Ordiscan(import.meta.env.VITE_ORDISCAN_API_KEY);
export class OrdinalAPI {
/**
* Fetches Ordinal operator details for a given Bitcoin address.
* @param address - The Bitcoin address to query.
* @returns A promise that resolves with the API response.
*/
async getOperatorDetails(address: string) {
const response = await ordiscan.address.getInscriptions({address: address})
console.log(response);
return response;
}
}

110
src/pages/OrdiscanPage.tsx Normal file
View File

@ -0,0 +1,110 @@
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Loader2 } from 'lucide-react';
import { OrdinalAPI } from '@/lib/services/OrdinalService';
const ordinalAPI = new OrdinalAPI();
export default function OrdiscanPage() {
const [address, setAddress] = useState('');
const [loading, setLoading] = useState(false);
const [data, setData] = useState<unknown[] | Record<string, unknown> | null>(
null
);
const [error, setError] = useState<string | null>(null);
const handleSearch = async () => {
if (!address.trim()) {
setError('Please enter a Bitcoin address');
return;
}
setLoading(true);
setError(null);
setData(null);
try {
const result = await ordinalAPI.getOperatorDetails(address.trim());
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to fetch data');
console.error('Ordiscan API error:', err);
} finally {
setLoading(false);
}
};
return (
<div className="container mx-auto p-6 max-w-4xl">
<Card>
<CardHeader>
<CardTitle className="text-2xl font-bold">
Ordiscan Explorer
</CardTitle>
<CardDescription>
Search for Bitcoin inscriptions and ordinal operator details
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex gap-2">
<Input
placeholder="Enter Bitcoin address (e.g., bc1p...)"
value={address}
onChange={e => setAddress(e.target.value)}
onKeyDown={e => e.key === 'Enter' && handleSearch()}
className="flex-1"
/>
<Button
onClick={handleSearch}
disabled={loading}
className="min-w-[100px]"
>
{loading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Loading
</>
) : (
'Search'
)}
</Button>
</div>
{error && (
<Alert variant="destructive">
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{data && (
<Card>
<CardHeader>
<CardTitle className="text-lg">Inscriptions Data</CardTitle>
</CardHeader>
<CardContent>
<pre className="bg-muted p-4 rounded-lg overflow-auto text-sm">
{JSON.stringify(data, null, 2)}
</pre>
</CardContent>
</Card>
)}
{!data && !loading && !error && (
<div className="text-center text-muted-foreground py-8">
Enter a Bitcoin address to search for inscriptions
</div>
)}
</CardContent>
</Card>
</div>
);
}

View File

@ -59,7 +59,9 @@ export default function ProfilePage() {
const [isEditing, setIsEditing] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [callSign, setCallSign] = useState('');
const [displayPreference, setDisplayPreference] = useState(EDisplayPreference.WALLET_ADDRESS);
const [displayPreference, setDisplayPreference] = useState(
EDisplayPreference.WALLET_ADDRESS
);
const [walletWizardOpen, setWalletWizardOpen] = useState(false);
// Initialize and update local state when user data changes
@ -67,8 +69,11 @@ export default function ProfilePage() {
if (currentUser) {
// Use the same data source as the display (userInfo) for consistency
const currentCallSign = userInfo.callSign || currentUser.callSign || '';
const currentDisplayPreference = userInfo.displayPreference || currentUser.displayPreference || EDisplayPreference.WALLET_ADDRESS;
const currentDisplayPreference =
userInfo.displayPreference ||
currentUser.displayPreference ||
EDisplayPreference.WALLET_ADDRESS;
setCallSign(currentCallSign);
setDisplayPreference(currentDisplayPreference);
}
@ -174,8 +179,11 @@ export default function ProfilePage() {
const handleCancel = () => {
// Reset to the same data source as display for consistency
const currentCallSign = userInfo.callSign || currentUser.callSign || '';
const currentDisplayPreference = userInfo.displayPreference || currentUser.displayPreference || EDisplayPreference.WALLET_ADDRESS;
const currentDisplayPreference =
userInfo.displayPreference ||
currentUser.displayPreference ||
EDisplayPreference.WALLET_ADDRESS;
setCallSign(currentCallSign);
setDisplayPreference(currentDisplayPreference);
setIsEditing(false);