Write on the margins of the internet. Powered by the AT Protocol. margin.at
extension web atproto comments

fix profile page

+63 -37
+36 -11
backend/internal/db/tags.go
··· 1 1 package db 2 2 3 - import "fmt" 4 - 5 3 type TrendingTag struct { 6 4 Tag string `json:"tag"` 7 5 Count int `json:"count"` 8 6 } 9 7 10 8 func (db *DB) GetTrendingTags(limit int) ([]TrendingTag, error) { 11 - query := ` 9 + var query string 10 + if db.driver == "postgres" { 11 + query = ` 12 + SELECT 13 + value as tag, 14 + COUNT(*) as count 15 + FROM annotations, json_array_elements_text(tags_json::json) as value 16 + WHERE tags_json IS NOT NULL 17 + AND tags_json != '' 18 + AND tags_json != '[]' 19 + AND created_at > NOW() - INTERVAL '7 days' 20 + GROUP BY tag 21 + HAVING count > 2 22 + ORDER BY count DESC 23 + LIMIT ? 24 + ` 25 + rows, err := db.Query(db.Rebind(query), limit) 26 + if err != nil { 27 + return nil, err 28 + } 29 + defer rows.Close() 30 + 31 + var tags []TrendingTag 32 + for rows.Next() { 33 + var t TrendingTag 34 + if err := rows.Scan(&t.Tag, &t.Count); err != nil { 35 + return nil, err 36 + } 37 + tags = append(tags, t) 38 + } 39 + return tags, nil 40 + } 41 + 42 + query = ` 12 43 SELECT 13 44 json_each.value as tag, 14 45 COUNT(*) as count ··· 16 47 WHERE tags_json IS NOT NULL 17 48 AND tags_json != '' 18 49 AND tags_json != '[]' 19 - AND created_at > %s 50 + AND created_at > datetime('now', '-7 days') 20 51 GROUP BY tag 21 52 HAVING count > 2 22 53 ORDER BY count DESC 23 54 LIMIT ? 24 55 ` 25 - 26 - dateFilter := "datetime('now', '-7 days')" 27 - if db.driver == "postgres" { 28 - dateFilter = "NOW() - INTERVAL '7 days'" 29 - } 30 - 31 - rows, err := db.Query(db.Rebind(fmt.Sprintf(query, dateFilter)), limit) 56 + rows, err := db.Query(db.Rebind(query), limit) 32 57 if err != nil { 33 58 return nil, err 34 59 }
+27 -26
web/src/pages/Profile.jsx
··· 81 81 const handle = routeHandle || user?.did || user?.handle; 82 82 const isOwnProfile = user && (user.did === handle || user.handle === handle); 83 83 84 - if (authLoading) { 85 - return ( 86 - <div className="profile-page"> 87 - <div className="feed"> 88 - {[1, 2, 3].map((i) => ( 89 - <div key={i} className="card"> 90 - <div 91 - className="skeleton skeleton-text" 92 - style={{ width: "40%" }} 93 - /> 94 - <div className="skeleton skeleton-text" /> 95 - <div 96 - className="skeleton skeleton-text" 97 - style={{ width: "60%" }} 98 - /> 99 - </div> 100 - ))} 101 - </div> 102 - </div> 103 - ); 104 - } 105 - 106 - if (!handle) { 107 - return <Navigate to="/login" replace />; 108 - } 109 - 110 84 useEffect(() => { 85 + if (!handle) return; 111 86 async function fetchProfile() { 112 87 try { 113 88 setLoading(true); ··· 202 177 alert("Failed to delete key: " + err.message); 203 178 } 204 179 }; 180 + 181 + if (authLoading) { 182 + return ( 183 + <div className="profile-page"> 184 + <div className="feed"> 185 + {[1, 2, 3].map((i) => ( 186 + <div key={i} className="card"> 187 + <div 188 + className="skeleton skeleton-text" 189 + style={{ width: "40%" }} 190 + /> 191 + <div className="skeleton skeleton-text" /> 192 + <div 193 + className="skeleton skeleton-text" 194 + style={{ width: "60%" }} 195 + /> 196 + </div> 197 + ))} 198 + </div> 199 + </div> 200 + ); 201 + } 202 + 203 + if (!handle) { 204 + return <Navigate to="/login" replace />; 205 + } 205 206 206 207 const displayName = profile?.displayName || profile?.handle || handle; 207 208 const displayHandle =