From c5078ad34105cdfb4d61c3be9800c57cb2a991a2 Mon Sep 17 00:00:00 2001 From: Gareth Latty Date: Fri, 20 May 2022 21:11:44 +0100 Subject: [PATCH] Add AtP ad, ad configuration. --- client/assets/images/at-the-party/at-the.svg | 21 +++ client/assets/images/at-the-party/lines.svg | 1 + client/assets/images/at-the-party/party.svg | 21 +++ client/src/elm/MassiveDecks.elm | 17 +- client/src/elm/MassiveDecks/Messages.elm | 4 +- client/src/elm/MassiveDecks/Model.elm | 3 + .../src/elm/MassiveDecks/Models/Decoders.elm | 17 ++ client/src/elm/MassiveDecks/Pages/Start.elm | 36 ++++- client/src/elm/MassiveDecks/Requests/Api.elm | 13 +- client/src/elm/MassiveDecks/ServerConfig.elm | 19 +++ client/src/elm/MassiveDecks/Strings.elm | 2 +- .../elm/MassiveDecks/Strings/Languages/De.elm | 13 +- .../Strings/Languages/DeXInformal.elm | 13 +- .../Strings/Languages/En/Internal.elm | 13 +- .../elm/MassiveDecks/Strings/Languages/Es.elm | 13 +- .../elm/MassiveDecks/Strings/Languages/Id.elm | 13 +- .../elm/MassiveDecks/Strings/Languages/It.elm | 13 +- .../elm/MassiveDecks/Strings/Languages/Pl.elm | 13 +- .../MassiveDecks/Strings/Languages/PtBR.elm | 13 +- client/src/scss/_settings.scss | 5 + client/src/scss/pages/_start.scss | 152 ++++++++++++++++-- client/webpack.config.js | 6 +- server/config.json5 | 10 ++ server/src/ts/config.ts | 18 ++- server/src/ts/index.ts | 30 ++-- 25 files changed, 413 insertions(+), 66 deletions(-) create mode 100644 client/assets/images/at-the-party/at-the.svg create mode 100644 client/assets/images/at-the-party/lines.svg create mode 100644 client/assets/images/at-the-party/party.svg create mode 100644 client/src/elm/MassiveDecks/ServerConfig.elm diff --git a/client/assets/images/at-the-party/at-the.svg b/client/assets/images/at-the-party/at-the.svg new file mode 100644 index 0000000..97fa625 --- /dev/null +++ b/client/assets/images/at-the-party/at-the.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/client/assets/images/at-the-party/lines.svg b/client/assets/images/at-the-party/lines.svg new file mode 100644 index 0000000..4bcee81 --- /dev/null +++ b/client/assets/images/at-the-party/lines.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/assets/images/at-the-party/party.svg b/client/assets/images/at-the-party/party.svg new file mode 100644 index 0000000..269a34b --- /dev/null +++ b/client/assets/images/at-the-party/party.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/client/src/elm/MassiveDecks.elm b/client/src/elm/MassiveDecks.elm index 0d3366d..6ff3317 100644 --- a/client/src/elm/MassiveDecks.elm +++ b/client/src/elm/MassiveDecks.elm @@ -88,7 +88,9 @@ init flags url key = , speech = speech , notifications = Notifications.init , remoteMode = remoteMode + , serverVersion = Nothing , sources = { builtIn = Nothing, manyDecks = Nothing, jsonAgainstHumanity = Nothing } + , adverts = { manyDecks = False, atTheParty = False } } ( page, pageCmd ) = @@ -99,7 +101,7 @@ init flags url key = ( Pages.Loading, Cmd.none ) sourceCmd = - Request.map (Error.Add >> ErrorMsg) never UpdateSources |> Api.sourceInfo |> Http.request + Request.map (Error.Add >> ErrorMsg) never UpdateServerConfig |> Api.serverConfig |> Http.request in ( { page = page, shared = shared, errorOverlay = Overlay.init } , Cmd.batch [ sourceCmd, pageCmd, settingsCmd, speechCmd ] @@ -271,12 +273,21 @@ update msg model = in ( { model | shared = { oldShared | notifications = notifications } }, notificationsCmd ) - UpdateSources info -> + UpdateServerConfig { serverVersion, sourceInfo, adverts } -> let oldShared = model.shared in - ( { model | shared = { oldShared | sources = info } }, Cmd.none ) + ( { model + | shared = + { oldShared + | serverVersion = Just serverVersion + , sources = sourceInfo + , adverts = adverts + } + } + , Cmd.none + ) Refresh -> ( model, Navigation.reload ) diff --git a/client/src/elm/MassiveDecks/Messages.elm b/client/src/elm/MassiveDecks/Messages.elm index 2d43ff1..395bc5f 100644 --- a/client/src/elm/MassiveDecks/Messages.elm +++ b/client/src/elm/MassiveDecks/Messages.elm @@ -1,6 +1,5 @@ module MassiveDecks.Messages exposing (Msg(..)) -import MassiveDecks.Card.Source.Model as Source import MassiveDecks.Cast.Model as Cast import MassiveDecks.Error.Messages as Error import MassiveDecks.Notifications.Model as Notifications @@ -8,6 +7,7 @@ import MassiveDecks.Pages.Lobby.Messages as Lobby import MassiveDecks.Pages.Lobby.Model as Lobby import MassiveDecks.Pages.Route exposing (Route) import MassiveDecks.Pages.Start.Messages as Start +import MassiveDecks.ServerConfig exposing (ServerConfig) import MassiveDecks.Settings.Messages as Settings import MassiveDecks.Speech as Speech import Url exposing (Url) @@ -28,6 +28,6 @@ type Msg | UpdateToken Lobby.Auth | CastStatusUpdate Cast.Status | RemoteCommand Cast.RemoteControlCommand - | UpdateSources Source.Info + | UpdateServerConfig ServerConfig | Refresh | BlockedExternalUrl diff --git a/client/src/elm/MassiveDecks/Model.elm b/client/src/elm/MassiveDecks/Model.elm index b1cb968..3f0cb5e 100644 --- a/client/src/elm/MassiveDecks/Model.elm +++ b/client/src/elm/MassiveDecks/Model.elm @@ -7,6 +7,7 @@ import Browser.Navigation as Navigation import MassiveDecks.Card.Source.Model as Sources import MassiveDecks.Cast.Model as Cast import MassiveDecks.Notifications.Model as Notifications +import MassiveDecks.ServerConfig exposing (Adverts) import MassiveDecks.Settings.Model as Settings exposing (Settings) import MassiveDecks.Speech as Speech import MassiveDecks.Strings.Languages.Model exposing (Language) @@ -24,7 +25,9 @@ type alias Shared = , speech : Speech.Model , notifications : Notifications.Model , remoteMode : Bool + , serverVersion : Maybe String , sources : Sources.Info + , adverts : Adverts } diff --git a/client/src/elm/MassiveDecks/Models/Decoders.elm b/client/src/elm/MassiveDecks/Models/Decoders.elm index b8593a1..9f8394d 100644 --- a/client/src/elm/MassiveDecks/Models/Decoders.elm +++ b/client/src/elm/MassiveDecks/Models/Decoders.elm @@ -16,6 +16,7 @@ module MassiveDecks.Models.Decoders exposing , privilege , remoteControlCommand , revealingRound + , serverConfig , settings , sourceInfo , specificRound @@ -53,6 +54,7 @@ import MassiveDecks.Pages.Lobby.Events as Events exposing (Event) import MassiveDecks.Pages.Lobby.GameCode as GameCode exposing (GameCode) import MassiveDecks.Pages.Lobby.Model as Lobby exposing (Lobby) import MassiveDecks.Pages.Start.LobbyBrowser.Model as LobbyBrowser +import MassiveDecks.ServerConfig as ServerConfig exposing (ServerConfig) import MassiveDecks.Settings.Model as Settings exposing (Settings) import MassiveDecks.Speech as Speech import MassiveDecks.Strings.Languages as Lang @@ -62,6 +64,13 @@ import MassiveDecks.Util.NeList as NonEmptyList import Set exposing (Set) +adverts : Json.Decoder ServerConfig.Adverts +adverts = + Json.succeed ServerConfig.Adverts + |> Json.optional "manyDecks" Json.bool False + |> Json.optional "atTheParty" Json.bool False + + sourceInfo : Json.Decoder Source.Info sourceInfo = let @@ -79,6 +88,14 @@ sourceInfo = |> Json.andThen atLeastOne +serverConfig : Json.Decoder ServerConfig +serverConfig = + Json.succeed ServerConfig + |> Json.required "version" Json.string + |> Json.required "sources" sourceInfo + |> Json.optional "adverts" adverts { manyDecks = False, atTheParty = False } + + builtInInfo : Json.Decoder BuiltIn.Info builtInInfo = Json.succeed BuiltIn.Info diff --git a/client/src/elm/MassiveDecks/Pages/Start.elm b/client/src/elm/MassiveDecks/Pages/Start.elm index 1868844..60b1493 100644 --- a/client/src/elm/MassiveDecks/Pages/Start.elm +++ b/client/src/elm/MassiveDecks/Pages/Start.elm @@ -200,13 +200,34 @@ view shared model = model.route manyDecksAd { baseUrl } = - Html.blankA - [ HtmlA.href baseUrl, HtmlA.id "many-decks-ad", Strings.ManyDecksWhereToGet |> Lang.title shared ] - [ Html.div [] - [ Icon.boxOpen |> Icon.viewIcon - , Html.span [] [ Strings.ManyDecks |> Lang.string shared |> Html.text ] + if shared.adverts.manyDecks then + Html.blankA + [ HtmlA.href baseUrl, HtmlA.id "many-decks-ad", Strings.ManyDecksWhereToGet |> Lang.title shared ] + [ Html.div [] + [ Icon.boxOpen |> Icon.viewIcon + , Html.span [] [ Strings.ManyDecks |> Lang.string shared |> Html.text ] + ] ] - ] + + else + Html.nothing + + atThePartyAd = + if shared.adverts.atTheParty then + Html.div [ HtmlA.id "at-the-party-ad" ] + [ Html.div [] + [ Html.blankA + [ HtmlA.href "https://store.steampowered.com/app/1506530/At_The_Party" + , HtmlA.title "Check out Reread Game's new game, At The Party!" + ] + [ Html.div [ HtmlA.class "at-the" ] [ Html.span [] [ Html.text "At The" ] ] + , Html.div [ HtmlA.class "party" ] [ Html.span [] [ Html.text "Party" ] ] + ] + ] + ] + + else + Html.nothing in [ Html.div [ HtmlA.class "page start" ] [ overlay shared model.overlay @@ -247,11 +268,12 @@ view shared model = [ Html.text "\"" , Strings.MassiveDecks |> Lang.html shared , Html.text "\" " - , Strings.Version { versionNumber = Version.version } |> Lang.html shared + , Strings.Version { clientVersion = Version.version, serverVersion = shared.serverVersion } |> Lang.html shared ] ] ] , shared.sources.manyDecks |> Maybe.map manyDecksAd |> Maybe.withDefault Html.nothing + , atThePartyAd ] diff --git a/client/src/elm/MassiveDecks/Requests/Api.elm b/client/src/elm/MassiveDecks/Requests/Api.elm index 83d8543..c3316be 100644 --- a/client/src/elm/MassiveDecks/Requests/Api.elm +++ b/client/src/elm/MassiveDecks/Requests/Api.elm @@ -3,7 +3,7 @@ module MassiveDecks.Requests.Api exposing , joinLobby , lobbySummaries , newLobby - , sourceInfo + , serverConfig ) import Http @@ -19,6 +19,7 @@ import MassiveDecks.Pages.Lobby.Token as Token import MassiveDecks.Pages.Start.LobbyBrowser.Model as LobbyBrowser import MassiveDecks.Pages.Start.Model as Start import MassiveDecks.Requests.Request as Request exposing (Request) +import MassiveDecks.ServerConfig exposing (ServerConfig) import MassiveDecks.User as User import MassiveDecks.Util.Result as Result import Url.Builder @@ -80,15 +81,15 @@ checkAlive msg tokens = } -{-| Find out what sources the server offers. +{-| Get configuration from the server. -} -sourceInfo : (Request.Response Never Source.Info -> msg) -> Request msg -sourceInfo msg = +serverConfig : (Request.Response Never ServerConfig -> msg) -> Request msg +serverConfig msg = { method = "GET" , headers = [] - , url = url [ "sources" ] + , url = url [ "config" ] , body = Http.emptyBody - , expect = Request.expectResponse msg noError Decoders.sourceInfo + , expect = Request.expectResponse msg noError Decoders.serverConfig , timeout = Nothing , tracker = Nothing } diff --git a/client/src/elm/MassiveDecks/ServerConfig.elm b/client/src/elm/MassiveDecks/ServerConfig.elm new file mode 100644 index 0000000..4557d3a --- /dev/null +++ b/client/src/elm/MassiveDecks/ServerConfig.elm @@ -0,0 +1,19 @@ +module MassiveDecks.ServerConfig exposing + ( Adverts + , ServerConfig + ) + +import MassiveDecks.Card.Source.Model as Source + + +type alias Adverts = + { manyDecks : Bool + , atTheParty : Bool + } + + +type alias ServerConfig = + { serverVersion : String + , sourceInfo : Source.Info + , adverts : Adverts + } diff --git a/client/src/elm/MassiveDecks/Strings.elm b/client/src/elm/MassiveDecks/Strings.elm index 90c037e..9d295a0 100644 --- a/client/src/elm/MassiveDecks/Strings.elm +++ b/client/src/elm/MassiveDecks/Strings.elm @@ -54,7 +54,7 @@ type MdString -- Special | Noun { noun : Noun, quantity : Quantity } -- The given noun, described in the given quantity and context. -- Start screen. - | Version { versionNumber : String } -- The version of the game being played. + | Version { clientVersion : String, serverVersion : Maybe String } -- The version of the game being played. | ShortGameDescription -- A one-line description of the game. | WhatIsThis -- A title for a section describing the game. | GameDescription -- A long description of the game. diff --git a/client/src/elm/MassiveDecks/Strings/Languages/De.elm b/client/src/elm/MassiveDecks/Strings/Languages/De.elm index f8e6311..f51be43 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/De.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/De.elm @@ -92,8 +92,17 @@ translate _ mdString = [ Text "Zuschauer" ] -- Start screen. - Version { versionNumber } -> - [ Text "Version “", Text versionNumber, Text "”" ] + Version { clientVersion, serverVersion } -> + let + quote version = + [ Text "“", Text version, Text "”" ] + in + List.concat + [ [ Text "Version " ] + , clientVersion |> quote + , [ Text " / " ] + , serverVersion |> Maybe.map quote |> Maybe.withDefault [] + ] ShortGameDescription -> [ Text "Ein lustiges Party-Spiel." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/DeXInformal.elm b/client/src/elm/MassiveDecks/Strings/Languages/DeXInformal.elm index d9c059c..e316499 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/DeXInformal.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/DeXInformal.elm @@ -91,8 +91,17 @@ translate _ mdString = [ Text "Zuschauer" ] -- Start screen. - Version { versionNumber } -> - [ Text "Version “", Text versionNumber, Text "”" ] + Version { clientVersion, serverVersion } -> + let + quote version = + [ Text "“", Text version, Text "”" ] + in + List.concat + [ [ Text "Version " ] + , clientVersion |> quote + , [ Text " / " ] + , serverVersion |> Maybe.map quote |> Maybe.withDefault [] + ] ShortGameDescription -> [ Text "Ein lustiges Party-Spiel." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/En/Internal.elm b/client/src/elm/MassiveDecks/Strings/Languages/En/Internal.elm index 91b8fbc..7769e49 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/En/Internal.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/En/Internal.elm @@ -46,8 +46,17 @@ translate _ mdString = List.concat [ singular, plural ] -- Start screen. - Version { versionNumber } -> - [ Text "Version “", Text versionNumber, Text "”" ] + Version { clientVersion, serverVersion } -> + let + quote version = + [ Text "“", Text version, Text "”" ] + in + List.concat + [ [ Text "Version " ] + , clientVersion |> quote + , [ Text " / " ] + , serverVersion |> Maybe.map quote |> Maybe.withDefault [] + ] ShortGameDescription -> [ Text "A comedy party game." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/Es.elm b/client/src/elm/MassiveDecks/Strings/Languages/Es.elm index eed068b..fde423e 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/Es.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/Es.elm @@ -68,8 +68,17 @@ translate _ mdString = List.concat [ singular, plural ] -- Start screen. - Version { versionNumber } -> - [ Text "Versión “", Text versionNumber, Text "”" ] + Version { clientVersion, serverVersion } -> + let + quote version = + [ Text "“", Text version, Text "”" ] + in + List.concat + [ [ Text "Versión " ] + , clientVersion |> quote + , [ Text " / " ] + , serverVersion |> Maybe.map quote |> Maybe.withDefault [] + ] ShortGameDescription -> [ Text "Un juego para fiestas de comedia." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/Id.elm b/client/src/elm/MassiveDecks/Strings/Languages/Id.elm index 67e4b1b..f368106 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/Id.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/Id.elm @@ -81,8 +81,17 @@ translate _ mdString = List.concat [ singular, plural ] -- Start screen. - Version { versionNumber } -> - [ Text "Versi “", Text versionNumber, Text "”" ] + Version { clientVersion, serverVersion } -> + let + quote version = + [ Text "“", Text version, Text "”" ] + in + List.concat + [ [ Text "Versi " ] + , clientVersion |> quote + , [ Text " / " ] + , serverVersion |> Maybe.map quote |> Maybe.withDefault [] + ] ShortGameDescription -> [ Text "Sebuah permainan pesta komedi." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/It.elm b/client/src/elm/MassiveDecks/Strings/Languages/It.elm index c3889a8..a24d083 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/It.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/It.elm @@ -91,8 +91,17 @@ translate _ mdString = [ Text "Spettatori" ] -- Start screen. - Version { versionNumber } -> - [ Text "Versione “", Text versionNumber, Text "”" ] + Version { clientVersion, serverVersion } -> + let + quote version = + [ Text "“", Text version, Text "”" ] + in + List.concat + [ [ Text "Versione " ] + , clientVersion |> quote + , [ Text " / " ] + , serverVersion |> Maybe.map quote |> Maybe.withDefault [] + ] ShortGameDescription -> [ Text "Un divertente gioco di società." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/Pl.elm b/client/src/elm/MassiveDecks/Strings/Languages/Pl.elm index bd67805..943fae7 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/Pl.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/Pl.elm @@ -67,8 +67,17 @@ translate maybeDeclCase mdString = [ Text (decl noun quantity declCase) ] -- Start screen. - Version { versionNumber } -> - [ Text "Wersja „", Text versionNumber, Text "”" ] + Version { clientVersion, serverVersion } -> + let + quote version = + [ Text "„", Text version, Text "”" ] + in + List.concat + [ [ Text "Wersja " ] + , clientVersion |> quote + , [ Text " / " ] + , serverVersion |> Maybe.map quote |> Maybe.withDefault [] + ] ShortGameDescription -> [ Text "Komediowa gra towarzyska." ] diff --git a/client/src/elm/MassiveDecks/Strings/Languages/PtBR.elm b/client/src/elm/MassiveDecks/Strings/Languages/PtBR.elm index 80491b5..1ed21e6 100644 --- a/client/src/elm/MassiveDecks/Strings/Languages/PtBR.elm +++ b/client/src/elm/MassiveDecks/Strings/Languages/PtBR.elm @@ -91,8 +91,17 @@ translate _ mdString = [ Text "Espectadores" ] -- Start screen. - Version { versionNumber } -> - [ Text "Versão “", Text versionNumber, Text "”" ] + Version { clientVersion, serverVersion } -> + let + quote version = + [ Text "“", Text version, Text "”" ] + in + List.concat + [ [ Text "Versão " ] + , clientVersion |> quote + , [ Text " / " ] + , serverVersion |> Maybe.map quote |> Maybe.withDefault [] + ] ShortGameDescription -> [ Text "Um jogo de festa de comédia." ] diff --git a/client/src/scss/_settings.scss b/client/src/scss/_settings.scss index ab9af48..e1072ce 100644 --- a/client/src/scss/_settings.scss +++ b/client/src/scss/_settings.scss @@ -9,6 +9,11 @@ display: flex; justify-content: flex-end; z-index: 10; + pointer-events: none; + + > div { + pointer-events: all; + } } .settings-panel { diff --git a/client/src/scss/pages/_start.scss b/client/src/scss/pages/_start.scss index a54f966..b36fd68 100644 --- a/client/src/scss/pages/_start.scss +++ b/client/src/scss/pages/_start.scss @@ -38,7 +38,7 @@ flex-wrap: wrap; justify-content: center; overflow: hidden; - padding: 4em 0 0 2em; + padding: 5em 0 0 2em; h1, .subtitle { @@ -177,10 +177,12 @@ } } -#many-decks-ad div { +#many-decks-ad > div { + font-size: clamp(0.9rem, 3vw, 1.2rem); + position: fixed; - left: -0.8em; - top: -3em; + left: 0; + top: -6.5em; z-index: 11; @@ -191,14 +193,11 @@ justify-content: flex-start; width: 7em; - height: 7em; - border-radius: 100%; + height: 10em; + border-radius: 7em; transition: left 0.25s, top 0.25s; - transform: rotate(-15deg); - - font-size: 1.2em; background-color: colors.$many-decks; color: #ffffff; @@ -207,7 +206,138 @@ } &:hover { - left: -0.7em; - top: -2.5em; + left: 0em; + top: -5.5em; + } +} + +@keyframes sway { + 0% { + transform: rotateX(0); + } + 33% { + transform: rotateX(30deg); + } + 66% { + transform: rotateX(-30deg); + } + 100% { + transform: rotateX(0); + } +} + +#at-the-party-ad { + font-size: clamp(0.25rem, 2vw, 1.25rem); + + > div { + position: fixed; + right: -5em; + top: 2rem; + + transform: rotate(15deg); + + width: 30em; + perspective: 20em; + + &::before { + content: ""; + display: block; + + position: absolute; + right: -0.4em; + top: -0.4em; + + background-image: url(../../../assets/images/at-the-party/lines.svg); + background-size: 7em 100%; + + width: 40em; + height: 6.5em; + box-sizing: border-box; + + z-index: -1; + } + + > a { + display: block; + width: 50%; + aspect-ratio: 135 / 53; + margin: auto; + background: none; + + transition: none; + + position: relative; + + &::before { + transform: rotate(-15deg); + content: ""; + display: block; + + position: absolute; + right: -10em; + top: -2em; + + width: 50em; + height: 22em; + + pointer-events: none; + + opacity: 0; + transition: opacity 0.8s; + + background-image: url(data:image/svg+xml;base64,CiAgPHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMDAlJyBoZWlnaHQ9JzEwMCUnPgogICAgPHJlY3Qgd2lkdGg9JzEwMDAlJyBoZWlnaHQ9JzEwMDAlJyBmaWxsPScjZmZmJy8+CiAgICA8cmVjdCB3aWR0aD0nMTAwJScgaGVpZ2h0PScxMDAlJyBmaWxsPScjZmYwMDAwJy8+CiAgICA8c3ZnIHg9JzUwJScgeT0nMTAlJyBvdmVyZmxvdz0ndmlzaWJsZSc+CiAgICAgIDxyZWN0IHdpZHRoPScyMDAwJScgaGVpZ2h0PScyMDAwJScgZmlsbC1vcGFjaXR5PScwLjEnIGZpbGw9JyMwMDAwMDAnIHRyYW5zZm9ybT0ncm90YXRlKDIwKScvPgogICAgICA8cmVjdCB3aWR0aD0nMjAwMCUnIGhlaWdodD0nMjAwMCUnIGZpbGwtb3BhY2l0eT0nMC4xJyBmaWxsPScjMDAwMDAwJyB0cmFuc2Zvcm09J3JvdGF0ZSg0MCknLz4KICAgICAgPHJlY3Qgd2lkdGg9JzIwMDAlJyBoZWlnaHQ9JzIwMDAlJyBmaWxsLW9wYWNpdHk9JzAuMScgZmlsbD0nIzAwMDAwMCcgdHJhbnNmb3JtPSdyb3RhdGUoNjApJy8+CiAgICAgIDxyZWN0IHdpZHRoPScyMDAwJScgaGVpZ2h0PScyMDAwJScgZmlsbC1vcGFjaXR5PScwLjEnIGZpbGw9JyMwMDAwMDAnIHRyYW5zZm9ybT0ncm90YXRlKDgwKScvPgogICAgICA8cmVjdCB3aWR0aD0nMjAwMCUnIGhlaWdodD0nMjAwMCUnIGZpbGwtb3BhY2l0eT0nMC4xJyBmaWxsPScjMDAwMDAwJyB0cmFuc2Zvcm09J3JvdGF0ZSgxMDApJy8+CiAgICAgIDxyZWN0IHdpZHRoPScyMDAwJScgaGVpZ2h0PScyMDAwJScgZmlsbC1vcGFjaXR5PScwLjEnIGZpbGw9JyMwMDAwMDAnIHRyYW5zZm9ybT0ncm90YXRlKDEyMCknLz4KICAgICAgPHJlY3Qgd2lkdGg9JzIwMDAlJyBoZWlnaHQ9JzIwMDAlJyBmaWxsLW9wYWNpdHk9JzAuMScgZmlsbD0nIzAwMDAwMCcgdHJhbnNmb3JtPSdyb3RhdGUoMTQwKScvPgogICAgICA8cmVjdCB3aWR0aD0nMjAwMCUnIGhlaWdodD0nMjAwMCUnIGZpbGwtb3BhY2l0eT0nMC4xJyBmaWxsPScjMDAwMDAwJyB0cmFuc2Zvcm09J3JvdGF0ZSgxNjApJy8+CiAgICAgIDxyZWN0IHdpZHRoPScyMDAwJScgaGVpZ2h0PScyMDAwJScgZmlsbC1vcGFjaXR5PScwLjEnIGZpbGw9JyMwMDAwMDAnIHRyYW5zZm9ybT0ncm90YXRlKDE4MCknLz4KICAgICAgPHJlY3Qgd2lkdGg9JzIwMDAlJyBoZWlnaHQ9JzIwMDAlJyBmaWxsLW9wYWNpdHk9JzAuMScgZmlsbD0nIzAwMDAwMCcgdHJhbnNmb3JtPSdyb3RhdGUoMjAwKScvPgogICAgICA8cmVjdCB3aWR0aD0nMjAwMCUnIGhlaWdodD0nMjAwMCUnIGZpbGwtb3BhY2l0eT0nMC4xJyBmaWxsPScjMDAwMDAwJyB0cmFuc2Zvcm09J3JvdGF0ZSgyMjApJy8+CiAgICAgIDxyZWN0IHdpZHRoPScyMDAwJScgaGVpZ2h0PScyMDAwJScgZmlsbC1vcGFjaXR5PScwLjEnIGZpbGw9JyMwMDAwMDAnIHRyYW5zZm9ybT0ncm90YXRlKDI0MCknLz4KICAgICAgPHJlY3Qgd2lkdGg9JzIwMDAlJyBoZWlnaHQ9JzIwMDAlJyBmaWxsLW9wYWNpdHk9JzAuMScgZmlsbD0nIzAwMDAwMCcgdHJhbnNmb3JtPSdyb3RhdGUoMjYwKScvPgogICAgICA8cmVjdCB3aWR0aD0nMjAwMCUnIGhlaWdodD0nMjAwMCUnIGZpbGwtb3BhY2l0eT0nMC4xJyBmaWxsPScjMDAwMDAwJyB0cmFuc2Zvcm09J3JvdGF0ZSgyODApJy8+CiAgICAgIDxyZWN0IHdpZHRoPScyMDAwJScgaGVpZ2h0PScyMDAwJScgZmlsbC1vcGFjaXR5PScwLjEnIGZpbGw9JyMwMDAwMDAnIHRyYW5zZm9ybT0ncm90YXRlKDMwMCknLz4KICAgICAgPHJlY3Qgd2lkdGg9JzIwMDAlJyBoZWlnaHQ9JzIwMDAlJyBmaWxsLW9wYWNpdHk9JzAuMScgZmlsbD0nIzAwMDAwMCcgdHJhbnNmb3JtPSdyb3RhdGUoMzIwKScvPgogICAgICA8cmVjdCB3aWR0aD0nMjAwMCUnIGhlaWdodD0nMjAwMCUnIGZpbGwtb3BhY2l0eT0nMC4xJyBmaWxsPScjMDAwMDAwJyB0cmFuc2Zvcm09J3JvdGF0ZSgzNDApJy8+CiAgICAgIDxyZWN0IHdpZHRoPScyMDAwJScgaGVpZ2h0PScyMDAwJScgZmlsbC1vcGFjaXR5PScwLjEnIGZpbGw9JyMwMDAwMDAnIHRyYW5zZm9ybT0ncm90YXRlKDM2MCknLz4KCiAgICA8L3N2Zz4KICA8L3N2Zz4=); + background-size: 150% 150%; + background-position: 15% 5%; + mask-image: radial-gradient( + ellipse at top right, + #000000 0, + #000000 40%, + transparent 60% + ); + + z-index: -10; + } + + &:hover::before { + opacity: 1; + } + + &:hover { + background-position: inherit; + } + + > div { + content: ""; + background-size: contain; + + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + + z-index: 0; + + > span { + display: none; + } + + animation: sway 4s ease-in-out 0s alternate infinite; + + @media (prefers-reduced-motion) { + animation: none; + } + } + + > .at-the { + transform-origin: top center; + background-image: url(../../../assets/images/at-the-party/at-the.svg); + } + + > .party { + background-image: url(../../../assets/images/at-the-party/party.svg); + animation-delay: 0.5s; + } + } } } diff --git a/client/webpack.config.js b/client/webpack.config.js index baaaf8c..c255650 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -138,7 +138,7 @@ module.exports = (env, argv) => { test: /\.(jpg|png|svg)$/, type: "asset/resource", generator: { - filename: "assets/images/[name].[hash].[ext]", + filename: "assets/images/[name].[hash][ext]", }, }, // Font assets. @@ -146,7 +146,7 @@ module.exports = (env, argv) => { test: /\.(woff2)$/, type: "asset/resource", generator: { - filename: "assets/fonts/[name].[hash].[ext]", + filename: "assets/fonts/[name].[hash][ext]", }, }, // App manifest. @@ -155,7 +155,7 @@ module.exports = (env, argv) => { exclude: [/elm-stuff/, /node_modules/, /dist/], type: "asset/resource", generator: { - filename: "assets/[name].[hash].[ext]", + filename: "assets/[name].[hash][ext]", }, use: [ { diff --git a/server/config.json5 b/server/config.json5 index 9583a85..eab24f2 100644 --- a/server/config.json5 +++ b/server/config.json5 @@ -173,6 +173,16 @@ checkAfter: "PT5M", }, + // Show additional content to users of the application. These are not adverts + // you can buy, just related projects to Massive Decks. + adverts: { + // Show a link to Many Decks for users to build their own decks. + // Note this will only work if you have Many Decks set as a source. + manyDecks: true, + // Show a link to At The Party, Reread Games new game. + atTheParty: false, + }, + // A file to touch when the server is started. If null, we won't touch any files. touchOnStart: null, // This will be overwritten by the environment variable MD_TOUCH_ON_START, if set. diff --git a/server/src/ts/config.ts b/server/src/ts/config.ts index ea15f07..1fc0424 100644 --- a/server/src/ts/config.ts +++ b/server/src/ts/config.ts @@ -29,6 +29,7 @@ export interface Config extends EnvironmentalConfig { storage: BaseStorage; cache: BaseCache; defaults: LobbyConfig.Defaults; + adverts?: Adverts; } export type Parsed = Config; @@ -121,8 +122,13 @@ export type PostgreSQLCache = BasePostgreSQLCache; const parseDuration = (unparsed: UnparsedDuration): ParsedDuration => Moment.duration(unparsed).asMilliseconds(); +interface Adverts { + manyDecks?: boolean; + atTheParty?: boolean; +} + export const parseStorage = ( - storage: BaseStorage + storage: BaseStorage, ): BaseStorage => ({ ...storage, abandonedTime: parseDuration(storage.abandonedTime), @@ -130,28 +136,28 @@ export const parseStorage = ( }); export const parseCache = ( - cache: BaseCache + cache: BaseCache, ): BaseCache => ({ ...cache, checkAfter: parseDuration(cache.checkAfter), }); export const parseTimeouts = ( - timeouts: Timeouts + timeouts: Timeouts, ): Timeouts => Util.mapObjectValues(timeouts, (key: string, value: UnparsedDuration) => - parseDuration(value) + parseDuration(value), ); export const parseTasks = ( - tasks: Tasks + tasks: Tasks, ): Tasks => ({ ...tasks, processTickFrequency: parseDuration(tasks.processTickFrequency), }); const parseManyDecks = ( - manyDecks: BaseManyDecks + manyDecks: BaseManyDecks, ): ManyDecks => ({ ...manyDecks, baseUrl: manyDecks.baseUrl.endsWith("/") diff --git a/server/src/ts/index.ts b/server/src/ts/index.ts index 62bc460..e031d4e 100644 --- a/server/src/ts/index.ts +++ b/server/src/ts/index.ts @@ -50,8 +50,8 @@ function getConfigFilePath(): string { async function main(): Promise { const config = ServerConfig.parse( JSON5.parse( - (await fs.readFile(getConfigFilePath())).toString() - ) as ServerConfig.Unparsed + (await fs.readFile(getConfigFilePath())).toString(), + ) as ServerConfig.Unparsed, ); const { app } = ws(express()); @@ -64,7 +64,7 @@ async function main(): Promise { if (environment !== "development" && config.secret === "CHANGE ME") { throw new Error( "Secret not set - this should never be the case outside of " + - "development. Please set a real random secret value in 'config.json5'." + "development. Please set a real random secret value in 'config.json5'.", ); } @@ -75,11 +75,19 @@ async function main(): Promise { app.use( expressWinston.logger({ winstonInstance: Logging.logger, - }) + }), ); app.get("/api/version", async (req, res) => res.json(config.version)); + app.get("/api/config", async (req, res) => + res.json({ + version: config.version, + sources: state.sources.clientInfo(), + adverts: config.adverts, + }), + ); + app.get("/api/games", async (req, res) => { const result = []; for await (const summary of state.store.lobbySummaries()) { @@ -92,14 +100,14 @@ async function main(): Promise { const { gameCode, token, tasks } = await state.store.newLobby( CreateLobby.validate(req.body), config.secret, - config.defaults + config.defaults, ); for (const task of tasks) { state.tasks.enqueue(state, task); } res.append( "Location", - `${req.protocol}://${req.hostname}/${config.basePath}api/games/${gameCode}` + `${req.protocol}://${req.hostname}/${config.basePath}api/games/${gameCode}`, ); res.status(HttpStatus.CREATED).json(token); }); @@ -111,7 +119,7 @@ async function main(): Promise { const claims = Token.validate( current, await state.store.id(), - state.config.secret + state.config.secret, ); if (await state.store.exists(claims.gc)) { result.push(current); @@ -133,7 +141,7 @@ async function main(): Promise { } const newUser = User.create( registration, - lobby.config.audienceMode ? "Spectator" : "Player" + lobby.config.audienceMode ? "Spectator" : "Player", ); if ( wu(Object.values(lobby.users)).find((u) => u.name === registration.name) @@ -148,7 +156,7 @@ async function main(): Promise { game.playerOrder.push(id); if (newUser.role === "Player") { game.players[id] = Player.initial( - game.decks.responses.draw(game.rules.handSize) + game.decks.responses.draw(game.rules.handSize), ); } } @@ -208,7 +216,7 @@ async function main(): Promise { expressWinston.errorLogger({ winstonInstance: Logging.logger, msg: "{{err.message}}", - }) + }), ); app.use((error: Error, req: Request, res: Response, next: NextFunction) => { @@ -253,7 +261,7 @@ async function main(): Promise { state.tasks .loadFromStore(state) .catch((error) => - Logging.logException("Error running store tasks:", error) + Logging.logException("Error running store tasks:", error), ); app.listen(config.listenOn, async () => {