tangled
alpha
login
or
join now
margin.at
/
margin
87
fork
atom
Write on the margins of the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
87
fork
atom
overview
issues
4
pulls
1
pipelines
fix profile page
scanash.com
1 month ago
9870ecb9
d76abc06
+63
-37
2 changed files
expand all
collapse all
unified
split
backend
internal
db
tags.go
web
src
pages
Profile.jsx
+36
-11
backend/internal/db/tags.go
···
1
1
package db
2
2
3
3
-
import "fmt"
4
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
11
-
query := `
9
9
+
var query string
10
10
+
if db.driver == "postgres" {
11
11
+
query = `
12
12
+
SELECT
13
13
+
value as tag,
14
14
+
COUNT(*) as count
15
15
+
FROM annotations, json_array_elements_text(tags_json::json) as value
16
16
+
WHERE tags_json IS NOT NULL
17
17
+
AND tags_json != ''
18
18
+
AND tags_json != '[]'
19
19
+
AND created_at > NOW() - INTERVAL '7 days'
20
20
+
GROUP BY tag
21
21
+
HAVING count > 2
22
22
+
ORDER BY count DESC
23
23
+
LIMIT ?
24
24
+
`
25
25
+
rows, err := db.Query(db.Rebind(query), limit)
26
26
+
if err != nil {
27
27
+
return nil, err
28
28
+
}
29
29
+
defer rows.Close()
30
30
+
31
31
+
var tags []TrendingTag
32
32
+
for rows.Next() {
33
33
+
var t TrendingTag
34
34
+
if err := rows.Scan(&t.Tag, &t.Count); err != nil {
35
35
+
return nil, err
36
36
+
}
37
37
+
tags = append(tags, t)
38
38
+
}
39
39
+
return tags, nil
40
40
+
}
41
41
+
42
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
19
-
AND created_at > %s
50
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
25
-
26
26
-
dateFilter := "datetime('now', '-7 days')"
27
27
-
if db.driver == "postgres" {
28
28
-
dateFilter = "NOW() - INTERVAL '7 days'"
29
29
-
}
30
30
-
31
31
-
rows, err := db.Query(db.Rebind(fmt.Sprintf(query, dateFilter)), limit)
56
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
84
-
if (authLoading) {
85
85
-
return (
86
86
-
<div className="profile-page">
87
87
-
<div className="feed">
88
88
-
{[1, 2, 3].map((i) => (
89
89
-
<div key={i} className="card">
90
90
-
<div
91
91
-
className="skeleton skeleton-text"
92
92
-
style={{ width: "40%" }}
93
93
-
/>
94
94
-
<div className="skeleton skeleton-text" />
95
95
-
<div
96
96
-
className="skeleton skeleton-text"
97
97
-
style={{ width: "60%" }}
98
98
-
/>
99
99
-
</div>
100
100
-
))}
101
101
-
</div>
102
102
-
</div>
103
103
-
);
104
104
-
}
105
105
-
106
106
-
if (!handle) {
107
107
-
return <Navigate to="/login" replace />;
108
108
-
}
109
109
-
110
84
useEffect(() => {
85
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
180
+
181
181
+
if (authLoading) {
182
182
+
return (
183
183
+
<div className="profile-page">
184
184
+
<div className="feed">
185
185
+
{[1, 2, 3].map((i) => (
186
186
+
<div key={i} className="card">
187
187
+
<div
188
188
+
className="skeleton skeleton-text"
189
189
+
style={{ width: "40%" }}
190
190
+
/>
191
191
+
<div className="skeleton skeleton-text" />
192
192
+
<div
193
193
+
className="skeleton skeleton-text"
194
194
+
style={{ width: "60%" }}
195
195
+
/>
196
196
+
</div>
197
197
+
))}
198
198
+
</div>
199
199
+
</div>
200
200
+
);
201
201
+
}
202
202
+
203
203
+
if (!handle) {
204
204
+
return <Navigate to="/login" replace />;
205
205
+
}
205
206
206
207
const displayName = profile?.displayName || profile?.handle || handle;
207
208
const displayHandle =