first day of vibe coding
This commit is contained in:
+50
@@ -0,0 +1,50 @@
|
||||
import type { TrumpPost, Candle, BotTrade, BotPerformance } from '@/types'
|
||||
|
||||
const BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'
|
||||
|
||||
async function fetchJson<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const res = await fetch(`${BASE_URL}/api${path}`, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...init,
|
||||
})
|
||||
if (!res.ok) throw new Error(`API error ${res.status}: ${path}`)
|
||||
return res.json() as Promise<T>
|
||||
}
|
||||
|
||||
export async function getPosts(limit = 20, page = 1): Promise<TrumpPost[]> {
|
||||
return fetchJson<TrumpPost[]>(`/posts?limit=${limit}&page=${page}`)
|
||||
}
|
||||
|
||||
export async function getPost(id: number): Promise<TrumpPost> {
|
||||
return fetchJson<TrumpPost>(`/posts/${id}`)
|
||||
}
|
||||
|
||||
export async function getPrices(asset: 'BTC' | 'ETH', tf: string): Promise<Candle[]> {
|
||||
return fetchJson<Candle[]>(`/prices/${asset}?tf=${tf}`)
|
||||
}
|
||||
|
||||
export async function getTrades(limit = 20, page = 1): Promise<BotTrade[]> {
|
||||
return fetchJson<BotTrade[]>(`/trades?limit=${limit}&page=${page}`)
|
||||
}
|
||||
|
||||
export async function getPerformance(): Promise<BotPerformance> {
|
||||
return fetchJson<BotPerformance>(`/performance`)
|
||||
}
|
||||
|
||||
export async function subscribe(
|
||||
wallet: string,
|
||||
signature: string,
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
return fetchJson<{ success: boolean; message: string }>(`/subscribe`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ wallet, signature }),
|
||||
})
|
||||
}
|
||||
|
||||
export async function getUser(
|
||||
wallet: string,
|
||||
): Promise<{ wallet_address: string; active: boolean; subscribed_at: string | null; hl_api_key_set: boolean; trades: BotTrade[] }> {
|
||||
return fetchJson<{ wallet_address: string; active: boolean; subscribed_at: string | null; hl_api_key_set: boolean; trades: BotTrade[] }>(
|
||||
`/user/${wallet}`,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef, useCallback } from 'react'
|
||||
|
||||
const WS_URL = process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:8000'
|
||||
|
||||
type PriceUpdate = { type: 'price'; asset: 'BTC' | 'ETH'; price: number }
|
||||
type PostUpdate = { type: 'new_post'; post: object }
|
||||
type WsMessage = PriceUpdate | PostUpdate
|
||||
|
||||
interface Handlers {
|
||||
onPrice?: (asset: 'BTC' | 'ETH', price: number) => void
|
||||
onNewPost?: (post: object) => void
|
||||
}
|
||||
|
||||
export function usePriceSocket(handlers: Handlers) {
|
||||
const wsRef = useRef<WebSocket | null>(null)
|
||||
const handlersRef = useRef(handlers)
|
||||
handlersRef.current = handlers
|
||||
|
||||
const connect = useCallback(() => {
|
||||
const ws = new WebSocket(`${WS_URL}/ws/prices`)
|
||||
wsRef.current = ws
|
||||
|
||||
ws.onmessage = (e) => {
|
||||
try {
|
||||
const msg: WsMessage = JSON.parse(e.data)
|
||||
if (msg.type === 'price' && handlersRef.current.onPrice) {
|
||||
handlersRef.current.onPrice(msg.asset, msg.price)
|
||||
} else if (msg.type === 'new_post' && handlersRef.current.onNewPost) {
|
||||
handlersRef.current.onNewPost(msg.post)
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed messages
|
||||
}
|
||||
}
|
||||
|
||||
ws.onclose = () => {
|
||||
setTimeout(connect, 3000)
|
||||
}
|
||||
|
||||
ws.onerror = () => {
|
||||
ws.close()
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
connect()
|
||||
return () => {
|
||||
wsRef.current?.close()
|
||||
}
|
||||
}, [connect])
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
export function formatPrice(n: number): string {
|
||||
return '$' + n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||||
}
|
||||
|
||||
export function formatPct(n: number): string {
|
||||
const sign = n >= 0 ? '+' : ''
|
||||
return `${sign}${n.toFixed(2)}%`
|
||||
}
|
||||
|
||||
export function formatHold(seconds: number): string {
|
||||
if (seconds < 60) return `${seconds}s`
|
||||
const h = Math.floor(seconds / 3600)
|
||||
const m = Math.floor((seconds % 3600) / 60)
|
||||
if (h === 0) return `${m}m`
|
||||
if (m === 0) return `${h}h`
|
||||
return `${h}h ${m}m`
|
||||
}
|
||||
|
||||
export function shortenAddress(addr: string): string {
|
||||
if (addr.length < 10) return addr
|
||||
return `${addr.slice(0, 6)}...${addr.slice(-4)}`
|
||||
}
|
||||
|
||||
export function sentimentColor(s: string): string {
|
||||
switch (s) {
|
||||
case 'bullish':
|
||||
return '#4ade80'
|
||||
case 'bearish':
|
||||
return '#ef4444'
|
||||
default:
|
||||
return '#555555'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { createConfig, http } from 'wagmi'
|
||||
import { mainnet } from 'wagmi/chains'
|
||||
import { metaMask } from 'wagmi/connectors'
|
||||
|
||||
export const chains = [mainnet] as const
|
||||
|
||||
export const config = createConfig({
|
||||
chains,
|
||||
connectors: [metaMask()],
|
||||
transports: {
|
||||
[mainnet.id]: http(),
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user