Add AtP ad, ad configuration.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="200" viewBox="0 0 135 52.9">
|
||||
<defs/>
|
||||
<defs>
|
||||
<linearGradient id="a">
|
||||
<stop stop-color="#f69c30"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b">
|
||||
<stop stop-color="#30b4f6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#b)" d="M13.2 25v4.63l1.66-.66 1.65.66V25z" paint-order="stroke fill markers" transform="matrix(3.67 0 0 3.67 -26.7 -91.5)"/>
|
||||
<path stroke-width=".13" d="M29.08 9.42l-.56.02h-1.63l-.57-.02-.55 1.82q-.24.06-.7.06-.44 0-.72-.1l-.06-.09 2.6-7.81q.43-.06.93-.06t.87.07l2.55 7.82q-.34.2-.82.2-.46 0-.67-.18-.2-.18-.34-.67zM26.7 8.2l.52-.01h1l.5.01-.36-1.18q-.4-1.33-.62-2.26h-.07q-.1.5-.51 1.97z" aria-label="A"/>
|
||||
<path fill="url(#a)" d="M17.5 25v4.63l1.66-.66 1.65.66V25z" paint-order="stroke fill markers" transform="matrix(3.67 0 0 3.67 -26.7 -91.5)"/>
|
||||
<path stroke-width=".13" d="M44.5 4.65l.01.87v5.8q-.34.05-.8.05-.43 0-.77-.05v-5.8l.01-.87-.7.02h-1.3q-.06-.3-.06-.66t.06-.65h5.58q.08.29.08.64 0 .34-.2.5t-.64.16h-.54l-.71-.01z" aria-label="T"/>
|
||||
<path fill="url(#b)" d="M26.2 25v4.63l1.65-.66 1.66.66V25z" paint-order="stroke fill markers" transform="matrix(3.67 0 0 3.67 -26.7 -91.5)"/>
|
||||
<path stroke-width=".13" d="M76.43 4.65v6.67q-.33.05-.78.05-.44 0-.78-.05v-5.8l.01-.87-.7.02h-1.3q-.06-.3-.06-.66t.06-.65h5.58q.08.29.08.64 0 .34-.2.5t-.64.16h-.54l-.71-.01z" aria-label="T"/>
|
||||
<path fill="url(#a)" d="M30.6 25v4.63l1.65-.66 1.66.66V25z" paint-order="stroke fill markers" transform="matrix(3.67 0 0 3.67 -26.7 -91.5)"/>
|
||||
<path stroke-width=".13" d="M93.3 6.49V3.29q.3-.06.76-.06.48 0 .8.06v7.96q-.33.06-.8.06-.45 0-.76-.06V7.8h-2.88l.02.86v2.6q-.3.05-.78.05t-.78-.06V3.3q.3-.06.78-.06t.78.06v2.34l-.02.86.63-.01h1.64z" aria-label="H"/>
|
||||
<path fill="url(#b)" d="M35 25v4.63l1.65-.66 1.66.66V25z" paint-order="stroke fill markers" transform="matrix(3.67 0 0 3.67 -26.7 -91.5)"/>
|
||||
<path stroke-width=".13" d="M106.52 9.42l-.01.73.81-.03h2.86q0 .38-.04.57-.13.75-1 .75h-3.27q-.42 0-.67-.25-.23-.25-.23-.68V3.54l.08-.09h4.85q.06.3.06.63t-.13.69h-3.33l.01.8v1.2l.76-.01h1.96q.1.27.1.64t-.1.64h-2.72z" aria-label="E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="200" viewBox="0 0 67.73 52.92"><defs/><g fill="none" stroke="#000" stroke-width="1.94"><path d="M0 1.32h67.73M0 21.17h67.73"/></g></svg>
|
||||
|
After Width: | Height: | Size: 196 B |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="200" viewBox="0 0 135 52.9">
|
||||
<defs/>
|
||||
<defs>
|
||||
<linearGradient id="a">
|
||||
<stop stop-color="#f69c30"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b">
|
||||
<stop stop-color="#30b4f6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#a)" d="M9.31 31.4v9.26l3.31-1.32 3.3 1.32V31.4z" paint-order="stroke fill markers" transform="matrix(3.67 0 0 3.67 -31.5 -96.5)"/>
|
||||
<path stroke-width=".26" d="M14.74 35.99H12.6v5.28q-.65.12-1.56.12t-1.56-.12V25.45l.16-.14 3.15-.07 1.95-.02q2.52 0 4 1.38 1.52 1.37 1.52 4 0 1.3-.4 2.3t-1.12 1.68T17 35.63q-1 .35-2.26.35zm-2.14-8.33v5.94l1.54-.02q.65 0 1.09-.07.47-.1.77-.3.33-.24.58-.68.24-.4.35-.88.12-.52.12-1.03 0-.53-.12-1.05-.11-.53-.4-.95t-.74-.68-1.16-.25h-1.2q-.48 0-.83-.03z" aria-label="P"/>
|
||||
<path fill="url(#b)" d="M16.5 31.4v9.26l3.3-1.32 3.31 1.32V31.4z" paint-order="stroke fill markers" transform="matrix(3.67 0 0 3.67 -31.5 -96.5)"/>
|
||||
<path stroke-width=".26" d="M43.73 37.82l-1.14.03h-3.26l-1.14-.03-1.1 3.64q-.48.12-1.4.12-.88 0-1.44-.21l-.11-.17 5.2-15.67q.87-.11 1.87-.11t1.75.14l5.1 15.67q-.68.4-1.63.4-.93 0-1.35-.36-.4-.35-.68-1.33zm-4.77-2.47l1.05-.02h2l.99.02-.7-2.38q-.8-2.65-1.26-4.51h-.14q-.2 1-1.02 3.93z" aria-label="A"/>
|
||||
<path fill="url(#a)" d="M23.7 31.4v9.26l3.3-1.32 3.32 1.32V31.4z" paint-order="stroke fill markers" transform="matrix(3.67 0 0 3.67 -31.5 -96.5)"/>
|
||||
<path stroke-width=".26" d="M72.73 30.11q0 .78-.24 1.47-.2.68-.6 1.24-.4.54-.93.95-.52.4-1.14.61.6.2 1 .72.42.52.72 1.33l.86 2.35q.21.59.47 1t.63.7q-.3.42-.89.68-.56.25-1.28.25-.7 0-1.12-.41t-.77-1.42l-.98-2.78q-.3-.81-.77-1.24-.47-.44-1.4-.44h-1.42v6.1q-.32.04-.72.06-.4.05-.86.05-.87 0-1.52-.12V25.4l.16-.14 3.2-.07 2.12-.02q2.52 0 4 1.26 1.49 1.26 1.49 3.66zm-7.86-2.53v5.13q1.22-.07 2.15-.07 2.49 0 2.49-2.51T67 27.6h-1.25q-.52 0-.89-.03z" aria-label="R"/>
|
||||
<path fill="url(#b)" d="M30.9 31.4v9.26l3.3-1.32 3.31 1.32V31.4z" paint-order="stroke fill markers" transform="matrix(3.67 0 0 3.67 -31.5 -96.5)"/>
|
||||
<path stroke-width=".26" d="M95.48 27.91l.03 1.73v11.6q-.68.11-1.59.11-.88 0-1.56-.11v-11.6l.02-1.73-1.42.03h-2.58q-.12-.59-.12-1.31t.12-1.3h11.15q.17.58.17 1.27 0 .68-.4 1t-1.28.33h-1.1l-1.42-.02z" aria-label="T"/>
|
||||
<path fill="url(#a)" d="M38.1 31.4v9.26l3.3-1.32 3.32 1.32V31.4z" paint-order="stroke fill markers" transform="matrix(3.67 0 0 3.67 -31.5 -96.5)"/>
|
||||
<path stroke-width=".26" d="M121.9 30.11l1.85-4.62q.53-.12 1.35-.12t1.47.21l.11.19-4.47 10.68v4.95q-.6.12-1.56.12t-1.56-.12v-5.06l-4.48-10.5q.98-.55 1.77-.55t1.17.3.65 1l1.47 3.59q.6 1.59 1 3.12h.14q.38-1.35 1.1-3.21z" aria-label="Y"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -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 )
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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." ]
|
||||
|
||||
@@ -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." ]
|
||||
|
||||
@@ -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." ]
|
||||
|
||||
@@ -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." ]
|
||||
|
||||
@@ -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." ]
|
||||
|
||||
@@ -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à." ]
|
||||
|
||||
@@ -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." ]
|
||||
|
||||
@@ -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." ]
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
|
||||
> div {
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-panel {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: [
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
+12
-6
@@ -29,6 +29,7 @@ export interface Config<D extends Duration> extends EnvironmentalConfig {
|
||||
storage: BaseStorage<D>;
|
||||
cache: BaseCache<D>;
|
||||
defaults: LobbyConfig.Defaults;
|
||||
adverts?: Adverts;
|
||||
}
|
||||
|
||||
export type Parsed = Config<ParsedDuration>;
|
||||
@@ -121,8 +122,13 @@ export type PostgreSQLCache = BasePostgreSQLCache<ParsedDuration>;
|
||||
const parseDuration = (unparsed: UnparsedDuration): ParsedDuration =>
|
||||
Moment.duration(unparsed).asMilliseconds();
|
||||
|
||||
interface Adverts {
|
||||
manyDecks?: boolean;
|
||||
atTheParty?: boolean;
|
||||
}
|
||||
|
||||
export const parseStorage = (
|
||||
storage: BaseStorage<UnparsedDuration>
|
||||
storage: BaseStorage<UnparsedDuration>,
|
||||
): BaseStorage<ParsedDuration> => ({
|
||||
...storage,
|
||||
abandonedTime: parseDuration(storage.abandonedTime),
|
||||
@@ -130,28 +136,28 @@ export const parseStorage = (
|
||||
});
|
||||
|
||||
export const parseCache = (
|
||||
cache: BaseCache<UnparsedDuration>
|
||||
cache: BaseCache<UnparsedDuration>,
|
||||
): BaseCache<ParsedDuration> => ({
|
||||
...cache,
|
||||
checkAfter: parseDuration(cache.checkAfter),
|
||||
});
|
||||
|
||||
export const parseTimeouts = (
|
||||
timeouts: Timeouts<UnparsedDuration>
|
||||
timeouts: Timeouts<UnparsedDuration>,
|
||||
): Timeouts<ParsedDuration> =>
|
||||
Util.mapObjectValues(timeouts, (key: string, value: UnparsedDuration) =>
|
||||
parseDuration(value)
|
||||
parseDuration(value),
|
||||
);
|
||||
|
||||
export const parseTasks = (
|
||||
tasks: Tasks<UnparsedDuration>
|
||||
tasks: Tasks<UnparsedDuration>,
|
||||
): Tasks<ParsedDuration> => ({
|
||||
...tasks,
|
||||
processTickFrequency: parseDuration(tasks.processTickFrequency),
|
||||
});
|
||||
|
||||
const parseManyDecks = (
|
||||
manyDecks: BaseManyDecks<UnparsedDuration>
|
||||
manyDecks: BaseManyDecks<UnparsedDuration>,
|
||||
): ManyDecks => ({
|
||||
...manyDecks,
|
||||
baseUrl: manyDecks.baseUrl.endsWith("/")
|
||||
|
||||
+19
-11
@@ -50,8 +50,8 @@ function getConfigFilePath(): string {
|
||||
async function main(): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
}
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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 () => {
|
||||
|
||||
Reference in New Issue
Block a user