Game page improvements

This commit is contained in:
Gamebrary 2022-12-03 00:26:48 -07:00
parent 4cb8a62ae4
commit f9e65418b7
11 changed files with 184 additions and 156 deletions

BIN
public/no-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -106,6 +106,7 @@ export default {
border-radius: .25rem; border-radius: .25rem;
} }
} }
.mw-empty-elt { .mw-empty-elt {
display: none; display: none;
} }

View file

@ -1,16 +1,9 @@
<template> <template>
<b-card <div>
:img-src="gameHeaderImage"
img-alt="Image"
img-top
class="small mt-3"
bg-variant="light"
text-variant="dark"
>
<game-progress /> <game-progress />
<div v-if="gameGenres" class="pr-2 pb-2"> <div v-if="gameGenres">
<strong>Genres: </strong> <h4 class="mt-4">Genres: </h4>
<b-link <b-link
v-for="(genre, index) in gameGenres" v-for="(genre, index) in gameGenres"
@ -21,8 +14,8 @@
</b-link> </b-link>
</div> </div>
<div v-if="gameModes" class="pr-2 pb-2"> <div v-if="gameModes">
<strong>{{ $t('board.gameModal.gameModes') }}: </strong> <h4 class="mt-4">{{ $t('board.gameModal.gameModes') }}: </h4>
<b-link <b-link
v-for="(gameMode, index) in gameModes" v-for="(gameMode, index) in gameModes"
@ -33,8 +26,8 @@
</b-link> </b-link>
</div> </div>
<div v-if="playerPerspectives" class="pr-2 pb-2"> <div v-if="playerPerspectives">
<strong>{{ $t('board.gameModal.perspective') }}: </strong> <h4 class="mt-4">{{ $t('board.gameModal.perspective') }}: </h4>
<b-link <b-link
v-for="({ id, name }, index) in playerPerspectives" v-for="({ id, name }, index) in playerPerspectives"
@ -46,8 +39,8 @@
</div> </div>
<!-- TODO: restore release dates --> <!-- TODO: restore release dates -->
<!-- <div class="pr-2 pb-2"> <!-- <div>
<strong>{{ $t('board.gameModal.releaseDate') }}</strong> <h4 class="mt-4">{{ $t('board.gameModal.releaseDate') }}</h4>
<ol v-if="releaseDates" class="list-unstyled mb-0"> <ol v-if="releaseDates" class="list-unstyled mb-0">
<li <li
v-for="{ id, platform, date } in releaseDates" v-for="{ id, platform, date } in releaseDates"
@ -62,8 +55,8 @@
</div> </div>
</div> --> </div> -->
<div v-if="user" class="pr-2 pb-2"> <div v-if="user">
<strong>Found in: </strong> <h4 class="mt-4">Found in: </h4>
<b-link v-if="!boardsWithGame.length" v-b-modal.addRemoveGameModal> <b-link v-if="!boardsWithGame.length" v-b-modal.addRemoveGameModal>
Add to list Add to list
@ -81,15 +74,13 @@
<add-remove-game /> <add-remove-game />
</div> </div>
<div v-if="user" class="pr-2 pb-2"> <div v-if="user">
<strong>Tags: </strong> <h4 class="mt-4">Tags: </h4>
<b-link v-if="!tagsApplied.length" v-b-modal.gameTagsModal> <b-link v-if="!tagsApplied.length" v-b-modal.gameTagsModal>
Add tag Add tag
</b-link> </b-link>
<br />
<b-button <b-button
v-for="({ bgColor, textColor, name, index }) in tagsApplied" v-for="({ bgColor, textColor, name, index }) in tagsApplied"
:key="index" :key="index"
@ -107,8 +98,7 @@
<game-tags-modal /> <game-tags-modal />
</div> </div>
<strong>Other links</strong> <h4 class="mt-4">Other links</h4>
<br>
<b-button <b-button
v-for="{ url, id, icon, svg } in gameLinks" v-for="{ url, id, icon, svg } in gameLinks"
@ -134,16 +124,13 @@
/> />
</b-button> </b-button>
<b-button <b-link
v-if="officialWebsiteUrl" v-if="officialWebsiteUrl"
class="mt-3"
v-b-modal.officialWebsite v-b-modal.officialWebsite
block
:title="officialWebsiteUrl" :title="officialWebsiteUrl"
variant="secondary"
> >
Official website Official website
</b-button> </b-link>
<b-modal <b-modal
id="officialWebsite" id="officialWebsite"
@ -171,7 +158,7 @@
/> />
</b-modal> </b-modal>
</b-card> </div>
</template> </template>
<script> <script>
@ -202,9 +189,10 @@ export default {
return this.boards?.filter(({ lists }) => lists?.some(({ games }) => games?.includes(this.game?.id))) || []; return this.boards?.filter(({ lists }) => lists?.some(({ games }) => games?.includes(this.game?.id))) || [];
}, },
gameHeaderImage() { // TODO: find a good use for game header image
return this.game?.steam?.header_image; // gameHeaderImage() {
}, // return this.game?.steam?.header_image;
// },
officialWebsiteUrl() { officialWebsiteUrl() {
return this.gameLinks?.find(({ id }) => id === 'official')?.url; return this.gameLinks?.find(({ id }) => id === 'official')?.url;

View file

@ -1,11 +1,13 @@
<template lang="html"> <template lang="html">
<div class="mt-3"> <div class="mt-3">
<b-row no-gutters> <b-form-row>
<div <b-col
v-for="({ imageUrl, isVideo, isCover }, index) in gameMedia" v-for="({ imageUrl, isVideo, isCover }, index) in gameMedia"
cols="6"
sm="4"
:key="index" :key="index"
> >
<div class="mr-2 align-items-center text-center mb-2 rounded cursor-pointer position-relative"> <div class="align-items-center text-center mb-2 rounded cursor-pointer">
<i <i
v-if="isVideo" v-if="isVideo"
class="fa-solid fa-play video-indicator position-absolute text-white" class="fa-solid fa-play video-indicator position-absolute text-white"
@ -21,12 +23,14 @@
<b-img <b-img
:src="imageUrl" :src="imageUrl"
rounded rounded
style="height: 180px" class="mb-2"
fluid
style="max-height: 120px;"
@click="viewMedia(index)" @click="viewMedia(index)"
/> />
</div> </div>
</div> </b-col>
</b-row> </b-form-row>
<b-modal <b-modal
id="mediaModal" id="mediaModal"

View file

@ -1,7 +1,7 @@
<!-- TODO: hide progress if game is not yet released --> <!-- TODO: hide progress if game is not yet released -->
<template lang="html"> <template lang="html">
<div class="pr-2 pb-2"> <div>
<strong>Progress: </strong> <h4 class="mt-4">Progress: </h4>
<b-link @click="editing = true">{{ progress }}%</b-link> <b-link @click="editing = true">{{ progress }}%</b-link>
<b-form v-if="editing"> <b-form v-if="editing">
@ -96,6 +96,3 @@ export default {
}, },
}; };
</script> </script>
<style lang="scss" rel="stylesheet/scss" scoped>
</style>

