diff --git a/README.md b/README.md index c9cb3aa..8efa822 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ## TODOs - [ ] replace mock wallet connection/disconnection -- [ ] replace mock Ordinal verification (API) +- [x] replace mock Ordinal verification (API) - [ ] figure out using actual icons for cells - [ ] store message cache in indexedDB -- make app local-first (update from/to Waku when available) \ No newline at end of file diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 56a0d81..3be908b 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -1,7 +1,7 @@ - import React, { createContext, useContext, useState, useEffect } from 'react'; import { useToast } from '@/components/ui/use-toast'; import { User } from '@/types'; +import { OrdinalAPI } from '@/lib/identity/ordinal'; interface AuthContextType { currentUser: User | null; @@ -18,6 +18,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const [currentUser, setCurrentUser] = useState(null); const [isAuthenticating, setIsAuthenticating] = useState(false); const { toast } = useToast(); + const ordinalApi = new OrdinalAPI(); // Check for existing session on mount useEffect(() => { @@ -87,9 +88,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { }); }; - // Mock Ordinal verification const verifyOrdinal = async () => { - if (!currentUser) { + if (!currentUser || !currentUser.address) { toast({ title: "Not Connected", description: "Please connect your wallet first.", @@ -100,28 +100,43 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { setIsAuthenticating(true); try { - //TODO: replace with actual Ordinal verification + toast({ title: "Verifying Ordinal", description: "Checking your wallet for Ordinal Operators..." }); + + const response = await ordinalApi.getOperatorDetails(currentUser.address); + const hasOperators = response.has_operators; + const updatedUser = { ...currentUser, - ordinalOwnership: true, - signature: "mockSignature123", + ordinalOwnership: hasOperators, lastChecked: Date.now(), }; setCurrentUser(updatedUser); localStorage.setItem('opchan-user', JSON.stringify(updatedUser)); - toast({ - title: "Ordinal Verified", - description: "You can now post and interact with the forum.", - }); + if (hasOperators) { + toast({ + title: "Ordinal Verified", + description: "You can now post and interact with the forum.", + }); + } else { + toast({ + title: "Verification Failed", + description: "No Ordinal Operators found in the connected wallet.", + variant: "destructive", + }); + } - return true; + return hasOperators; } catch (error) { console.error("Error verifying Ordinal:", error); + let errorMessage = "Failed to verify Ordinal ownership. Please try again."; + if (error instanceof Error) { + errorMessage = error.message; + } toast({ - title: "Verification Failed", - description: "Failed to verify Ordinal ownership. Please try again.", + title: "Verification Error", + description: errorMessage, variant: "destructive", }); return false; diff --git a/src/lib/identity/ordinal.ts b/src/lib/identity/ordinal.ts new file mode 100644 index 0000000..f988da4 --- /dev/null +++ b/src/lib/identity/ordinal.ts @@ -0,0 +1,36 @@ +import { OrdinalApiResponse } from './types'; + +const BASE_URL = 'https://exit-test-567058b69f45.herokuapp.com/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 { + 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; + } + } +} \ No newline at end of file diff --git a/src/lib/identity/types.ts b/src/lib/identity/types.ts new file mode 100644 index 0000000..4b95159 --- /dev/null +++ b/src/lib/identity/types.ts @@ -0,0 +1,25 @@ +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[]; +} \ No newline at end of file