Skip to content

Commit

Permalink
Add pinning support
Browse files Browse the repository at this point in the history
  • Loading branch information
taiidani committed Sep 8, 2024
1 parent e9ee1e5 commit 2a271dc
Show file tree
Hide file tree
Showing 14 changed files with 311 additions and 188 deletions.
3 changes: 2 additions & 1 deletion internal/data/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import (
)

type Session struct {
SteamID string `json:"SteamID"`
Pinned []uint64
SteamID string
}

const DefaultSessionExpiration = time.Hour * 24 * 90
Expand Down
2 changes: 1 addition & 1 deletion internal/server/about.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type aboutBag struct {
}

func (s *Server) aboutHandler(resp http.ResponseWriter, req *http.Request) {
data := aboutBag{baseBag: newBag(req, "about")}
data := aboutBag{baseBag: s.newBag(req, "about")}

renderHtml(resp, http.StatusOK, "about.gohtml", data)
}
6 changes: 1 addition & 5 deletions internal/server/game.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type gameBag struct {
}

func (s *Server) gameHandler(resp http.ResponseWriter, req *http.Request) {
bag := gameBag{baseBag: newBag(req, "game")}
bag := gameBag{baseBag: s.newBag(req, "game")}

gameIDString := req.PathValue("id")
gameID, _ := strconv.ParseUint(gameIDString, 10, 64)
Expand All @@ -41,9 +41,5 @@ func (s *Server) gameHandler(resp http.ResponseWriter, req *http.Request) {
}

template := "game.gohtml"
if req.Header.Get("HX-Request") != "" {
template = "game-body.gohtml"
}

renderHtml(resp, http.StatusOK, template, bag)
}
54 changes: 47 additions & 7 deletions internal/server/hx.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package server
import (
"fmt"
"net/http"
"slices"
"strconv"

"github.com/taiidani/achievements/internal/data"
Expand All @@ -14,23 +15,62 @@ type hxGameRowBag struct {
Achievements data.Achievements
}

func (s *Server) hxGameRowHandler(resp http.ResponseWriter, req *http.Request) {
bag := hxGameRowBag{baseBag: newBag(req, "")}
func (s *Server) hxGameRowHandler(w http.ResponseWriter, r *http.Request) {
bag := hxGameRowBag{baseBag: s.newBag(r, "")}

gameIDString := req.URL.Query().Get("game-id")
gameIDString := r.PathValue("id")
bag.GameID, _ = strconv.ParseUint(gameIDString, 10, 64)

if bag.SteamID == "" {
errorResponse(resp, http.StatusBadRequest, fmt.Errorf("user ID is required"))
errorResponse(w, http.StatusBadRequest, fmt.Errorf("user ID is required"))
return
}

achievements, err := s.backend.GetAchievements(req.Context(), bag.SteamID, bag.GameID)
achievements, err := s.backend.GetAchievements(r.Context(), bag.SteamID, bag.GameID)
if err != nil {
errorResponse(resp, http.StatusNotFound, err)
errorResponse(w, http.StatusNotFound, err)
return
}
bag.Achievements = achievements

renderHtml(resp, http.StatusOK, "hx-achievement-progress.gohtml", bag)
renderHtml(w, http.StatusOK, "hx-achievement-progress.gohtml", bag)
}

type hxGamePinBag struct {
baseBag
HasPinned bool
Games []indexBagGame
}

func (s *Server) hxGamePinHandler(w http.ResponseWriter, r *http.Request) {
bag := hxGamePinBag{baseBag: s.newBag(r, "")}
gameIDString := r.PathValue("id")
gameID, _ := strconv.ParseUint(gameIDString, 10, 64)

if gameID == 0 {
errorResponse(w, http.StatusNotFound, fmt.Errorf("game-id required for pinning"))
return
}

switch r.Method {
case http.MethodPost:
if !slices.Contains(bag.Session.Pinned, gameID) {
bag.Session.Pinned = append(bag.Session.Pinned, gameID)
_ = s.backend.SetSession(r.Context(), bag.SessionKey, *bag.Session)
}
case http.MethodDelete:
if ix := slices.Index(bag.Session.Pinned, gameID); ix >= 0 {
bag.Session.Pinned = slices.Delete(bag.Session.Pinned, ix, ix+1)
_ = s.backend.SetSession(r.Context(), bag.SessionKey, *bag.Session)
}
}

var err error
bag.Games, bag.HasPinned, err = s.loadGamesList(r.Context(), bag.SteamID, bag.baseBag)
if err != nil {
errorResponse(w, http.StatusNotFound, err)
return
}

renderHtml(w, http.StatusOK, "games-pinned.gohtml", bag)
}
60 changes: 38 additions & 22 deletions internal/server/index.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
package server

import (
"context"
"fmt"
"net/http"
"slices"
"sort"

"github.com/taiidani/achievements/internal/data"
)

type indexBag struct {
baseBag
User data.User
Games []struct {
data.Game
SteamID string
}
User data.User
HasPinned bool
Games []indexBagGame
}

type indexBagGame struct {
data.Game
Pinned bool
}

func (s *Server) indexHandler(resp http.ResponseWriter, req *http.Request) {
bag := indexBag{baseBag: newBag(req, "home")}
bag := indexBag{baseBag: s.newBag(req, "home")}

// If no user has been set, display the welcome page
if bag.SteamID == "" {
Expand All @@ -45,30 +50,41 @@ func (s *Server) indexHandler(resp http.ResponseWriter, req *http.Request) {
}
bag.User = user

games, err := s.backend.GetGames(req.Context(), bag.SteamID)
bag.Games, bag.HasPinned, err = s.loadGamesList(req.Context(), user.SteamID, bag.baseBag)
if err != nil {
errorResponse(resp, http.StatusNotFound, err)
return
}

for _, game := range games {
bag.Games = append(bag.Games, struct {
data.Game
SteamID string
}{
Game: game,
SteamID: bag.SteamID,
})
template := "games.gohtml"
renderHtml(resp, http.StatusOK, template, bag)
}

func (s *Server) loadGamesList(ctx context.Context, steamID string, bag baseBag) ([]indexBagGame, bool, error) {
ret := []indexBagGame{}
retPinned := false

games, err := s.backend.GetGames(ctx, steamID)
if err != nil {
return ret, false, err
}

sort.Slice(bag.Games, func(i, j int) bool {
return bag.Games[i].DisplayName < bag.Games[j].DisplayName
})
for _, game := range games {
bagGame := indexBagGame{
Game: game,
Pinned: bag.Session != nil && slices.Contains(bag.Session.Pinned, game.ID),
}

template := "games.gohtml"
if req.Header.Get("HX-Request") != "" {
template = "games-body.gohtml"
if bagGame.Pinned {
retPinned = true
}

ret = append(ret, bagGame)
}

renderHtml(resp, http.StatusOK, template, bag)
sort.Slice(ret, func(i, j int) bool {
return ret[i].DisplayName < ret[j].DisplayName
})

return ret, retPinned, nil
}
27 changes: 21 additions & 6 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ func (s *Server) addRoutes(mux *http.ServeMux) {
mux.Handle("/game/{id}", s.sessionMiddleware(http.HandlerFunc(s.gameHandler)))
mux.Handle("/about", s.sessionMiddleware(http.HandlerFunc(s.aboutHandler)))
mux.Handle("/assets/*", http.HandlerFunc(s.assetsHandler))
mux.Handle("/hx/game/row", s.sessionMiddleware(http.HandlerFunc(s.hxGameRowHandler)))
mux.Handle("/hx/game/{id}/row", s.sessionMiddleware(http.HandlerFunc(s.hxGameRowHandler)))
mux.Handle("/hx/game/{id}/pin", s.sessionMiddleware(http.HandlerFunc(s.hxGamePinHandler)))
mux.Handle("/user/login", s.sessionMiddleware(http.HandlerFunc(s.userLoginHandler)))
mux.Handle("/user/login/steam", s.sessionMiddleware(http.HandlerFunc(s.userLoginSteamHandler)))
mux.Handle("/user/change", s.sessionMiddleware(http.HandlerFunc(s.userChangeHandler)))
Expand Down Expand Up @@ -88,15 +89,29 @@ func renderHtml(writer http.ResponseWriter, code int, file string, data any) {
}

type baseBag struct {
Page string
LoggedIn bool
SteamID string
SessionKey string
Session *data.Session
Page string
LoggedIn bool
SteamID string
}

func newBag(r *http.Request, pageName string) baseBag {
func (s *Server) newBag(r *http.Request, pageName string) baseBag {
ret := baseBag{}
ret.Page = pageName
ret.LoggedIn = r.Header.Get(steamIDHeaderKey) != ""

// Load the session if it exists
cookie, err := r.Cookie("session")
if err == nil {
ret.SessionKey = cookie.Value
sess, err := s.backend.GetSession(r.Context(), cookie.Value)
if err != nil {
slog.Warn("Unable to retrieve session", "key", cookie.Value, "error", err)
} else {
ret.Session = sess
ret.LoggedIn = true
}
}

// Prioritize the query parameter over the session ID
ret.SteamID = r.FormValue("steam-id")
Expand Down
69 changes: 0 additions & 69 deletions internal/server/templates/game-body.gohtml

This file was deleted.

Loading

0 comments on commit 2a271dc

Please sign in to comment.