1/* ===================================================================== 2 js/map-view/api.js 3 Dedicated API layer for Map View. 4 Handles fetching project bundles, presentations, and other async data. 5 ===================================================================== */ 6 7/** 8 * Fetches the full project bundle by Slug. 9 * Used for public URLs: /p/my-map 10 */ 11export async function fetchProjectBySlug(slug, username) { 12 if (!slug) throw new Error("Slug is required"); 13 14 try { 15 // features=0: skip the feature payload â map-view fetches 16 // /api/features in parallel instead (see bootstrap in index.js). 17 // On large projects the bundle otherwise ships every feature 18 // twice, once here and once via the authoritative refetch. 19 // slim=1: strip the two embedded settings copies of the layer 20 // list â map-view reads the top-level `layers` array only. 21 const qs = new URLSearchParams({ features: '0', slim: '1' }); 22 if (username) qs.set('owner', username); 23 const response = await fetch(`/api/projects/slug/${slug}?${qs}`); 24 if (!response.ok) { 25 throw new Error(`Project not found (Status: ${response.status})`); 26 } 27 return await response.json(); 28 } catch (err) { 29 console.error("[API] fetchProjectBySlug failed:", err); 30 throw err; 31 } 32} 33 34/** 35 * Fetches the full project bundle by ID. 36 * Used for editor preview or ID-based routing: /map-view/123 37 */ 38export async function fetchProjectById(id) { 39 if (!id) throw new Error("ID is required"); 40 41 try { 42 // Assuming your backend supports this, or we adapt the route 43 const response = await fetch(`/api/projects/${id}/bundle`); 44 // Note: If /bundle doesn't exist, we might need to chain requests, 45 // but typically you want a single endpoint for the viewer. 46 47 if (!response.ok) { 48 throw new Error(`Project not found (Status: ${response.status})`); 49 } 50 return await response.json(); 51 } catch (err) { 52 console.error("[API] fetchProjectById failed:", err); 53 throw err; 54 } 55} 56 57 58/** 59 * [NEW] Fetches current user session to determine Admin/Guest status. 60 */ 61export async function fetchSession() { 62 try { 63 const r = await fetch('/api/session-info'); 64 if (!r.ok) return { loggedIn: false, role: 'Guest' }; 65 return await r.json(); 66 } catch (err) { 67 console.warn("[API] Session fetch failed", err); 68 return { loggedIn: false, role: 'Guest' }; 69 } 70} 71 72export async function fetchJson(url, options = {}) { 73 const r = await fetch(url, options); 74 75 if (!r.ok) { 76 const err = new Error(`HTTP ${r.status}`); 77 err.status = r.status; 78 79 // ð 404 is often "no data yet" â don't scream about it 80 if (r.status !== 404) { 81 console.error(`[API] fetchJson failed for ${url}:`, err); 82 } 83 84 throw err; 85 } 86 87 return await r.json(); 88} 89/** 90 * Fetches presentations associated with the project. 91 * Originally found in initPresentationsModal logic. 92 */ 93export async function fetchPresentations(projectId) { 94 if (!projectId) return []; 95 96 try { 97 const response = await fetch(`/api/presentations/project/${projectId}`); 98 if (!response.ok) return []; 99 100 const data = await response.json(); 101 return Array.isArray(data) ? data : []; 102 } catch (err) { 103 console.warn("[API] fetchPresentations failed:", err); 104 return []; 105 } 106} 107 108/** 109 * Fetches public presentations for a project by slug. 110 * Used by the map-view Presentations menu button. 111 * No auth required â returns only is_public=1 presentations. 112 */ 113export async function fetchPublicPresentations(projectSlug) { 114 if (!projectSlug) return []; 115 try { 116 const res = await fetch(`/api/presentations/public-list/${projectSlug}`); 117 if (!res.ok) return []; 118 const data = await res.json(); 119 return Array.isArray(data) ? data : []; 120 } catch (err) { 121 console.warn('[API] fetchPublicPresentations failed:', err); 122 return []; 123 } 124} 125 126// api.js 127 128function normalizeGeometry(feature) { 129 // API already sends GeoJSON geometry 130 if (feature.geometry && feature.geometry.type) { 131 return feature.geometry; 132 } 133 134 // legacy fallback (just in case) 135 if (feature.geo_shape && feature.geo_shape.type) { 136 return feature.geo_shape; 137 } 138 139 return null; 140} 141 142export async function fetchFeatures(params = {}) { 143 // ð Admins/editors need hidden features (is_hidden=1) included 144 // so they can see and un-hide them. The server filters them out 145 // unless include_hidden=1 is explicitly passed. 146 const role = window.wmState?.user?.projectRole || 'viewer'; 147 if ((role === 'admin' || role === 'editor') && !params.include_hidden) { 148 params = { ...params, include_hidden: '1' }; 149 } 150 151 const qs = new URLSearchParams(params).toString(); 152 const url = qs ? `/api/features?${qs}` : '/api/features'; 153 154 const res = await fetch(url, { credentials: 'include' }); 155 156 if (!res.ok) { 157 console.warn('[fetchFeatures] HTTP error', res.status); 158 return []; 159 } 160 161 const json = await res.json(); 162 163 // â SUPPORT BOTH API SHAPES 164 const rawFeatures = Array.isArray(json) 165 ? json 166 : Array.isArray(json?.features) 167 ? json.features 168 : []; 169 170 if (!rawFeatures.length) { 171 console.warn('[fetchFeatures] No features returned', json); 172 return []; 173 } 174 175 const features = rawFeatures.map(f => { 176 const geometry = normalizeGeometry(f); 177 178 if (!geometry) { 179 console.warn('[fetchFeatures] Feature has no geometry', f.id); 180 } 181 182 return { 183 ...f, 184 geometry 185 }; 186 }); 187 188 console.log('[fetchFeatures] Loaded features:', features.length); 189 return features; 190} 191 192export async function fetchIcons(projectId) { 193 if (!projectId) return []; 194 const res = await fetch(`/api/icons/${projectId}`); 195 if (!res.ok) throw new Error('Failed to fetch icons'); 196 const data = await res.json(); 197 return data.icons || []; 198}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.