Skip to content

Commit

Permalink
Initial gallery implementation
Browse files Browse the repository at this point in the history
By appending `/gallery`, you can view the list of all past drawings from
the lobby. The link will work after the lobby dies, if the data is still
in your session storage. We try to avoid sending requests multiple times
by caching.

In order to do this, we've refactored parts of the lobby, moving shared
drawing / color code into draw.js (previously floodfill.js).
  • Loading branch information
Bios-Marcel committed Nov 17, 2024
1 parent 20055f2 commit 6245230
Show file tree
Hide file tree
Showing 14 changed files with 613 additions and 305 deletions.
2 changes: 2 additions & 0 deletions internal/api/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ func (handler *V1Handler) SetupRoutes(rootPath string, router chi.Router) {
router.Get(routePrefix+"/lobby", handler.getLobbies)
router.Post(routePrefix+"/lobby", handler.postLobby)

router.Get(routePrefix+"/lobby/{lobby_id}/gallery", handler.getGallery)

router.Patch(routePrefix+"/lobby/{lobby_id}", handler.patchLobby)
// The websocket is shared between the public API and the official client
router.Get(routePrefix+"/lobby/{lobby_id}/ws", handler.websocketUpgrade)
Expand Down
21 changes: 21 additions & 0 deletions internal/api/v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,27 @@ type LobbyEntry struct {
CustomWords bool `json:"customWords"`
}

//easyjson:json
type Gallery [][]any

func (handler *V1Handler) getGallery(writer http.ResponseWriter, request *http.Request) {
lobby := state.GetLobby(chi.URLParam(request, "lobby_id"))
if lobby == nil {
http.Error(writer, ErrLobbyNotExistent.Error(), http.StatusNotFound)
return
}

// FIXME Synchronise access to lobby.Drawings.
// The drawings should also be available in an unstarted game.

if started, _, err := easyjson.MarshalToHTTPResponseWriter(Gallery(lobby.Drawings), writer); err != nil {
if !started {
http.Error(writer, err.Error(), http.StatusInternalServerError)
}
return
}
}

func (handler *V1Handler) getLobbies(writer http.ResponseWriter, _ *http.Request) {
// REMARK: If paging is ever implemented, we might want to maintain order
// when deleting lobbies from state in the state package.
Expand Down
110 changes: 110 additions & 0 deletions internal/api/v1_easyjson.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion internal/frontend/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,5 +197,5 @@ func (handler *SSRHandler) ssrCreateLobby(writer http.ResponseWriter, request *h
// We only add the lobby if we could do all necessary pre-steps successfully.
state.AddLobby(lobby)

http.Redirect(writer, request, handler.basePageConfig.RootPath+"/ssrEnterLobby/"+lobby.LobbyID, http.StatusFound)
http.Redirect(writer, request, handler.basePageConfig.RootPath+"/lobby/"+lobby.LobbyID, http.StatusFound)
}
42 changes: 42 additions & 0 deletions internal/frontend/gallery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package frontend

import (
"log"
"net/http"
"strings"

"github.com/go-chi/chi/v5"
"github.com/scribble-rs/scribble.rs/internal/translations"
)

type galleryPageData struct {
*BasePageConfig

LobbyID string
Translation translations.Translation
Locale string
}

func (handler *SSRHandler) ssrGallery(writer http.ResponseWriter, request *http.Request) {
userAgent := strings.ToLower(request.UserAgent())
if !isHumanAgent(userAgent) {
// FIXME Handle robots
return
}

lobbyId := chi.URLParam(request, "lobby_id")
translation, locale := determineTranslation(request)
pageData := &galleryPageData{
BasePageConfig: handler.basePageConfig,
LobbyID: lobbyId,
Translation: translation,
Locale: locale,
}

// If the pagedata isn't initialized, it means the synchronized block has exited.
// In this case we don't want to template the lobby, since an error has occurred
// and probably already has been handled.
if err := pageTemplates.ExecuteTemplate(writer, "gallery-page", pageData); err != nil {
log.Printf("Error templating lobby: %s\n", err)
}
}
37 changes: 35 additions & 2 deletions internal/frontend/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import (
"html/template"
"net/http"
"path"
"strings"

"github.com/go-chi/chi/v5"
"github.com/scribble-rs/scribble.rs/internal/translations"
"golang.org/x/text/language"
)

var (
Expand Down Expand Up @@ -61,8 +63,9 @@ func (handler *SSRHandler) SetupRoutes(router chi.Router) {
}),
).ServeHTTP,
)
router.Get("/"+path.Join(handler.cfg.RootPath, "ssrEnterLobby/{lobby_id}"), handler.ssrEnterLobby)
router.Post("/"+path.Join(handler.cfg.RootPath, "ssrCreateLobby"), handler.ssrCreateLobby)
router.Get("/"+path.Join(handler.cfg.RootPath, "lobby/{lobby_id}/gallery"), handler.ssrGallery)
router.Get("/"+path.Join(handler.cfg.RootPath, "lobby/{lobby_id}"), handler.ssrEnterLobby)
router.Post("/"+path.Join(handler.cfg.RootPath, "create_lobby"), handler.ssrCreateLobby)
}

