the statusphere demo reworked into a vite/react app in a monorepo
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

1import { AppBskyActorDefs, XyzStatusphereDefs } from '@statusphere/lexicon' 2 3// Use '/api' prefix consistently for all API calls 4const API_URL = '/api' 5 6// Helper function for logging API actions 7function logApiCall( 8 method: string, 9 endpoint: string, 10 status?: number, 11 error?: any, 12) { 13 const statusStr = status ? `[${status}]` : '' 14 const errorStr = error 15 ? ` - Error: ${error.message || JSON.stringify(error)}` 16 : '' 17 console.log(`🔄 API ${method} ${endpoint} ${statusStr}${errorStr}`) 18} 19 20export interface User { 21 did: string 22 profile: AppBskyActorDefs.ProfileView 23 status?: XyzStatusphereDefs.StatusView 24} 25 26// API service 27export const api = { 28 // Login 29 async login(handle: string) { 30 const url = `${API_URL}/login` 31 logApiCall('POST', url) 32 33 const response = await fetch(url, { 34 method: 'POST', 35 headers: { 36 'Content-Type': 'application/json', 37 }, 38 credentials: 'include', 39 body: JSON.stringify({ handle }), 40 }) 41 42 if (!response.ok) { 43 const error = await response.json() 44 throw new Error(error.error || 'Login failed') 45 } 46 47 return response.json() 48 }, 49 50 // Logout 51 async logout() { 52 const url = `${API_URL}/logout` 53 logApiCall('POST', url) 54 const response = await fetch(url, { 55 method: 'POST', 56 credentials: 'include', 57 }) 58 59 if (!response.ok) { 60 throw new Error('Logout failed') 61 } 62 63 return response.json() 64 }, 65 66 // Get current user 67 async getCurrentUser() { 68 const url = `${API_URL}/user` 69 logApiCall('GET', url) 70 try { 71 const headers = { 72 Accept: 'application/json', 73 } 74 75 const response = await fetch(url, { 76 credentials: 'include', // This is crucial for sending cookies 77 headers, 78 cache: 'no-cache', // Don't cache this request 79 }) 80 81 logApiCall('GET', '/user', response.status) 82 83 if (!response.ok) { 84 if (response.status === 401) { 85 return null 86 } 87 88 // Try to get error details 89 let errorText = '' 90 try { 91 const errorData = await response.text() 92 errorText = errorData 93 } catch (e) { 94 // Ignore error reading error 95 } 96 97 throw new Error( 98 `Failed to get user: ${response.status} ${response.statusText} ${errorText}`, 99 ) 100 } 101 102 return response.json() 103 } catch (error) { 104 logApiCall('GET', '/user', undefined, error) 105 if ( 106 error instanceof TypeError && 107 error.message.includes('Failed to fetch') 108 ) { 109 console.error('Network error - Unable to connect to API server') 110 } 111 throw error 112 } 113 }, 114 115 // Get statuses 116 async getStatuses() { 117 const url = `${API_URL}/statuses` 118 logApiCall('GET', url) 119 const response = await fetch(url, { 120 credentials: 'include', 121 }) 122 123 if (!response.ok) { 124 throw new Error('Failed to get statuses') 125 } 126 127 return response.json() as Promise<{ 128 statuses: XyzStatusphereDefs.StatusView[] 129 }> 130 }, 131 132 // Create status 133 async createStatus(status: string) { 134 const url = `${API_URL}/status` 135 logApiCall('POST', url) 136 const response = await fetch(url, { 137 method: 'POST', 138 headers: { 139 'Content-Type': 'application/json', 140 }, 141 credentials: 'include', 142 body: JSON.stringify({ status }), 143 }) 144 145 if (!response.ok) { 146 const error = await response.json() 147 throw new Error(error.error || 'Failed to create status') 148 } 149 150 return response.json() 151 }, 152} 153 154export default api