mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-02 12:53:10 +00:00
wip
This commit is contained in:
parent
f8aed8e199
commit
27a5e5c500
@ -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
19
package-lock.json
generated
@ -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"
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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[];
|
||||
}
|
||||
17
src/lib/services/OrdinalService.ts
Normal file
17
src/lib/services/OrdinalService.ts
Normal 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
110
src/pages/OrdiscanPage.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user