tinyrelayfile
tinyrelay/tinyclient/read_contracts.go/

tinyclient/read_contracts.go raw
1package tinyclient
2
3import (
4 "context"
5 "encoding/json"
6 "net/url"
7
8 "github.com/FelineStateMachine/tinyrelay/protocol/nostr"
9)
10
11// RoomsReader supplies the typed reads used by room pages. actor is the
12// authenticated public key, or an empty string for an anonymous request.
13// cursor is empty for the newest page and otherwise is the value returned in
14// NextCursor. limit requests at most that many rows. The daemon adapter uses
15// 100 when the requested limit falls outside 1 through 100.
16//
17// ListRooms returns visible room summaries ordered by room ID and a cursor for
18// the next page. ReadRoom returns the room summary, visible members, newest
19// room messages, message edits and a cursor for older messages. ReadThread
20// returns the room summary, one root event, its replies, edits and a cursor for
21// older replies. Reads enforce tenant and room access and return errors for
22// missing rooms, invalid cursors or an inaccessible room. ReadThread also
23// returns an error when rootID is not a lower-case hexadecimal event ID or does
24// not identify a message in roomID.
25type RoomsReader interface {
26 // ListRooms returns up to limit visible room summaries after cursor.
27 ListRooms(ctx context.Context, actor, cursor string, limit int) (RoomList, error)
28 // ReadRoom returns one visible room, its members, messages and edits after cursor.
29 ReadRoom(ctx context.Context, actor, roomID, cursor string, limit int) (RoomPage, error)
30 // ReadThread returns one room thread root, replies and edits after cursor.
31 ReadThread(ctx context.Context, actor, roomID, rootID, cursor string, limit int) (RoomPage, error)
32}
33
34type RoomList struct {
35 // Rooms contains visible room summaries in ascending ID order.
36 Rooms []RoomSummary `json:"rooms"`
37 // NextCursor is the last room ID when another page is available.
38 NextCursor string `json:"next_cursor,omitempty"`
39}
40
41// RoomSummary is the room metadata shown in the rail and room header.
42type RoomSummary struct {
43 ID string `json:"id"`
44 Name string `json:"name,omitempty"`
45 Access string `json:"access,omitempty"`
46 About string `json:"about,omitempty"`
47 Picture string `json:"picture,omitempty"`
48 CreatedBy string `json:"created_by,omitempty"`
49 CreatedAt int64 `json:"created_at,omitempty"`
50 EventID string `json:"event_id,omitempty"`
51 DeletedAt int64 `json:"deleted_at,omitempty"`
52 Members int `json:"members,omitempty"`
53 LastMessageAt int64 `json:"last_message_at,omitempty"`
54 Role string `json:"role,omitempty"`
55}
56
57type RoomMember struct {
58 PubKey string `json:"pubkey"`
59 Role string `json:"role,omitempty"`
60 Agent bool `json:"agent,omitempty"`
61 Operator string `json:"operator,omitempty"`
62 AddedAt int64 `json:"added_at,omitempty"`
63}
64
65type RoomMessage = nostr.Event
66
67// RoomPage is the typed result for a room or thread read. A room read fills
68// Members and Messages; a thread read fills Root and Replies. Edits contains
69// the newest visible edit for the returned targets.
70type RoomPage struct {
71 Room RoomSummary `json:"room"`
72 Members []RoomMember `json:"members,omitempty"`
73 Messages []RoomMessage `json:"messages,omitempty"`
74 // ReplyCounts and ReactionCounts carry folded interaction summaries for
75 // the roots in Messages without expanding every reply into the timeline.
76 ReplyCounts map[string]int `json:"reply_counts,omitempty"`
77 ReactionCounts map[string]map[string]int `json:"reaction_counts,omitempty"`
78 Replies []RoomMessage `json:"replies,omitempty"`
79 Edits []RoomMessage `json:"edits,omitempty"`
80 Root *RoomMessage `json:"root,omitempty"`
81 NextCursor string `json:"next_cursor,omitempty"`
82}
83
84// rowsReader converts method-and-JSON query results into the row slices used
85// by page renderers.
86type rowsReader struct{ backend Backend }
87
88func (r rowsReader) rows(ctx context.Context, method string, params []json.RawMessage, actor string) ([]any, error) {
89 result, err := r.backend.Query(ctx, method, params, actor)
90 if err != nil {
91 return nil, err
92 }
93 return browseRows(result), nil
94}
95
96func (a *App) readRows(ctx context.Context, method string, params []json.RawMessage, actor string) ([]any, error) {
97 return rowsReader{backend: a.backend}.rows(ctx, method, params, actor)
98}
99
100func (a *App) roomsReader() RoomsReader {
101 reader, _ := a.backend.(RoomsReader)
102 return reader
103}
104
105func roomListValue(list RoomList) any {
106 return map[string]any{"items": list.Rooms, "next_cursor": list.NextCursor}
107}
108
109func roomPageValueFromContract(page RoomPage) any {
110 return map[string]any{
111 "room": page.Room,
112 "members": page.Members,
113 "messages": page.Messages,
114 "reply_counts": page.ReplyCounts,
115 "reaction_counts": page.ReactionCounts,
116 "replies": page.Replies,
117 "edits": page.Edits,
118 "root": page.Root,
119 "next_cursor": page.NextCursor,
120 }
121}
122
123func (a *App) typedRoomResult(ctx context.Context, route roomPath, query url.Values, actor string) (any, error, bool) {
124 reader := a.roomsReader()
125 if reader == nil || route.tab == "" {
126 return nil, nil, false
127 }
128 var (
129 value any
130 err error
131 )
132 switch route.tab {
133 case "rooms":
134 list, readErr := reader.ListRooms(ctx, actor, query.Get("cursor"), 100)
135 value, err = roomListValue(list), readErr
136 case "room":
137 page, readErr := reader.ReadRoom(ctx, actor, route.id, query.Get("cursor"), 50)
138 value, err = roomPageValueFromContract(page), readErr
139 case "thread":
140 page, readErr := reader.ReadThread(ctx, actor, route.id, route.event, query.Get("cursor"), 50)
141 value, err = roomPageValueFromContract(page), readErr
142 }
143 return value, err, true
144}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs