100 lines
3.0 KiB
Go
100 lines
3.0 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
)
|
|
|
|
func (h *apiHandler) addReaction(w http.ResponseWriter, r *http.Request) {
|
|
userID, _ := userIDFrom(r.Context())
|
|
convID, msgID, ok := h.parseMessagePath(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req struct {
|
|
Emoji string `json:"emoji"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid json")
|
|
return
|
|
}
|
|
if len(req.Emoji) == 0 || len(req.Emoji) > 32 {
|
|
writeError(w, http.StatusBadRequest, "emoji must be 1-32 chars")
|
|
return
|
|
}
|
|
|
|
if err := h.store.AddReaction(r.Context(), msgID, userID, req.Emoji); err != nil {
|
|
writeError(w, http.StatusInternalServerError, "internal server error")
|
|
return
|
|
}
|
|
h.hub.PublishReactionChanged(convID, msgID, userID, req.Emoji, true)
|
|
h.writeMessageWithReactions(w, r, convID, msgID)
|
|
}
|
|
|
|
func (h *apiHandler) removeReaction(w http.ResponseWriter, r *http.Request) {
|
|
userID, _ := userIDFrom(r.Context())
|
|
convID, msgID, ok := h.parseMessagePath(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
emoji := r.PathValue("emoji")
|
|
if emoji == "" {
|
|
writeError(w, http.StatusBadRequest, "emoji is required")
|
|
return
|
|
}
|
|
|
|
if err := h.store.RemoveReaction(r.Context(), msgID, userID, emoji); err != nil {
|
|
writeError(w, http.StatusInternalServerError, "internal server error")
|
|
return
|
|
}
|
|
h.hub.PublishReactionChanged(convID, msgID, userID, emoji, false)
|
|
h.writeMessageWithReactions(w, r, convID, msgID)
|
|
}
|
|
|
|
func (h *apiHandler) parseMessagePath(w http.ResponseWriter, r *http.Request) (convID, msgID int64, ok bool) {
|
|
userID, _ := userIDFrom(r.Context())
|
|
convID, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
|
|
if err != nil || convID <= 0 {
|
|
writeError(w, http.StatusBadRequest, "invalid conversation id")
|
|
return 0, 0, false
|
|
}
|
|
msgID, err = strconv.ParseInt(r.PathValue("msg_id"), 10, 64)
|
|
if err != nil || msgID <= 0 {
|
|
writeError(w, http.StatusBadRequest, "invalid message id")
|
|
return 0, 0, false
|
|
}
|
|
if member, err := h.store.IsMember(r.Context(), convID, userID); err != nil || !member {
|
|
writeError(w, http.StatusForbidden, "not a member")
|
|
return 0, 0, false
|
|
}
|
|
belongs, err := h.store.MessageBelongsToConversation(r.Context(), convID, msgID)
|
|
if err != nil || !belongs {
|
|
writeError(w, http.StatusNotFound, "message not found")
|
|
return 0, 0, false
|
|
}
|
|
return convID, msgID, true
|
|
}
|
|
|
|
func (h *apiHandler) writeMessageWithReactions(w http.ResponseWriter, r *http.Request, convID, msgID int64) {
|
|
msg, err := h.store.GetMessageByID(r.Context(), msgID)
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, "internal server error")
|
|
return
|
|
}
|
|
msgs := []messageJSON{toMessageJSON(msg)}
|
|
if msg.MediaID != nil {
|
|
if m, err := h.store.GetMediaByID(r.Context(), *msg.MediaID); err == nil {
|
|
md := toMediaJSON(m)
|
|
msgs[0].Media = &md
|
|
}
|
|
}
|
|
reactions, err := h.store.ReactionsForMessages(r.Context(), []int64{msgID})
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, "internal server error")
|
|
return
|
|
}
|
|
attachReactions(msgs, reactions)
|
|
writeJSON(w, http.StatusOK, msgs[0])
|
|
}
|