diff options
-rw-r--r-- | public/style.css | 104 | ||||
-rw-r--r-- | server.js | 93 | ||||
-rw-r--r-- | tools/gencovers.sh | 9 | ||||
-rw-r--r-- | views/games_active.pug | 10 | ||||
-rw-r--r-- | views/games_finished.pug | 2 | ||||
-rw-r--r-- | views/games_public.pug | 14 | ||||
-rw-r--r-- | views/head.pug | 117 | ||||
-rw-r--r-- | views/info.pug | 15 |
8 files changed, 207 insertions, 157 deletions
diff --git a/public/style.css b/public/style.css index f4d677c..e7a87c8 100644 --- a/public/style.css +++ b/public/style.css @@ -37,7 +37,7 @@ article p, article dl, article ul { max-width: 50rem; } img.avatar { float: left; margin: 0 20px 5px 0; - box-shadow: 2px 2px 4px 0px rgba(0,0,0,.5); + box-shadow: 1px 2px 4px rgba(0,0,0,0.3); width: 80px; height: 80px; } @@ -49,30 +49,9 @@ div.logo { height: 200px; } div.logo img { - box-shadow: 2px 2px 4px 0px rgba(0,0,0,0.5); + box-shadow: 1px 2px 4px 0px rgba(0,0,0,0.3); } -table { - min-width: min(50rem,100%); - border-collapse: collapse; - border: 1px solid black; -} -thead, tfoot { - background-color: gainsboro; - border: 1px solid black; -} -th, td { - vertical-align: top; - text-align: left; - padding: 5px 10px; -} -tbody tr:nth-child(2n) { - background-color: whitesmoke; -} -table a:not(:hover) { text-decoration: none; color: black; } -td.command a { text-decoration: underline; color: blue; } -td.is_active { background-color: lemonchiffon; } - input[type="checkbox"] { margin-right: 7px; } @@ -107,10 +86,37 @@ button:disabled { .warning { color: brown; } p.warning::before { content: "\26a0"; } +/* TABLES */ + +table { + min-width: min(50rem,100%); + border-collapse: collapse; + border: 1px solid black; + box-shadow: 1px 2px 4px rgba(0,0,0,0.2); +} +thead, tfoot { + background-color: gainsboro; + border: 1px solid black; +} +th, td { + vertical-align: top; + text-align: left; + padding: 5px 10px; +} +tbody tr:nth-child(2n) { + background-color: whitesmoke; +} +table a:not(:hover) { text-decoration: none; color: black; } +td.command a { text-decoration: underline; color: blue; } +td.is_active { background-color: lemonchiffon; } + +/* FORUM AND MESSAGE POSTS */ + div.post { max-width: 50em; margin-top: 24px; border: 1px solid black; + box-shadow: 1px 2px 4px rgba(0,0,0,0.2); } div.post > div.head { display: flex; @@ -127,3 +133,55 @@ div.post + div.edit { max-width: 50em; margin-top: 5px; text-align:right; } article hr { max-width: 50rem; margin-right: auto; margin-left: 0; } article hr { border: none; border-top: 2px dotted brown; } article hr + p { font-style: italic; } + +/* GAME BOXES */ + +.game_list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(0, 400px)); + gap: 24px; +} +.game_item { + border: 1px solid black; + box-shadow: 1px 2px 4px rgba(0,0,0,0.2); +} +.game_head, .game_main { + display: flex; + justify-content: space-between; + padding: 4px 8px; +} +.game_head { + border-bottom: 1px solid black; +} +.game_info { + font-family: "Source Serif SmText", "Dingbats", "Noto Emoji", "Georgia", serif; + font-size: 14px; + line-height: 20px; +} +.game_item a:not(:hover) { text-decoration: none; color: black; } +.game_item a.command { text-decoration: underline; font-weight: bold } +.game_info .is_active { text-decoration: underline } +.game_info div { + text-indent: -20px; + padding-left: 20px; +} +.game_main img { + display: block; + height: 72px; + margin: 4px 0; + border: 1px solid black; +} +.game_head { background-color: gainsboro } +.game_main { background-color: whitesmoke } +.game_list.open .game_head { background-color: lightskyblue } +.game_list.open .game_main { background-color: aliceblue } +.game_list.replacement .game_head { background-color: thistle } +.game_list.replacement .game_main { background-color: lavenderblush } +.game_list.finished .game_head { background-color: silver } +.game_list.finished .game_main { background-color: gainsboro } +.game_item.your_turn .game_head { background-color: gold } +.game_item.your_turn .game_main { background-color: ivory } +.game_list a:hover { color: navy } +.game_list.open a:hover { color: blue } +.game_list.replacement a:hover { color: purple } +.game_item.your_turn a:hover { color: maroon } @@ -1009,14 +1009,6 @@ function is_active(game, players, user_id) { return false } -function is_shared(game, players, user_id) { - let n = 0 - for (let i = 0; i < players.length; ++i) - if (players[i].user_id === user_id) - ++n - return n > 1 -} - function is_solo(players) { return players.every(p => p.user_id === players[0].user_id) } @@ -1027,53 +1019,60 @@ function format_options(options) { if (k === false) return 'no' return k.replace(/_/g, " ").replace(/^\w/, c => c.toUpperCase()) } - if (!options || options === '{}') - return "None" - options = JSON.parse(options) return Object.entries(options||{}).map(([k,v]) => (v === true || v === 1) ? to_english(k) : `${to_english(k)}=${to_english(v)}`).join(", ") } function annotate_game(game, user_id) { let players = SQL_SELECT_PLAYERS_JOIN.all(game.game_id) + let options = JSON.parse(game.options) - game.human_options = format_options(game.options) - game.is_ready = RULES[game.title_id].ready(game.scenario, JSON.parse(game.options), players) - game.is_active = is_active(game, players, user_id) - game.is_shared = is_shared(game, players, user_id) - game.is_yours = false - game.your_role = null - - game.player_names = players.map(p => { - let name = p.name.replace(/ /g, '\xa0') - if (p.user_id > 0) - name = `<a href="/user/${p.name}">${name}</a>` - return name - }).join(", ") - - if (user_id > 0) { - for (let i = 0; i < players.length; ++i) { - if (players[i].user_id === user_id) { - game.is_yours = 1 - game.your_role = players[i].role - } - } - } + if (game.options === '{}') + game.human_options = "None" + else + game.human_options = format_options(options) - if (!game.is_shared) { - for (let i = 0; i < players.length; ++i) { - if (game.active === players[i].role) { - game.active = `<a href="/user/${players[i].name}">${players[i].name}</a>` - break - } + game.is_ready = RULES[game.title_id].ready(game.scenario, options, players) + + let your_count = 0 + let your_role = null + game.player_names = "" + for (let i = 0; i < players.length; ++i) { + let p = players[i] + + if (p.user_id === user_id) { + your_role = p.role + your_count++ } - if (game.status > 1) { - for (let i = 0; i < players.length; ++i) { - if (game.result === players[i].role) { - game.result = `<a href="/user/${players[i].name}">${players[i].name}</a>` - break - } - } + + let p_is_active = false + if (game.status === 0 && (game.owner_id === p.user_id)) + p_is_active = true + if (game.status === 1 && (game.active === p.role || game.active === "Both" || game.active === "All")) + p_is_active = true + + let link + if (p_is_active) { + link = `<span class="is_active"><a href="/user/${p.name}">${p.name}</a></span>` + if (p.user_id === user_id) + game.your_turn = true + } else { + link = `<a href="/user/${p.name}">${p.name}</a>` } + + if (game.player_names.length > 0) + game.player_names += ", " + game.player_names += link + + if (game.active === p.role) + game.active = link + if (game.result === p.role) + game.result = `${link} (${game.result})` + } + + if (your_count > 0) { + game.is_yours = true + if (your_count === 1) + game.your_role = your_role } game.ctime = human_date(game.ctime) @@ -2064,7 +2063,7 @@ app.get('/stats', function (req, res) { let stats = SQL_GAME_STATS.all() stats.forEach(row => { row.title_name = TITLES[row.title_id].title_name - row.options = format_options(row.options) + row.options = format_options(JSON.parse(row.options)) row.result_role = row.result_role.split(",") row.result_count = row.result_count.split(",").map(Number) }) diff --git a/tools/gencovers.sh b/tools/gencovers.sh index 911a050..a0ec53d 100644 --- a/tools/gencovers.sh +++ b/tools/gencovers.sh @@ -2,12 +2,17 @@ for F in public/*/cover.jpg do echo Processing: $F B=$(echo $F | sed s/.jpg//) + D=$(dirname $F) - convert -scale 200x200 $F $B.1x.png + convert -resize 200x200 $F $B.1x.png mozjpeg -q 95 -outfile $B.1x.jpg $B.1x.png rm -f $B.1x.png - convert -scale 400x400 $F $B.2x.png + convert -resize 400x400 $F $B.2x.png mozjpeg -q 90 -outfile $B.2x.jpg $B.2x.png rm -f $B.2x.png + + convert -resize 108x144! $F $D/thumbnail.png + mozjpeg -q 90 -outfile $D/thumbnail.jpg $D/thumbnail.png + rm -f $D/thumbnail.png done diff --git a/views/games_active.pug b/views/games_active.pug index 49d20d8..16d8ceb 100644 --- a/views/games_active.pug +++ b/views/games_active.pug @@ -13,24 +13,24 @@ html if ready_games.length > 0 h2 Ready to start - +gametable(0,ready_games) + +gamelist(ready_games, "ready") if open_games.length > 0 h2 Open - +gametable(0,open_games) + +gamelist(open_games, "open") if replacement_games.length > 0 h2 Need replacement - +gametable(0, replacement_games) + +gamelist(replacement_games, "replacement") if active_games.length > 0 h2 Active - +gametable(1,active_games) + +gamelist(active_games, "active") if finished_games.length > 0 h2 Recently finished - +gametable(2,finished_games) + +gamelist(finished_games, "finished") p <a href="/games/finished">All your finished games</a> diff --git a/views/games_finished.pug b/views/games_finished.pug index 59c0491..c6fa66d 100644 --- a/views/games_finished.pug +++ b/views/games_finished.pug @@ -10,6 +10,6 @@ html h1 Your finished games if finished_games.length > 0 - +gametable(2,finished_games) + +gamelist(finished_games, "finished") else p Nothing here. diff --git a/views/games_public.pug b/views/games_public.pug index 4af36bd..a681419 100644 --- a/views/games_public.pug +++ b/views/games_public.pug @@ -12,15 +12,21 @@ html h1 All Public Games h2 Open - +gametable(0, open_games) + if open_games.length > 0 + +gamelist(open_games, "open") + else + p No open games. if replacement_games.length > 0 h2 Need replacement - +gametable(0, replacement_games) + +gamelist(replacement_games, "replacement") if ready_games.length > 0 h2 Ready to start - +gametable(0, ready_games) + +gamelist(ready_games, "ready") h2 Active - +gametable(1, active_games) + if active_games.length > 0 + +gamelist(active_games, "active") + else + p No open games. diff --git a/views/head.pug b/views/head.pug index 0a1052a..911def4 100644 --- a/views/head.pug +++ b/views/head.pug @@ -34,75 +34,54 @@ mixin forumpost(row,show_buttons) | | #[a(href="/forum/reply/"+row.post_id) Reply] -mixin gametable(status,table,hide_title=0) - table - thead - tr - th ID - unless hide_title - th Title - th Scenario - th Players - th Description - case status - when 0 - th Date - when 1 - th Changed - th Turn - when 2 - th Finished - th Result - th - tbody - each row in table - tr - td: a(href="/join/"+row.game_id)= row.game_id - unless hide_title - td.w: a(href="/"+row.title_id)= row.title_name - td= row.scenario - if row.player_names - td!= row.player_names - else - td.error Nobody - td= row.description - if row.status === 0 - td.w= row.ctime - else - td.w= row.mtime - case status - when 1 - if (row.is_active) - td.is_active!= row.active - else - td!= row.active - when 2 - td!= row.result - td.command - if status === 0 - a(href="/join/"+row.game_id) Join - else if status === 1 - if row.is_yours - if row.is_shared - a(href="/join/"+row.game_id) Play +mixin gamelist(list,status,hide_title=0) + div.game_list(class=status) + each item in list + div + div.game_item(class=item.your_turn ? "your_turn" : "") + div.game_head + if item.scenario.length <= 2 + div + | <a href="/join/#{item.game_id}">#{item.game_id}</a> – + | <a href="/#{item.title_id}">#{item.title_name}</a> + | (#{item.scenario}) + else + div + | <a href="/join/#{item.game_id}">#{item.game_id}</a> – + | <a href="/#{item.title_id}">#{item.title_name}</a> + + if item.status === 0 || !item.is_ready + a(class="command" href=`/join/${item.game_id}`) Join + else + if item.is_yours + - let verb = (item.status > 1) ? "Review" : "Play" + if item.your_role + a(class="command" href=`/${item.title_id}/play:${item.game_id}:${item.your_role}`)= verb else - a(href=`/${row.title_id}/play:${row.game_id}:${row.your_role}`) Play + a(class="command" href="/join/"+item.game_id)= verb else - a(href=`/${row.title_id}/play:${row.game_id}`) View - else if status >= 2 - if row.is_yours - if row.is_shared - a(href="/join/"+row.game_id) View - else - a(href=`/${row.title_id}/play:${row.game_id}:${row.your_role}`) View + - let verb = (item.status > 1) ? "Review" : "View" + a(class="command" href=`/${item.title_id}/play:${item.game_id}`)= verb + + div.game_main + div.game_info + if item.description + i= item.description + if item.scenario !== "Standard" && item.scenario !== "Historical" && item.scenario.length > 2 + div Scenario: #{item.scenario} + unless item.human_options === "None" + div Options: #{item.human_options} + if item.player_names + div Players: !{item.player_names} else - a(href=`/${row.title_id}/replay:${row.game_id}`) View - else - tr - case status - when 0 - td(colspan=7-hide_title) No open games. - when 1 - td(colspan=8-hide_title) No active games. - when 2 - td(colspan=8-hide_title) No finished games. + div Players: <span class="error">Nobody</span> + case item.status + when 0 + div Created: #{item.ctime} + when 1 + div Changed: #{item.mtime} + when 2 + div Finished: #{item.mtime} + div Result: !{item.result} + unless hide_title + img(src=`/${item.title_id}/thumbnail.jpg`) diff --git a/views/info.pug b/views/info.pug index 785ee91..c7083ef 100644 --- a/views/info.pug +++ b/views/info.pug @@ -20,23 +20,26 @@ html p Read more about the game on #[a(href="https://boardgamegeek.com/boardgame/"+title.bgg) boardgamegeek.com]. h2 Open - +gametable(0,open_games,1) + if open_games.length > 0 + +gamelist(open_games, "open", true) + else + p No open games. if replacement_games.length > 0 h2 Need replacement - +gametable(0, replacement_games) + +gamelist(replacement_games, "replacement", true) p a(href="/create/"+title.title_id) Create a new game if ready_games.length > 0 h2 Ready to start - +gametable(0,ready_games,1) + +gamelist(ready_games, "ready", true) if active_games.length > 0 h2 Active - +gametable(1,active_games,1) + +gamelist(active_games, "active", true) if finished_games.length > 0 - h2 Finished - +gametable(2,finished_games,1) + h2 Recently finished + +gamelist(finished_games, "finished", true) |