Finished :)

This commit is contained in:
2026-04-27 16:55:02 -06:00
parent cb4a210567
commit 85a2a3116b
16 changed files with 1345 additions and 1265 deletions
+172
View File
@@ -0,0 +1,172 @@
package api
import (
"fmt"
"net/http"
"sync"
"sprintpadawan/lib"
)
type contextKey string
const userKey contextKey = "user"
type sseClient struct {
ch chan string
userID int
}
type MemberView struct {
Username string
HasVoted bool
ID int
}
type RoomData struct {
*lib.Room
User *lib.User
Members []MemberView
Stories []lib.Story
IsOwner bool
UserVotes map[int]string
StoryVotes map[int][]lib.VoteView
}
var (
roomClients = make(map[int][]*sseClient)
clientsMu sync.Mutex
)
func addSSEClient(roomID, userID int, ch chan string) {
clientsMu.Lock()
defer clientsMu.Unlock()
roomClients[roomID] = append(roomClients[roomID], &sseClient{ch: ch, userID: userID})
}
func removeSSEClient(roomID int, ch chan string) {
clientsMu.Lock()
defer clientsMu.Unlock()
clients := roomClients[roomID]
for i, c := range clients {
if c.ch == ch {
roomClients[roomID] = append(clients[:i], clients[i+1:]...)
break
}
}
if len(roomClients[roomID]) == 0 {
delete(roomClients, roomID)
}
}
func broadcast(roomID int, event string) {
clientsMu.Lock()
defer clientsMu.Unlock()
alive := roomClients[roomID][:0]
for _, c := range roomClients[roomID] {
select {
case c.ch <- event:
alive = append(alive, c)
default:
// drop dead client
}
}
roomClients[roomID] = alive
}
func GetConnectedUserIDs(roomID int) []int {
clientsMu.Lock()
defer clientsMu.Unlock()
seen := make(map[int]bool)
var ids []int
for _, c := range roomClients[roomID] {
if !seen[c.userID] {
seen[c.userID] = true
ids = append(ids, c.userID)
}
}
return ids
}
func scaleToOptions(scale string) []string {
switch scale {
case "fibonacci":
return []string{"1", "2", "3", "5", "8", "13", "21", "?"}
case "tshirt":
return []string{"XS", "S", "M", "L", "XL", "XXL", "?"}
case "linear":
return []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "?"}
default:
return []string{"1", "2", "3", "5", "8", "13", "21", "?"}
}
}
func getRoomID(r *http.Request) int {
return getPathInt(r, "id")
}
func getPathInt(r *http.Request, key string) int {
var id int
fmt.Sscanf(r.PathValue(key), "%d", &id)
return id
}
func buildRoomData(room *lib.Room, user *lib.User) RoomData {
members, _ := lib.GetRoomMembers(room.ID)
stories, _ := lib.GetStoriesForRoom(room.ID)
connectedIDs := GetConnectedUserIDs(room.ID)
connectedMap := make(map[int]bool)
for _, cid := range connectedIDs {
connectedMap[cid] = true
}
connectedMap[user.ID] = true
var activeVotes []lib.Vote
if room.ActiveStoryID != nil {
activeVotes, _ = lib.GetVotesForStory(*room.ActiveStoryID)
}
var memberViews []MemberView
for _, m := range members {
if !connectedMap[m.ID] {
continue
}
hasVoted := false
for _, v := range activeVotes {
if v.UserID == m.ID {
hasVoted = true
break
}
}
memberViews = append(memberViews, MemberView{
Username: m.Username,
HasVoted: hasVoted,
ID: m.ID,
})
}
userVotes := make(map[int]string)
storyVotes := make(map[int][]lib.VoteView)
for _, s := range stories {
votes, _ := lib.GetVotesForStory(s.ID)
for _, v := range votes {
if v.UserID == user.ID {
userVotes[s.ID] = v.Value
}
}
if s.Voted {
vv, _ := lib.GetVotesWithUsernames(s.ID)
storyVotes[s.ID] = vv
}
}
return RoomData{
Room: room,
User: user,
Members: memberViews,
Stories: stories,
IsOwner: room.OwnerID == user.ID,
UserVotes: userVotes,
StoryVotes: storyVotes,
}
}