Ground Control to Major Tom

This commit is contained in:
Gamebrary 2022-03-30 15:28:14 -07:00
parent 01c8e4ff99
commit 4914c08dfa
12 changed files with 326 additions and 196 deletions

View file

@ -1,120 +1,134 @@
<!-- TODO: add speedruns -->
<!-- TODO: use v-observe-visibility -->
<template lang="html">
<div class="game">
<aside>
<div class="position-relative">
<b-img
:src="gameCoverUrl"
:alt="game.name"
class="cursor-pointer game-cover"
fluid-grow
rounded
@click.stop="openGameCover"
<b-container fluid>
<b-row>
<b-col cols="3">
<div class="position-relative">
<b-img
:src="gameCoverUrl"
:alt="game.name"
class="cursor-pointer game-cover"
fluid-grow
rounded
@click.stop="openGameCover"
/>
<div class="game-info">
<game-rating :game="game" />
</div>
</div>
<b-skeleton-img
v-if="loading"
width="100px"
height="100px"
/>
<div class="game-info">
<game-rating :game="game" />
</div>
</div>
<template v-else>
<game-images ref="gameImages" :game="game" />
<!-- <game-videos :videos="game.videos" v-if="game.videos" /> -->
</template>
<b-skeleton-img
v-if="loading"
width="100px"
height="100px"
/>
<game-websites :game="game" class="d-none d-md-inline" />
<!-- <pre>{{ game.genres.map(({ id }) => id) }}</pre> -->
<!-- TODO: add bundles to game detail? -->
<!-- {{ game.bundles ? `Found in ${game.bundles.length} compilations.` : null }} -->
<!-- <timeline
v-if="twitterHandle"
:id="twitterHandle"
sourceType="profile"
>
loading...
</timeline> -->
</b-col>
<template v-else>
<game-images ref="gameImages" :game="game" />
<game-videos :videos="game.videos" v-if="game.videos" />
</template>
<b-col cols="7">
<h3 class="mb-2">
{{ game.name }}
<b-badge variant="success" v-if="steamGame && steamGame.metacritic">{{ steamGame.metacritic.score }}</b-badge>
</h3>
<small>
<pre class="text-dark">{{ steamGame }}</pre>
</small>
<small v-if="gog && gog.isPriceVisible">{{gog.price.symbol}}{{ gog.price.amount }}</small>
<small><pre class="text-dark">{{ gog }}</pre></small>
<!-- <pre class="small text-dark">{{ steamGame }}</pre> -->
<b-progress
v-if="progress"
:value="progress"
variant="success"
height="8px"
v-b-modal.progress
class="my-1 w-25"
/>
<game-websites :game="game" class="d-none d-md-inline" />
<!-- <pre>{{ game.genres.map(({ id }) => id) }}</pre> -->
<!-- TODO: add bundles to game detail? -->
<!-- {{ game.bundles ? `Found in ${game.bundles.length} compilations.` : null }} -->
<!-- <timeline
v-if="twitterHandle"
:id="twitterHandle"
sourceType="profile"
>
loading...
</timeline> -->
<!-- TODO: add icons for game modes:
single-player
multiplayer
co-operative
split-screen
massively-multiplayer-online-mmo
battle-royale -->
</aside>
<game-genres :game="game" />
<game-description :game="game" :steam-game="steamGame" />
<article>
<portal to="pageTitle">{{ game.name }}</portal>
<game-platforms :game="game" />
<!-- <game-news :game="game" /> -->
<game-details :game="game" />
<h3 class="mb-2">
{{ game.name }}
<b-badge variant="success" v-if="steamGame && steamGame.metacritic">{{ steamGame.metacritic.score }}</b-badge>
</h3>
<small v-if="gog && gog.isPriceVisible">{{gog.price.symbol}}{{ gog.price.amount }}</small>
<small><pre class="text-dark">{{ gog }}</pre></small>
<!-- <pre class="small text-dark">{{ steamGame }}</pre> -->
<b-progress
v-if="progress"
:value="progress"
variant="success"
height="8px"
v-b-modal.progress
class="my-1 w-25"
/>
<game-description :game="game" :steam-game="steamGame" />
<game-platforms :game="game" />
<!-- <game-news :game="game" /> -->
<game-details :game="game" />
<game-websites
:game="game"
grid
class="d-md-none"
/>
<game-websites
:game="game"
grid
class="d-md-none"
/>
<b-badge
v-for="({ games, hex, tagTextColor }, name) in tags"
v-if="games.includes(game.id)"
:key="name"
pill
tag="small"
class="mr-1 mb-2"
:style="`background-color: ${hex}; color: ${tagTextColor}`"
v-b-modal.tags
>
{{ name }}
</b-badge>
<b-badge
v-for="({ games, hex, tagTextColor }, name) in tags"
v-if="games.includes(game.id)"
:key="name"
pill
tag="small"
class="mr-1 mb-2"
:style="`background-color: ${hex}; color: ${tagTextColor}`"
v-b-modal.tags
>
{{ name }}
</b-badge>
<!-- <template v-if="!loading">
<b-skeleton v-for="n in 3" :key="n" />
</template> -->
<!-- <template v-if="!loading">
<b-skeleton v-for="n in 3" :key="n" />
</template> -->
<game-notes :game="game" />
<!-- <b-form-rating
v-if="rating"
:value="rating"
inline
readonly
variant="warning"
size="lg"
no-border
/>
<game-notes :game="game" />
<!-- <b-form-rating
v-if="rating"
:value="rating"
inline
readonly
variant="warning"
size="lg"
no-border
/>
<br /> -->
</b-col>
<br /> -->
</article>
<footer>
<similar-games
:game="game"
:loading="loading"
class="mb-2"
/>
</footer>
</div>
<b-col cols="2" sm="12" md="2">
<similar-games
:game="game"
:loading="loading"
class="mb-2"
/>
</b-col>
</b-row>
</b-container>
</template>
<script>
import GameNotes from '@/components/Game/GameNotes';
import GameGenres from '@/components/Game/GameGenres';
// import GameNews from '@/components/Game/GameNews';
import GameDetails from '@/components/Game/GameDetails';
import GamePlatforms from '@/components/Game/GamePlatforms';
@ -123,7 +137,7 @@ import GameDescription from '@/components/Game/GameDescription';
import SimilarGames from '@/components/Game/SimilarGames';
import GameWebsites from '@/components/Game/GameWebsites';
import GameImages from '@/components/Game/GameImages';
import GameVideos from '@/components/Game/GameVideos';
// import GameVideos from '@/components/Game/GameVideos';
import { mapGetters, mapState } from 'vuex';
// import { Timeline } from 'vue-tweet-embed'
@ -136,8 +150,9 @@ export default {
GameRating,
GameImages,
GameNotes,
GameGenres,
// GameNews,
GameVideos,
// GameVideos,
GameWebsites,
SimilarGames,
},
@ -208,39 +223,39 @@ export default {
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
.game {
width: calc(100% - .5rem);
display: grid;
grid-template-columns: 360px 2fr;
grid-gap: 1rem;
@media(max-width: 1280px) {
grid-template-columns: 360px 1fr;
}
@media(max-width: 1024px) {
grid-template-columns: 320px 1fr;
}
@media(max-width: 768px) {
width: 100%;
grid-template-columns: 100%;
}
}
.game-cover {
max-width: 100%;
// position: relative;
height: auto;
}
.game-info {
position: absolute;
bottom: 1rem;
right: 1rem;
}
footer {
grid-column: 1 / -1;
}
// .game {
// width: calc(100% - .5rem);
// display: grid;
// grid-template-columns: 360px 2fr;
// grid-gap: 1rem;
//
// @media(max-width: 1280px) {
// grid-template-columns: 360px 1fr;
// }
//
// @media(max-width: 1024px) {
// grid-template-columns: 320px 1fr;
// }
//
// @media(max-width: 768px) {
// width: 100%;
// grid-template-columns: 100%;
// }
// }
//
// .game-cover {
// max-width: 100%;
// // position: relative;
// height: auto;
// }
//
// .game-info {
// position: absolute;
// bottom: 1rem;
// right: 1rem;
// }
//
// footer {
// grid-column: 1 / -1;
// }
</style>

View file

@ -7,29 +7,13 @@
/>
</div>
<!-- TODO: get images from article and add them to screenshots -->
<template v-else>
<small class="text-muted">Source: {{ source }}</small>
<p>
<span
:class="{'break-spaces': source === 'IGDB' }"
v-html="description"
/>
<b-button v-b-modal.wikipediaArticle v-if="source === 'Wikipedia'">
<i class="fab fa-wikipedia-w" aria-hidden />
Read more
</b-button>
<!-- <template v-if="!trimmedDescription">
<div
v-html="gameDescription"
:class="{'break-spaces': source === 'IGDB' }"
/>
</template> -->
</p>
<p
:class="{'break-spaces': source === 'IGDB' }"
v-html="gameDescription"
/>
</template>
<b-modal

View file

@ -25,11 +25,6 @@
<span class="text-wrap">{{ platforms }}</span>
</div>
<div v-if="genres">
<strong>{{ $t('board.gameModal.genres') }}:</strong>
<span class="text-wrap">{{ genres }}</span>
</div>
<div v-if="gameModes">
<strong>{{ $t('board.gameModal.gameModes') }}:</strong>
<span class="text-wrap">{{ gameModes }}</span>
@ -78,12 +73,6 @@ export default {
computed: {
...mapGetters(['platformNames']),
genres() {
return this.game && this.game.genres
? this.game.genres.map(({ name }) => name).join(', ')
: null;
},
platforms() {
return this.game && this.game.platforms
? this.game.platforms.map(({ name }) => name).join(', ')

View file

@ -0,0 +1,41 @@
<template lang="html">
<div class="genres" v-if="gameGenres">
<b-button
v-for="genre in gameGenres"
:key="genre.id"
variant="transparent"
>
<i v-if="genre.icon" :class="`${genre.icon} p-0`" />
<span v-else>{{ genre.id }}</span>
<br />
<small>{{ genre.name }}</small>
</b-button>
<!-- <pre class="text-dark">{{ genres }}</pre> -->
<!-- <pre class="text-dark">{{ game.genres }}</pre> -->
</div>
</template>
<script>
import { mapState } from 'vuex';
import { GENRE_ICONS } from '@/constants';
export default {
computed: {
...mapState(['game']),
gameGenres() {
const gameGenres = this.game && this.game.genres
? this.game.genres
: null;
return gameGenres.map(genre => ({
...genre,
icon: GENRE_ICONS[genre.id] || null,
}));
},
},
};
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
</style>

View file

@ -1,18 +1,27 @@
<template lang="html">
<div class="games" v-if="similarGames.length">
<!-- <pre>{{ similarGameIds }}</pre> -->
<!-- TODO: limit to fewer games, increase game cover, add modal to view all -->
<!-- You may also like: -->
<div v-if="similarGames.length">
You may also like:
<!-- TODO: use array map instead -->
<b-img
<b-card
v-for="game in similarGames"
v-if="game.cover"
:key="game.id"
:src="getCoverUrl(game.cover)"
class="rounded mr-2 mb-2 cursor-pointer"
@click="openGame(game)"
/>
no-body
class="overflow-hidden mb-2"
>
<b-row no-gutters>
<b-col md="6">
<b-card-img
:src="getCoverUrl(game.cover)"
class="rounded m-2"
@click="openGame(game)"
/>
<!-- <b-card-img src="https://picsum.photos/400/400/?image=20" alt="Image" class="rounded-0"></b-card-img> -->
</b-col>
<b-col md="6">
<div class="p-2">{{ game.name }}</div>
</b-col>
</b-row>
</b-card>
</div>
</template>
@ -84,7 +93,7 @@ export default {
this.similarGames = this.similarGameIds ?
this.similarGameIds
.filter(game => this.games && this.games[game])
.filter(game => this.games && this.games[game] && this.games[game].cover)
.map(game => this.games && this.games[game])
: [];
},
@ -93,11 +102,4 @@ export default {
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
.games {
display: flex;
flex-wrap: nowrap;
overflow-x: auto;
align-items: center;
width: 90vw;
}
</style>

View file

@ -1,15 +1,15 @@
<template lang="html">
<b-button
title="Dashboard"
size="sm"
class="home-button"
variant="transparent"
@click="handleLogoClick"
>
<!-- <img
src="/static/gamebrary-logo-dark.png"
width="32"
/> -->
<i class="fas fa-home" />
<img
class="py-2"
src="/static/logo.png"
width="24"
/>
<!-- TODO: move back button to here when viewing a game that's part of a board -->
</b-button>
</template>
@ -40,7 +40,4 @@ export default {
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
.home-button {
width: 50px;
}
</style>

View file

@ -47,6 +47,105 @@ export const LINKS_CATEGORIES = {
18: { id: 'discord', svg: true },
};
export const GENRE_ICONS = {
2: 'fa-solid fa-computer-mouse',
4: 'fa-solid fa-hand-fist',
5: 'fa-solid fa-person-rifle',
7: 'fa-solid fa-music',
8: 'fa-solid fa-person-running',
9: 'fa-solid fa-puzzle-piece',
10: 'fa-solid fa-flag-checkered',
12: 'fa-solid fa-shield',
13: 'fa-solid fa-cube',
14: 'fa-solid fa-futbol',
15: 'fa-solid fa-chess',
16: 'fa-solid fa-arrows-turn-to-dots',
24: 'fa-solid fa-user-ninja',
26: 'fa-solid fa-person-circle-question',
30: 'fa-solid fa-table-tennis-paddle-ball',
31: 'fa-solid fa-mountain-sun',
32: 'fa-solid fa-gamepad',
33: 'fa-solid fa-asterisk',
35: 'fa-solid fa-chess-board',
34: 'fa-solid fa-comments',
36: 'fa-solid fa-network-wired',
};
// 1.4 Beat 'em up games
// 3.4 Interactive movie
// 3.5 Real-time 3D adventures
// 4 Puzzle
// 4.1 Breakout clone game
// 4.2 Logical game
// 4.2.1 Physics game
// 4.2.2 Coding game
// 4.3 Trial-and-error / exploration
// 4.4 Hidden object game
// 4.5 Reveal the picture game
// 4.6 Tile-matching game
// 4.7 Traditional puzzle game
// 5 Role-playing
// 5.1 Action RPG
// 5.2 MMORPG
// 5.3 Roguelikes
// 5.4 Tactical RPG
// 5.5 Sandbox RPG
// 5.6 First-person party-based RPG
// 5.7 JRPG
// 5.8 Monster Tamer
// 6 Simulation
// 6.1 Construction and management simulation
// 6.2 Life simulation
// 6.3 Vehicle simulation
// 7 Strategy
// 7.1 4X game
// 7.2 Artillery game
// 7.3 Auto battler (Auto chess)
// 7.4 Multiplayer online battle arena (MOBA)
// 7.5 Real-time strategy (RTS)
// 7.6 Real-time tactics (RTT)
// 7.7 Tower defense
// 7.8 Turn-based strategy (TBS)
// 7.9 Turn-based tactics (TBT)
// 7.10 Wargame
// 7.11 Grand strategy wargame
// 8 Sports
// 8.1 Racing
// 8.2 Sports game
// 8.3 Competitive
// 8.4 Sports-based fighting
// 9 MMO
// 10 Other notable genres
// 10.1 Board game or card game
// 10.2 Casino game
// 10.3 Casual games
// 10.4 Digital collectible card game
// 10.5 Gacha game
// 10.6 Horror game
// 10.7 Idle game
// 10.8 Logic game
// 10.9 Party game
// 10.10 Photography game
// 10.11 Programming game
// 10.12 Social deduction game
// 10.13 Trivia game
// 10.14 Typing game
// 11 Video game genres by purpose
// 11.1 Advergame
// 11.2 Art game
// 11.3 Casual game
// 11.4 Christian game
// 11.5 Educational game
// 11.6 Esports
// 11.7 Exergame
// 11.8 Personalized game
// 11.9 Serious game
// 12 Sandbox / open world games
// 12.1 Sandbox
// 12.2 Creative
// 12.3 Open world
// 13
export const KEYBOARD_SHORTCUTS = {
'MODAL_keyboard-shortcuts': ['shift', '?'],
'MODAL_create-board': ['shift', 'c'],

View file

@ -52,6 +52,8 @@ export default {
mounted() {
this.loadGame();
this.$store.dispatch('IGDB', { path: 'game_modes', data: 'fields *;' });
},
methods: {

View file

@ -1,4 +1,5 @@
<!-- TODO: Mix media from other sources (e.g. instagram, wikipedia, youtube, twitter, etc... ) -->
<!-- TODO: get images from article and add them to media page -->
<template lang="html">
<b-container fluid class="p-2">
<b-form-row>

View file

@ -68,7 +68,7 @@ export default {
LOAD_STEAM_GAME(context, steamGameId) {
return new Promise((resolve, reject) => {
// axios.get(`${API_BASE}/steam-game?gameId=${steamGameId}`)
axios.get(`http://localhost:5001/gamebrary-8c736/us-central1/steam-game?gameId=${steamGameId}`)
axios.get(`${API_BASE}/steam-game?gameId=${steamGameId}`)
.then(({ data }) => {
const steamGameData = data[steamGameId];
@ -478,7 +478,7 @@ export default {
LOAD_GOG_GAME(context, search) {
return new Promise((resolve, reject) => {
axios.get(`http://localhost:5001/gamebrary-8c736/us-central1/gog?search=${search}`)
axios.get(`${API_BASE}/gog?search=${search}`)
.then(({ data }) => {
const game = data.totalGamesFound
? data.products[0]

View file

@ -223,7 +223,7 @@ $container-max-widths: (
// Set the number of columns and specify the width of the gutters.
$grid-columns: 12 !default;
$grid-gutter-width: 30px !default;
$grid-gutter-width: 8px !default;
$grid-row-columns: 6 !default;

BIN
static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB