Files
logseq/docs/agent-guide/002-reactions.md
2026-02-01 21:45:31 +08:00

3.2 KiB
Raw Blame History

ADR: Reactions via Properties

Status

  • Proposed

Context

  • Users want lightweight reactions (e.g., 👍 ❤️) on blocks and pages.
  • Reactions must be stored in the graph so they sync, can be queried, and work across devices.
  • The system already uses properties to attach structured metadata to blocks/pages.
  • We need to show who reacted and which emoji they used.

Decision

  • Store reactions as separate entities linked to the reacted block/page.
  • Each reaction entity records:
    • Emoji id (from Logseqs supported emojis-data set).
    • Optional :logseq.property/created-by-ref pointing to the reacting user (absent for anonymous graphs).
    • Reacted block/page reference.
    • :block/created-at timestamp for the reaction entity.
  • No :logseq.property/reactions collection property is required; use the reverse ref (:logseq.property.reaction/_target node-entity) to fetch reactions for a node.
  • Keep the property name namespaced in logseq.db.frontend.property/built-in-properties.

Proposed entity shape

{:db/id                         ...
 :logseq.property.reaction/emoji-id             "smile"
 :logseq.property/created-by-ref                <user-db-id>   ;; omitted for anonymous graphs
 :logseq.property.reaction/target               <target-db-id> ;; block/page db id
 :block/created-at              1710000000000}

Read/write rules

  • Toggling a reaction adds/removes a reaction entity for the current emoji/user.
  • If anonymous, only one reaction per emoji per block/page (no user id).
  • Reactions are derived via reverse reference lookup; no dedicated collection property is stored on the node.

Example queries

;; Given a block/page entity `node-entity`, fetch all reactions.
(:logseq.property.reaction/_target node-entity)

;; Filter reactions by emoji id.
(filter #(= "smile" (:logseq.property.reaction/emoji-id %))
        (:logseq.property.reaction/_target node-entity))

;; Count reactions per emoji id.
(->> (:logseq.property.reaction/_target node-entity)
     (map :logseq.property.reaction/emoji-id)
     (frequencies))

;; Filter reactions by user id (when present).
(filter #(= user-db-id (:logseq.property/created-by-ref %))
        (:logseq.property.reaction/_target node-entity))

Consequences

  • Reactions sync naturally as part of DB transactions and are queryable.
  • Data model supports “who reacted” and multiple users per emoji without map merging.
  • Adds more entities; need efficient queries and indexes.

Alternatives Considered

  • Dedicated table/attribute per emoji: complicates schema, increases complexity.
  • Property map (emoji -> users): smaller but harder to resolve conflicts and query per user.
  • Inline text markers: not structured, hard to query and sync.

Open Questions

  • Which user identifier should be stored as :logseq.property/created-by-ref? Each user has a page in the graph
  • How to handle anonymous/local graphs (no user identity)? Record reactions, for anonymous graphs, don't store :logseq.property/created-by-ref

Notes for Implementation

  • Add emoji entity schema to DB validation.
  • UI should show a summary (emoji + count) and a hover/popover with user list.
  • User can toggle reaction.