View file

@ -1,6 +1,6 @@
<template lang="html"> <template lang="html">
<section v-if="similarGames.length" class="my-5 bg-light p-3"> <section v-if="similarGames.length" :class="['my-5 px-3 py-5', hasWallpaper ? '' : 'bg-light']">
<h4 class="text-center mb-3">You may also like</h4> <h4 :class="['text-center mb-3', { 'text-outlined': hasWallpaper }]">You may also like</h4>
<div class="similar-games"> <div class="similar-games">
<router-link <router-link
@ -27,6 +27,10 @@ import { getImageUrl } from '@/utils';
export default { export default {
getImageUrl, getImageUrl,
props: {
hasWallpaper: Boolean,
},
data() { data() {
return { return {
similarGames: [], similarGames: [],

View file

@ -1,12 +1,19 @@
<template lang="html"> <template lang="html">
<b-card <b-card
v-if="game" v-if="game"
:img-src="$options.getImageUrl(game)" :img-src="gameCoverUrl"
:img-alt="game.name" :img-alt="game.name"
:class="['mb-3 cursor-pointer', { 'border-selected': selected }]" :class="['mb-3 cursor-pointer', { 'border-selected': selected }]"
overlay overlay
@click="handleClick" @click="handleClick"
> >
<strong
v-if="noImage"
class="text-center pb-5 d-flex justify-content-center"
>
{{ game.name }}
</strong>
<div v-if="selected" class="selected-indicator rounded bg-success text-white"> <div v-if="selected" class="selected-indicator rounded bg-success text-white">
<i class="fa fa-check" /> <i class="fa fa-check" />
</div> </div>
@ -16,11 +23,10 @@
<script> <script>
import { getImageUrl } from '@/utils'; import { getImageUrl } from '@/utils';
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { NO_IMAGE_PATH } from '@/constants';
import slugify from 'slugify' import slugify from 'slugify'
export default { export default {
getImageUrl,
props: { props: {
game: { game: {
type: Object, type: Object,
@ -37,10 +43,18 @@ export default {
return this.boards.find(({ id }) => id === boardId); return this.boards.find(({ id }) => id === boardId);
}, },
gameCoverUrl() {
return getImageUrl(this.game);
},
selected() { selected() {
return this.selectedList?.games?.includes(this.game.id) return this.selectedList?.games?.includes(this.game.id)
}, },
noImage() {
return NO_IMAGE_PATH === this.gameCoverUrl;
},
selectedList() { selectedList() {
const { listIndex } = this.$route.query; const { listIndex } = this.$route.query;

View file

@ -74,6 +74,7 @@ export default {
}, },
isBoardPage() { isBoardPage() {
// TODO: do show blurred background in game page
return ['board', 'game'].includes(this.$route.name); return ['board', 'game'].includes(this.$route.name);
}, },
}, },

View file

@ -15,6 +15,7 @@ export const MAX_PROFILE_LENGTH = 20;
export const STEAM_CATEGORY_ID = 13; export const STEAM_CATEGORY_ID = 13;
export const GOG_CATEGORY_ID = 17; export const GOG_CATEGORY_ID = 17;
export const TWITTER_CATEGORY_ID = 5; export const TWITTER_CATEGORY_ID = 5;
export const NO_IMAGE_PATH = '/no-image.png';
export const LIST_VIEW_SINGLE = 'single'; export const LIST_VIEW_SINGLE = 'single';
export const LIST_VIEW_COVERS = 'covers'; export const LIST_VIEW_COVERS = 'covers';

View file

@ -16,7 +16,13 @@
<b-spinner v-if="loading" class="spinner-centered" /> <b-spinner v-if="loading" class="spinner-centered" />
<template v-else-if="game"> <template v-else-if="game">
<div v-if="backdrop" class="backdrop" :style="`background-image: url('${backdrop.url}')`" /> <div
v-if="backdrop"
class="backdrop"
:style="`background-image: url('${backdrop.url}')`"
v-b-modal.mediaModal
/>
<b-container> <b-container>
<portal to="pageTitle"> <portal to="pageTitle">
@ -99,123 +105,135 @@
<b-col <b-col
cols="12" cols="12"
md="8" md="8"
xl="9" lg="5"
xl="6"
:class="['pt-3', darkTheme || hasWallpaper ? 'text-light' : '']"
> >
<article :class="darkTheme || hasWallpaper ? 'text-light' : ''"> <div class="d-flex justify-content-between" v-b-visible="visibleHandler">
<h2 :class="{ 'mt-3': backdrop }">{{ game.name }}</h2>
</div>
<div class="d-flex justify-content-between" v-b-visible="visibleHandler"> <game-description />
<h2 :class="{ 'mt-3': backdrop }">{{ game.name }}</h2>
</div>
<game-description /> <b-alert
v-if="note"
v-html="note"
show
class="cursor-pointer mt-3"
variant="warning"
@click.native="$router.push({ name: 'game.notes', params: { id: game.id, slug: game.slug } })"
/>
<div v-if="gameDevelopers.length" class="mt-3"> <!-- <b-link v-if="!boardsWithGame.length" v-b-modal.addRemoveGameModal>
<h4>Developed by:</h4> Add to list
<b-link </b-link> -->
v-for="developer in gameDevelopers"
:key="developer.id"
:to="{ name: 'company', params: { id: developer.id }}"
>
<b-img
v-if="developer.logo"
:src="$options.getImageUrl(developer)"
:alt="developer.name"
width="120"
/>
<span v-else>{{ developer.name }}</span> <game-ratings />
</b-link>
</div>
<div v-if="gamePublishers.length" class="mt-3">
<h4>Published by:</h4>
<b-link
v-for="publisher in gamePublishers"
:key="publisher.id"
:to="{ name: 'company', params: { id: publisher.id }}"
>
<!-- TODO: use publisher.logo.alpha_channel to style logo -->
<b-img
v-if="publisher.logo"
:src="$options.getImageUrl(publisher)"
:alt="publisher.name"
width="120"
/>
<span v-else>{{ publisher.name }}</span>
</b-link>
</div>
<b-alert
v-if="note"
v-html="note"
show
class="cursor-pointer mt-3"
variant="warning"
@click.native="$router.push({ name: 'game.notes', params: { id: game.id, slug: game.slug } })"
/>
<div class="mt-3" v-if="alternativeNames.length">
<p>Alternative names:</p>
<div
class="mb-1 small"
variant="light"
v-for="{ comment, id, name, imgUrl } in alternativeNames"
:key="id"
>
<b-avatar
v-b-tooltip.hover
:title="comment || null"
size="sm"
class="mr-1"
:src="imgUrl"
/>
{{ name }}
</div>
</div>
<div v-if="gamePlatforms">
<h4>Available for:</h4>
<b-link
v-for="platform in gamePlatforms"
:key="platform.id"
:to="{ name: 'search', query: { platforms: platform.id }}"
>
<b-img
v-if="platform.platform_logo"
:src="$options.getImageUrl(platform.platform_logo)"
:alt="platform.name"
thumbnail
width="100"
/>
<div v-else>
{{ platform.name }}
</div>
</b-link>
</div>
<!-- <b-link v-if="!boardsWithGame.length" v-b-modal.addRemoveGameModal>
Add to list
</b-link> -->
<game-ratings />
<game-details />
</article>
<small <small
v-if="legalNotice" v-if="legalNotice"
class="text-muted" class="text-muted"
v-html="legalNotice" v-html="legalNotice"
/> />
<game-media />
</b-col>
<b-col
cols="12"
md="12"
lg="3"
xl="3"
class="pt-3"
>
<game-details />
<div v-if="gamePublishers.length" class="d-flex justify-content-center flex-column">
<h4 class="mt-4">Published by:</h4>
<b-link
v-for="publisher in gamePublishers"
:key="publisher.id"
:to="{ name: 'company', params: { id: publisher.id }}"
>
<!-- TODO: use publisher.logo.alpha_channel to style logo -->
<b-img
v-if="publisher.logo"
:src="$options.getImageUrl(publisher)"
:alt="publisher.name"
width="120"
/>
<span v-else>{{ publisher.name }}</span>
</b-link>
</div>
<div v-if="gameDevelopers.length">
<h4 class="mt-4">Developed by:</h4>
<b-link
v-for="developer in gameDevelopers"
:key="developer.id"
:to="{ name: 'company', params: { id: developer.id }}"
>
<b-img
v-if="developer.logo"
:src="$options.getImageUrl(developer)"
:alt="developer.name"
width="120"
/>
<span v-else>{{ developer.name }}</span>
</b-link>
</div>
<div v-if="alternativeNames.length">
<h4 class="mt-4">Alternative names:</h4>
<div
class="mb-1"
variant="light"
v-for="{ comment, id, name, imgUrl } in alternativeNames"
:key="id"
>
<b-avatar
v-b-tooltip.hover
:title="comment || null"
size="sm"
class="mr-1"
:src="imgUrl"
/>
{{ name }}
</div>
</div>
<div v-if="gamePlatforms">
<h4 class="mt-4">Available for:</h4>
<b-button
v-for="platform in gamePlatforms"
:key="platform.id"
variant="transparent"
class="pb-0"
:to="{ name: 'search', query: { platforms: platform.id }}"
>
<b-img
v-if="platform.platform_logo"
:src="$options.getImageUrl(platform.platform_logo)"
:alt="platform.name"
class="mr-2 mb-2"
width="100"
/>
<b-avatar size="100" rounded v-else>
<small>{{ platform.name }}</small>
</b-avatar>
</b-button>
</div>
</b-col> </b-col>
</b-row> </b-row>
<game-media />
<section v-if="boardsWithGame.length" class="mt-3"> <section v-if="boardsWithGame.length" class="mt-3">
<strong :class="{ 'text-outlined': hasWallpaper }">Found in: </strong> <strong :class="{ 'text-outlined': hasWallpaper }">Found in: </strong>
@ -246,7 +264,7 @@
</timeline> --> </timeline> -->
</b-container> </b-container>
<similar-games /> <similar-games :has-wallpaper="hasWallpaper" />
</template> </template>
<div class="pt-5" v-else> <div class="pt-5" v-else>

View file

@ -1,4 +1,4 @@
import { THUMBNAIL_PREFIX, IMAGE_SIZE_COVER_BIG } from '@/constants'; import { THUMBNAIL_PREFIX, IMAGE_SIZE_COVER_BIG, NO_IMAGE_PATH } from '@/constants';
export const bytesToSize = (bytes) => { export const bytesToSize = (bytes) => {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
@ -15,7 +15,7 @@ export const getImageUrl = (item, size = IMAGE_SIZE_COVER_BIG) => {
return imageId return imageId
? `https://images.igdb.com/igdb/image/upload/${size}/${imageId}.png` ? `https://images.igdb.com/igdb/image/upload/${size}/${imageId}.png`
: '/placeholder.gif'; : NO_IMAGE_PATH;
}; };
export const getFileExtension = (fileName) => { export const getFileExtension = (fileName) => {