backend for xcvr appview
1package model
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "net/http"
8 "os"
9 "rvcx/internal/atputils"
10 "rvcx/internal/lex"
11 "rvcx/internal/types"
12 "time"
13
14 "github.com/gorilla/websocket"
15)
16
17type client struct {
18 conn *websocket.Conn
19 bus chan any
20}
21
22func (cm *channelModel) WSHandler(uri string, m *Model) http.HandlerFunc {
23 return func(w http.ResponseWriter, r *http.Request) {
24 upgrader := &websocket.Upgrader{
25 CheckOrigin: func(r *http.Request) bool {
26 return true
27 },
28 }
29 conn, err := upgrader.Upgrade(w, r, nil)
30 if err != nil {
31 return
32 }
33 defer conn.Close()
34
35 bus := make(chan any, 10)
36 client := &client{
37 conn,
38 bus,
39 }
40 cm.clientsmu.Lock()
41 cm.clients[client] = true
42 cm.clientsmu.Unlock()
43
44 client.wsWriter()
45 cm.logger.Deprintln("i am a lex stream wshandler and i am exiting")
46
47 cm.clientsmu.Lock()
48 delete(cm.clients, client)
49 cm.clientsmu.Unlock()
50 }
51}
52
53func (c *client) wsWriter() {
54 ticker := time.NewTicker(15 * time.Second)
55 for {
56 select {
57 case <-ticker.C:
58 err := c.conn.WriteControl(websocket.PingMessage, nil, time.Now().Add(5*time.Second))
59 if err != nil {
60 return
61 }
62 case e, ok := <-c.bus:
63 if !ok {
64 return
65 }
66 c.conn.WriteJSON(e)
67 }
68 }
69}
70
71// func (cm *channelModel) cleanUp() {
72// cm.clientsmu.Lock()
73// defer cm.clientsmu.Unlock()
74// for cli := range cm.clients {
75// close(cli.bus)
76// }
77// }
78
79func (cm *channelModel) broadcast(a any) {
80 cm.clientsmu.Lock()
81 defer cm.clientsmu.Unlock()
82 for cli := range cm.clients {
83 select {
84 case cli.bus <- a:
85 default:
86 delete(cm.clients, cli)
87 close(cli.bus)
88 }
89 }
90}
91
92func (m *Model) BroadcastSignet(uri string, s *types.Signet) error {
93 cm := m.uriMap[uri]
94 if cm == nil {
95 return errors.New("AAAAAAAAAAA")
96 }
97 ihandle, err := m.store.ResolveDid(s.IssuerDID, context.Background())
98 if err != nil {
99 ihandle, err = atputils.TryLookupDid(context.Background(), s.IssuerDID)
100 if err != nil {
101 return errors.New("AAAAAAAAAAAAAAAAAAAAA")
102 }
103 go m.store.StoreDidHandle(s.IssuerDID, ihandle, context.Background())
104 }
105 sv := types.SignetView{
106 URI: s.URI,
107 Issuer: s.IssuerDID,
108 ChannelURI: s.ChannelURI,
109 LrcId: s.MessageID,
110 Author: s.Author,
111 AuthorHandle: s.AuthorHandle,
112 StartedAt: s.StartedAt,
113 }
114 cm.broadcast(sv)
115 return nil
116}
117
118func (m *Model) BroadcastMessage(uri string, msg *types.Message) error {
119 cm := m.uriMap[uri]
120 if cm == nil {
121 return errors.New("failed to map uri to lsm!")
122 }
123 pv, err := m.store.GetProfileView(msg.DID, context.Background())
124 if err != nil {
125 return errors.New("failed to get profile view: " + err.Error())
126 }
127 mv := types.MessageView{
128 URI: msg.URI,
129 Author: *pv,
130 Body: msg.Body,
131 Nick: msg.Nick,
132 Color: msg.Color,
133 SignetURI: msg.SignetURI,
134 PostedAt: msg.PostedAt,
135 }
136 cm.broadcast(mv)
137 return nil
138}
139
140func (m *Model) BroadcastImage(uri string, media *types.Image) error {
141 cm := m.uriMap[uri]
142 if cm == nil {
143 return errors.New("failed to map uri to lsm!")
144 }
145 pv, err := m.store.GetProfileView(media.DID, context.Background())
146 if err != nil {
147 return errors.New("failed to get profile view: " + err.Error())
148 }
149 var ar *lex.AspectRatio
150 if media.Width != nil && media.Height != nil {
151 ar = &lex.AspectRatio{
152 Width: *media.Width,
153 Height: *media.Height,
154 }
155 }
156 src := fmt.Sprintf("https://%s/xrpc/org.xcvr.lrc.getImage?uri=%s", os.Getenv("MY_IDENTITY"), media.URI)
157
158 img := types.ImageView{
159 Alt: media.Alt,
160 Src: &src,
161 AspectRatio: ar,
162 }
163 mv := types.MediaView{
164 URI: media.URI,
165 Author: *pv,
166 Image: &img,
167 Nick: media.Nick,
168 Color: media.Color,
169 SignetURI: media.SignetURI,
170 PostedAt: media.PostedAt,
171 }
172 cm.broadcast(mv)
173 return nil
174}