// errorPageData represents the data that error.html requires to be displayed.
Expand All @@ -86,3 +89,33 @@ func (handler *SSRHandler) userFacingError(w http.ResponseWriter, errorMessage s
panic(err)
}
}

func determineTranslation(r *http.Request) (translations.Translation, string) {
languageTags, _, err := language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
if err == nil {
for _, languageTag := range languageTags {
fullLanguageIdentifier := languageTag.String()
fullLanguageIdentifierLowercased := strings.ToLower(fullLanguageIdentifier)
translation := translations.GetLanguage(fullLanguageIdentifierLowercased)
if translation != nil {
return translation, fullLanguageIdentifierLowercased
}

baseLanguageIdentifier, _ := languageTag.Base()
baseLanguageIdentifierLowercased := strings.ToLower(baseLanguageIdentifier.String())
translation = translations.GetLanguage(baseLanguageIdentifierLowercased)
if translation != nil {
return translation, baseLanguageIdentifierLowercased
}
}
}

return translations.DefaultTranslation, "en-us"
}

func isHumanAgent(userAgent string) bool {
return strings.Contains(userAgent, "gecko") ||
strings.Contains(userAgent, "chrome") ||
strings.Contains(userAgent, "opera") ||
strings.Contains(userAgent, "safari")
}
26 changes: 1 addition & 25 deletions internal/frontend/lobby.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/scribble-rs/scribble.rs/internal/api"
"github.com/scribble-rs/scribble.rs/internal/state"
"github.com/scribble-rs/scribble.rs/internal/translations"
"golang.org/x/text/language"
)

type lobbyPageData struct {
Expand All @@ -34,7 +33,7 @@ func (handler *SSRHandler) ssrEnterLobby(writer http.ResponseWriter, request *ht
}

userAgent := strings.ToLower(request.UserAgent())
if !(strings.Contains(userAgent, "gecko") || strings.Contains(userAgent, "chrome") || strings.Contains(userAgent, "opera") || strings.Contains(userAgent, "safari")) {
if !isHumanAgent(userAgent) {
err := pageTemplates.ExecuteTemplate(writer, "robot-page", &robotPageData{
BasePageConfig: handler.basePageConfig,
LobbyData: api.CreateLobbyData(lobby),
Expand Down Expand Up @@ -91,26 +90,3 @@ func (handler *SSRHandler) ssrEnterLobby(writer http.ResponseWriter, request *ht
}
}
}

func determineTranslation(r *http.Request) (translations.Translation, string) {
languageTags, _, err := language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
if err == nil {
for _, languageTag := range languageTags {
fullLanguageIdentifier := languageTag.String()
fullLanguageIdentifierLowercased := strings.ToLower(fullLanguageIdentifier)
translation := translations.GetLanguage(fullLanguageIdentifierLowercased)
if translation != nil {
return translation, fullLanguageIdentifierLowercased
}

baseLanguageIdentifier, _ := languageTag.Base()
baseLanguageIdentifierLowercased := strings.ToLower(baseLanguageIdentifier.String())
translation = translations.GetLanguage(baseLanguageIdentifierLowercased)
if translation != nil {
return translation, baseLanguageIdentifierLowercased
}
}
}

return translations.DefaultTranslation, "en-us"
}
Loading

0 comments on commit 6245230

Please sign in to comment.