diff --git a/backend/internal/api/annotations.go b/backend/internal/api/annotations.go index 8806b73..948457f 100644 --- a/backend/internal/api/annotations.go +++ b/backend/internal/api/annotations.go @@ -72,6 +72,9 @@ func (s *AnnotationService) CreateAnnotation(w http.ResponseWriter, r *http.Requ } record := xrpc.NewAnnotationRecordWithMotivation(req.URL, urlHash, req.Text, req.Selector, req.Title, motivation) + if len(req.Tags) > 0 { + record.Tags = req.Tags + } var result *xrpc.CreateRecordOutput err = s.refresher.ExecuteWithAutoRefresh(r, session, func(client *xrpc.Client, did string) error { @@ -98,6 +101,13 @@ func (s *AnnotationService) CreateAnnotation(w http.ResponseWriter, r *http.Requ selectorJSONPtr = &selectorStr } + var tagsJSONPtr *string + if len(req.Tags) > 0 { + tagsBytes, _ := json.Marshal(req.Tags) + tagsStr := string(tagsBytes) + tagsJSONPtr = &tagsStr + } + cid := result.CID did := session.DID annotation := &db.Annotation{ @@ -110,6 +120,7 @@ func (s *AnnotationService) CreateAnnotation(w http.ResponseWriter, r *http.Requ TargetHash: urlHash, TargetTitle: targetTitlePtr, SelectorJSON: selectorJSONPtr, + TagsJSON: tagsJSONPtr, CreatedAt: time.Now(), IndexedAt: time.Now(), } @@ -208,33 +219,12 @@ func (s *AnnotationService) UpdateAnnotation(w http.ResponseWriter, r *http.Requ } rkey := parts[2] - var selector interface{} = nil - if annotation.SelectorJSON != nil && *annotation.SelectorJSON != "" { - json.Unmarshal([]byte(*annotation.SelectorJSON), &selector) - } - tagsJSON := "" if len(req.Tags) > 0 { tagsBytes, _ := json.Marshal(req.Tags) tagsJSON = string(tagsBytes) } - record := map[string]interface{}{ - "$type": xrpc.CollectionAnnotation, - "text": req.Text, - "url": annotation.TargetSource, - "createdAt": annotation.CreatedAt.Format(time.RFC3339), - } - if selector != nil { - record["selector"] = selector - } - if len(req.Tags) > 0 { - record["tags"] = req.Tags - } - if annotation.TargetTitle != nil { - record["title"] = *annotation.TargetTitle - } - if annotation.BodyValue != nil { previousContent := *annotation.BodyValue s.db.SaveEditHistory(uri, "annotation", previousContent, annotation.CID) @@ -242,6 +232,23 @@ func (s *AnnotationService) UpdateAnnotation(w http.ResponseWriter, r *http.Requ var result *xrpc.PutRecordOutput err = s.refresher.ExecuteWithAutoRefresh(r, session, func(client *xrpc.Client, did string) error { + existing, getErr := client.GetRecord(r.Context(), did, xrpc.CollectionAnnotation, rkey) + if getErr != nil { + return fmt.Errorf("failed to fetch existing record: %w", getErr) + } + + var record map[string]interface{} + if err := json.Unmarshal(existing.Value, &record); err != nil { + return fmt.Errorf("failed to parse existing record: %w", err) + } + + record["text"] = req.Text + if req.Tags != nil { + record["tags"] = req.Tags + } else { + delete(record, "tags") + } + var updateErr error result, updateErr = client.PutRecord(r.Context(), did, xrpc.CollectionAnnotation, rkey, record) if updateErr != nil { diff --git a/backend/internal/api/collections.go b/backend/internal/api/collections.go index 3c24333..72b51a3 100644 --- a/backend/internal/api/collections.go +++ b/backend/internal/api/collections.go @@ -278,6 +278,12 @@ func (s *CollectionService) GetCollectionItems(w http.ResponseWriter, r *http.Re enrichedItems := make([]EnrichedCollectionItem, 0, len(items)) + session, err := s.refresher.GetSessionWithAutoRefresh(r) + viewerDID := "" + if err == nil { + viewerDID = session.DID + } + for _, item := range items { enriched := EnrichedCollectionItem{ URI: item.URI, @@ -290,7 +296,7 @@ func (s *CollectionService) GetCollectionItems(w http.ResponseWriter, r *http.Re if strings.Contains(item.AnnotationURI, "at.margin.annotation") { enriched.Type = "annotation" if a, err := s.db.GetAnnotationByURI(item.AnnotationURI); err == nil { - hydrated, _ := hydrateAnnotations([]db.Annotation{*a}) + hydrated, _ := hydrateAnnotations(s.db, []db.Annotation{*a}, viewerDID) if len(hydrated) > 0 { enriched.Annotation = &hydrated[0] } @@ -298,7 +304,7 @@ func (s *CollectionService) GetCollectionItems(w http.ResponseWriter, r *http.Re } else if strings.Contains(item.AnnotationURI, "at.margin.highlight") { enriched.Type = "highlight" if h, err := s.db.GetHighlightByURI(item.AnnotationURI); err == nil { - hydrated, _ := hydrateHighlights([]db.Highlight{*h}) + hydrated, _ := hydrateHighlights(s.db, []db.Highlight{*h}, viewerDID) if len(hydrated) > 0 { enriched.Highlight = &hydrated[0] } @@ -306,7 +312,7 @@ func (s *CollectionService) GetCollectionItems(w http.ResponseWriter, r *http.Re } else if strings.Contains(item.AnnotationURI, "at.margin.bookmark") { enriched.Type = "bookmark" if b, err := s.db.GetBookmarkByURI(item.AnnotationURI); err == nil { - hydrated, _ := hydrateBookmarks([]db.Bookmark{*b}) + hydrated, _ := hydrateBookmarks(s.db, []db.Bookmark{*b}, viewerDID) if len(hydrated) > 0 { enriched.Bookmark = &hydrated[0] } diff --git a/backend/internal/api/handler.go b/backend/internal/api/handler.go index 8cbad30..e4332b1 100644 --- a/backend/internal/api/handler.go +++ b/backend/internal/api/handler.go @@ -102,7 +102,7 @@ func (h *Handler) GetAnnotations(w http.ResponseWriter, r *http.Request) { return } - enriched, _ := hydrateAnnotations(annotations) + enriched, _ := hydrateAnnotations(h.db, annotations, h.getViewerDID(r)) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ @@ -136,6 +136,11 @@ func (h *Handler) GetFeed(w http.ResponseWriter, r *http.Request) { bookmarks, _ = h.db.GetBookmarksByTag(tag, limit, 0) collectionItems = []db.CollectionItem{} } + } else if creator != "" { + annotations, _ = h.db.GetAnnotationsByAuthor(creator, limit, 0) + highlights, _ = h.db.GetHighlightsByAuthor(creator, limit, 0) + bookmarks, _ = h.db.GetBookmarksByAuthor(creator, limit, 0) + collectionItems = []db.CollectionItem{} } else { annotations, _ = h.db.GetRecentAnnotations(limit, 0) highlights, _ = h.db.GetRecentHighlights(limit, 0) @@ -146,11 +151,12 @@ func (h *Handler) GetFeed(w http.ResponseWriter, r *http.Request) { } } - authAnnos, _ := hydrateAnnotations(annotations) - authHighs, _ := hydrateHighlights(highlights) - authBooks, _ := hydrateBookmarks(bookmarks) + viewerDID := h.getViewerDID(r) + authAnnos, _ := hydrateAnnotations(h.db, annotations, viewerDID) + authHighs, _ := hydrateHighlights(h.db, highlights, viewerDID) + authBooks, _ := hydrateBookmarks(h.db, bookmarks, viewerDID) - authCollectionItems, _ := hydrateCollectionItems(h.db, collectionItems) + authCollectionItems, _ := hydrateCollectionItems(h.db, collectionItems, viewerDID) var feed []interface{} for _, a := range authAnnos { @@ -222,14 +228,14 @@ func (h *Handler) GetAnnotation(w http.ResponseWriter, r *http.Request) { } if annotation, err := h.db.GetAnnotationByURI(uri); err == nil { - if enriched, _ := hydrateAnnotations([]db.Annotation{*annotation}); len(enriched) > 0 { + if enriched, _ := hydrateAnnotations(h.db, []db.Annotation{*annotation}, h.getViewerDID(r)); len(enriched) > 0 { serveResponse(enriched[0], "http://www.w3.org/ns/anno.jsonld") return } } if highlight, err := h.db.GetHighlightByURI(uri); err == nil { - if enriched, _ := hydrateHighlights([]db.Highlight{*highlight}); len(enriched) > 0 { + if enriched, _ := hydrateHighlights(h.db, []db.Highlight{*highlight}, h.getViewerDID(r)); len(enriched) > 0 { serveResponse(enriched[0], "http://www.w3.org/ns/anno.jsonld") return } @@ -238,7 +244,7 @@ func (h *Handler) GetAnnotation(w http.ResponseWriter, r *http.Request) { if strings.Contains(uri, "at.margin.annotation") { highlightURI := strings.Replace(uri, "at.margin.annotation", "at.margin.highlight", 1) if highlight, err := h.db.GetHighlightByURI(highlightURI); err == nil { - if enriched, _ := hydrateHighlights([]db.Highlight{*highlight}); len(enriched) > 0 { + if enriched, _ := hydrateHighlights(h.db, []db.Highlight{*highlight}, h.getViewerDID(r)); len(enriched) > 0 { serveResponse(enriched[0], "http://www.w3.org/ns/anno.jsonld") return } @@ -246,7 +252,7 @@ func (h *Handler) GetAnnotation(w http.ResponseWriter, r *http.Request) { } if bookmark, err := h.db.GetBookmarkByURI(uri); err == nil { - if enriched, _ := hydrateBookmarks([]db.Bookmark{*bookmark}); len(enriched) > 0 { + if enriched, _ := hydrateBookmarks(h.db, []db.Bookmark{*bookmark}, h.getViewerDID(r)); len(enriched) > 0 { serveResponse(enriched[0], "http://www.w3.org/ns/anno.jsonld") return } @@ -255,7 +261,7 @@ func (h *Handler) GetAnnotation(w http.ResponseWriter, r *http.Request) { if strings.Contains(uri, "at.margin.annotation") { bookmarkURI := strings.Replace(uri, "at.margin.annotation", "at.margin.bookmark", 1) if bookmark, err := h.db.GetBookmarkByURI(bookmarkURI); err == nil { - if enriched, _ := hydrateBookmarks([]db.Bookmark{*bookmark}); len(enriched) > 0 { + if enriched, _ := hydrateBookmarks(h.db, []db.Bookmark{*bookmark}, h.getViewerDID(r)); len(enriched) > 0 { serveResponse(enriched[0], "http://www.w3.org/ns/anno.jsonld") return } @@ -284,8 +290,8 @@ func (h *Handler) GetByTarget(w http.ResponseWriter, r *http.Request) { annotations, _ := h.db.GetAnnotationsByTargetHash(urlHash, limit, offset) highlights, _ := h.db.GetHighlightsByTargetHash(urlHash, limit, offset) - enrichedAnnotations, _ := hydrateAnnotations(annotations) - enrichedHighlights, _ := hydrateHighlights(highlights) + enrichedAnnotations, _ := hydrateAnnotations(h.db, annotations, h.getViewerDID(r)) + enrichedHighlights, _ := hydrateHighlights(h.db, highlights, h.getViewerDID(r)) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ @@ -319,7 +325,7 @@ func (h *Handler) GetHighlights(w http.ResponseWriter, r *http.Request) { return } - enriched, _ := hydrateHighlights(highlights) + enriched, _ := hydrateHighlights(h.db, highlights, h.getViewerDID(r)) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ @@ -346,7 +352,7 @@ func (h *Handler) GetBookmarks(w http.ResponseWriter, r *http.Request) { return } - enriched, _ := hydrateBookmarks(bookmarks) + enriched, _ := hydrateBookmarks(h.db, bookmarks, h.getViewerDID(r)) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ @@ -371,7 +377,7 @@ func (h *Handler) GetUserAnnotations(w http.ResponseWriter, r *http.Request) { return } - enriched, _ := hydrateAnnotations(annotations) + enriched, _ := hydrateAnnotations(h.db, annotations, h.getViewerDID(r)) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ @@ -397,7 +403,7 @@ func (h *Handler) GetUserHighlights(w http.ResponseWriter, r *http.Request) { return } - enriched, _ := hydrateHighlights(highlights) + enriched, _ := hydrateHighlights(h.db, highlights, h.getViewerDID(r)) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ @@ -423,7 +429,7 @@ func (h *Handler) GetUserBookmarks(w http.ResponseWriter, r *http.Request) { return } - enriched, _ := hydrateBookmarks(bookmarks) + enriched, _ := hydrateBookmarks(h.db, bookmarks, h.getViewerDID(r)) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ @@ -622,3 +628,14 @@ func (h *Handler) MarkNotificationsRead(w http.ResponseWriter, r *http.Request) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{"status": "ok"}) } +func (h *Handler) getViewerDID(r *http.Request) string { + cookie, err := r.Cookie("margin_session") + if err != nil { + return "" + } + did, _, _, _, _, err := h.db.GetSession(cookie.Value) + if err != nil { + return "" + } + return did +} diff --git a/backend/internal/api/hydration.go b/backend/internal/api/hydration.go index f348692..b750b7f 100644 --- a/backend/internal/api/hydration.go +++ b/backend/internal/api/hydration.go @@ -50,40 +50,49 @@ type APIGenerator struct { } type APIAnnotation struct { - ID string `json:"id"` - CID string `json:"cid"` - Type string `json:"type"` - Motivation string `json:"motivation,omitempty"` - Author Author `json:"creator"` - Body *APIBody `json:"body,omitempty"` - Target APITarget `json:"target"` - Tags []string `json:"tags,omitempty"` - Generator *APIGenerator `json:"generator,omitempty"` - CreatedAt time.Time `json:"created"` - IndexedAt time.Time `json:"indexed"` + ID string `json:"id"` + CID string `json:"cid"` + Type string `json:"type"` + Motivation string `json:"motivation,omitempty"` + Author Author `json:"creator"` + Body *APIBody `json:"body,omitempty"` + Target APITarget `json:"target"` + Tags []string `json:"tags,omitempty"` + Generator *APIGenerator `json:"generator,omitempty"` + CreatedAt time.Time `json:"created"` + IndexedAt time.Time `json:"indexed"` + LikeCount int `json:"likeCount"` + ReplyCount int `json:"replyCount"` + ViewerHasLiked bool `json:"viewerHasLiked"` } type APIHighlight struct { - ID string `json:"id"` - Type string `json:"type"` - Author Author `json:"creator"` - Target APITarget `json:"target"` - Color string `json:"color,omitempty"` - Tags []string `json:"tags,omitempty"` - CreatedAt time.Time `json:"created"` - CID string `json:"cid,omitempty"` + ID string `json:"id"` + Type string `json:"type"` + Author Author `json:"creator"` + Target APITarget `json:"target"` + Color string `json:"color,omitempty"` + Tags []string `json:"tags,omitempty"` + CreatedAt time.Time `json:"created"` + CID string `json:"cid,omitempty"` + LikeCount int `json:"likeCount"` + ReplyCount int `json:"replyCount"` + ViewerHasLiked bool `json:"viewerHasLiked"` } type APIBookmark struct { - ID string `json:"id"` - Type string `json:"type"` - Author Author `json:"creator"` - Source string `json:"source"` - Title string `json:"title,omitempty"` - Description string `json:"description,omitempty"` - Tags []string `json:"tags,omitempty"` - CreatedAt time.Time `json:"created"` - CID string `json:"cid,omitempty"` + ID string `json:"id"` + Type string `json:"type"` + Author Author `json:"creator"` + Source string `json:"source"` + Title string `json:"title,omitempty"` + Description string `json:"description,omitempty"` + Tags []string `json:"tags,omitempty"` + CreatedAt time.Time `json:"created"` + CID string `json:"cid,omitempty"` + LikeCount int `json:"likeCount"` + ReplyCount int `json:"replyCount"` + ViewerHasLiked bool `json:"viewerHasLiked"` } type APIReply struct { @@ -132,7 +141,7 @@ type APINotification struct { ReadAt *time.Time `json:"readAt,omitempty"` } -func hydrateAnnotations(annotations []db.Annotation) ([]APIAnnotation, error) { +func hydrateAnnotations(database *db.DB, annotations []db.Annotation, viewerDID string) ([]APIAnnotation, error) { if len(annotations) == 0 { return []APIAnnotation{}, nil } @@ -197,12 +206,22 @@ func hydrateAnnotations(annotations []db.Annotation) ([]APIAnnotation, error) { CreatedAt: a.CreatedAt, IndexedAt: a.IndexedAt, } + + if database != nil { + result[i].LikeCount, _ = database.GetLikeCount(a.URI) + result[i].ReplyCount, _ = database.GetReplyCount(a.URI) + if viewerDID != "" { + if _, err := database.GetLikeByUserAndSubject(viewerDID, a.URI); err == nil { + result[i].ViewerHasLiked = true + } + } + } } return result, nil } -func hydrateHighlights(highlights []db.Highlight) ([]APIHighlight, error) { +func hydrateHighlights(database *db.DB, highlights []db.Highlight, viewerDID string) ([]APIHighlight, error) { if len(highlights) == 0 { return []APIHighlight{}, nil } @@ -251,12 +270,22 @@ func hydrateHighlights(highlights []db.Highlight) ([]APIHighlight, error) { CreatedAt: h.CreatedAt, CID: cid, } + + if database != nil { + result[i].LikeCount, _ = database.GetLikeCount(h.URI) + result[i].ReplyCount, _ = database.GetReplyCount(h.URI) + if viewerDID != "" { + if _, err := database.GetLikeByUserAndSubject(viewerDID, h.URI); err == nil { + result[i].ViewerHasLiked = true + } + } + } } return result, nil } -func hydrateBookmarks(bookmarks []db.Bookmark) ([]APIBookmark, error) { +func hydrateBookmarks(database *db.DB, bookmarks []db.Bookmark, viewerDID string) ([]APIBookmark, error) { if len(bookmarks) == 0 { return []APIBookmark{}, nil } @@ -296,6 +325,15 @@ func hydrateBookmarks(bookmarks []db.Bookmark) ([]APIBookmark, error) { CreatedAt: b.CreatedAt, CID: cid, } + if database != nil { + result[i].LikeCount, _ = database.GetLikeCount(b.URI) + result[i].ReplyCount, _ = database.GetReplyCount(b.URI) + if viewerDID != "" { + if _, err := database.GetLikeByUserAndSubject(viewerDID, b.URI); err == nil { + result[i].ViewerHasLiked = true + } + } + } } return result, nil @@ -439,7 +477,7 @@ func fetchProfiles(dids []string) (map[string]Author, error) { return result, nil } -func hydrateCollectionItems(database *db.DB, items []db.CollectionItem) ([]APICollectionItem, error) { +func hydrateCollectionItems(database *db.DB, items []db.CollectionItem, viewerDID string) ([]APICollectionItem, error) { if len(items) == 0 { return []APICollectionItem{}, nil } @@ -479,28 +517,27 @@ func hydrateCollectionItems(database *db.DB, items []db.CollectionItem) ([]APICo if strings.Contains(item.AnnotationURI, "at.margin.annotation") { if a, err := database.GetAnnotationByURI(item.AnnotationURI); err == nil { - hydrated, _ := hydrateAnnotations([]db.Annotation{*a}) + hydrated, _ := hydrateAnnotations(database, []db.Annotation{*a}, viewerDID) if len(hydrated) > 0 { apiItem.Annotation = &hydrated[0] } } } else if strings.Contains(item.AnnotationURI, "at.margin.highlight") { if h, err := database.GetHighlightByURI(item.AnnotationURI); err == nil { - hydrated, _ := hydrateHighlights([]db.Highlight{*h}) + hydrated, _ := hydrateHighlights(database, []db.Highlight{*h}, viewerDID) if len(hydrated) > 0 { apiItem.Highlight = &hydrated[0] } } } else if strings.Contains(item.AnnotationURI, "at.margin.bookmark") { if b, err := database.GetBookmarkByURI(item.AnnotationURI); err == nil { - hydrated, _ := hydrateBookmarks([]db.Bookmark{*b}) + hydrated, _ := hydrateBookmarks(database, []db.Bookmark{*b}, viewerDID) if len(hydrated) > 0 { apiItem.Bookmark = &hydrated[0] } else { log.Printf("Failed to hydrate bookmark %s: empty hydration result\n", item.AnnotationURI) } } else { - log.Printf("GetBookmarkByURI failed for %s: %v\n", item.AnnotationURI, err) } } else { log.Printf("Unknown item type for URI: %s\n", item.AnnotationURI) diff --git a/backend/internal/db/queries.go b/backend/internal/db/queries.go index 8591be6..96d7810 100644 --- a/backend/internal/db/queries.go +++ b/backend/internal/db/queries.go @@ -634,6 +634,12 @@ func (db *DB) GetLikeCount(subjectURI string) (int, error) { return count, err } +func (db *DB) GetReplyCount(rootURI string) (int, error) { + var count int + err := db.QueryRow(db.Rebind(`SELECT COUNT(*) FROM replies WHERE root_uri = ?`), rootURI).Scan(&count) + return count, err +} + func (db *DB) GetLikeByUserAndSubject(userDID, subjectURI string) (*Like, error) { var like Like err := db.QueryRow(db.Rebind(` diff --git a/web/src/api/client.js b/web/src/api/client.js index 7cd9e3b..45a28af 100644 --- a/web/src/api/client.js +++ b/web/src/api/client.js @@ -314,6 +314,9 @@ export function normalizeAnnotation(item) { tags: item.tags || [], createdAt: item.createdAt || item.created, cid: item.cid || item.CID, + likeCount: item.likeCount || 0, + replyCount: item.replyCount || 0, + viewerHasLiked: item.viewerHasLiked || false, }; } @@ -328,6 +331,9 @@ export function normalizeAnnotation(item) { tags: item.tags || [], createdAt: item.createdAt || item.created, cid: item.cid || item.CID, + likeCount: item.likeCount || 0, + replyCount: item.replyCount || 0, + viewerHasLiked: item.viewerHasLiked || false, }; } @@ -343,6 +349,9 @@ export function normalizeAnnotation(item) { tags: item.tags || [], createdAt: item.createdAt || item.created, cid: item.cid || item.CID, + likeCount: item.likeCount || 0, + replyCount: item.replyCount || 0, + viewerHasLiked: item.viewerHasLiked || false, }; } @@ -358,6 +367,9 @@ export function normalizeAnnotation(item) { tags: item.tags || [], createdAt: item.createdAt || item.created, cid: item.cid || item.CID, + likeCount: item.likeCount || 0, + replyCount: item.replyCount || 0, + viewerHasLiked: item.viewerHasLiked || false, }; } @@ -371,6 +383,9 @@ export function normalizeHighlight(highlight) { color: highlight.color, tags: highlight.tags || [], createdAt: highlight.createdAt || highlight.created, + likeCount: highlight.likeCount || 0, + replyCount: highlight.replyCount || 0, + viewerHasLiked: highlight.viewerHasLiked || false, }; } @@ -383,6 +398,9 @@ export function normalizeBookmark(bookmark) { description: bookmark.description, tags: bookmark.tags || [], createdAt: bookmark.createdAt || bookmark.created, + likeCount: bookmark.likeCount || 0, + replyCount: bookmark.replyCount || 0, + viewerHasLiked: bookmark.viewerHasLiked || false, }; } diff --git a/web/src/components/AnnotationCard.jsx b/web/src/components/AnnotationCard.jsx index 36a1616..05f0875 100644 --- a/web/src/components/AnnotationCard.jsx +++ b/web/src/components/AnnotationCard.jsx @@ -67,8 +67,8 @@ export default function AnnotationCard({ const { user, login } = useAuth(); const data = normalizeAnnotation(annotation); - const [likeCount, setLikeCount] = useState(0); - const [isLiked, setIsLiked] = useState(false); + const [likeCount, setLikeCount] = useState(data.likeCount || 0); + const [isLiked, setIsLiked] = useState(data.viewerHasLiked || false); const [deleting, setDeleting] = useState(false); const [isEditing, setIsEditing] = useState(false); const [editText, setEditText] = useState(data.text || ""); @@ -80,7 +80,7 @@ export default function AnnotationCard({ const [loadingHistory, setLoadingHistory] = useState(false); const [replies, setReplies] = useState([]); - const [replyCount, setReplyCount] = useState(0); + const [replyCount, setReplyCount] = useState(data.replyCount || 0); const [showReplies, setShowReplies] = useState(false); const [replyingTo, setReplyingTo] = useState(null); const [replyText, setReplyText] = useState(""); @@ -90,45 +90,7 @@ export default function AnnotationCard({ const [hasEditHistory, setHasEditHistory] = useState(false); - useEffect(() => { - let mounted = true; - async function fetchData() { - try { - const repliesRes = await getReplies(data.uri); - if (mounted && repliesRes.items) { - setReplies(repliesRes.items); - setReplyCount(repliesRes.items.length); - } - - const likeRes = await getLikeCount(data.uri); - if (mounted) { - if (likeRes.count !== undefined) { - setLikeCount(likeRes.count); - } - if (likeRes.liked !== undefined) { - setIsLiked(likeRes.liked); - } - } - - if (!data.color && !data.description) { - try { - const history = await getEditHistory(data.uri); - if (mounted && history && history.length > 0) { - setHasEditHistory(true); - } - } catch {} - } - } catch (err) { - console.error("Failed to fetch data:", err); - } - } - if (data.uri) { - fetchData(); - } - return () => { - mounted = false; - }; - }, [data.uri]); + useEffect(() => {}, []); const fetchHistory = async () => { if (showHistory) { @@ -421,7 +383,7 @@ export default function AnnotationCard({ rel="noopener noreferrer" className="annotation-highlight" style={{ - borderLeftColor: isEditing ? editColor : data.color || "#f59e0b", + borderLeftColor: data.color || "#f59e0b", }} > "{highlightedText}" @@ -497,7 +459,17 @@ export default function AnnotationCard({ {user && ( )} diff --git a/web/src/pages/New.jsx b/web/src/pages/New.jsx index b069ac9..9e55a62 100644 --- a/web/src/pages/New.jsx +++ b/web/src/pages/New.jsx @@ -84,7 +84,11 @@ export default function New() {
navigate(-1)} diff --git a/web/src/pages/Profile.jsx b/web/src/pages/Profile.jsx index 61dc76e..9f4214e 100644 --- a/web/src/pages/Profile.jsx +++ b/web/src/pages/Profile.jsx @@ -130,23 +130,7 @@ export default function Profile() {
); } - return bookmarks.map((b) => ); - } - if (activeTab === "bookmarks") { - if (bookmarks.length === 0) { - return ( -
-
- -
-

No bookmarks

-

- This user hasn't bookmarked any pages. -

-
- ); - } - return bookmarks.map((b) => ); + return bookmarks.map((b) => ); } if (activeTab === "collections") {