mirror of
https://github.com/romancm/gamebrary
synced 2024-11-10 05:34:15 +00:00
Game page improvements
This commit is contained in:
parent
4cb8a62ae4
commit
f9e65418b7
11 changed files with 184 additions and 156 deletions
BIN
public/no-image.png
Normal file
BIN
public/no-image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4 KiB |
|
@ -106,6 +106,7 @@ export default {
|
|||
border-radius: .25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.mw-empty-elt {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
<template>
|
||||
<b-card
|
||||
:img-src="gameHeaderImage"
|
||||
img-alt="Image"
|
||||
img-top
|
||||
class="small mt-3"
|
||||
bg-variant="light"
|
||||
text-variant="dark"
|
||||
>
|
||||
<div>
|
||||
<game-progress />
|
||||
|
||||
<div v-if="gameGenres" class="pr-2 pb-2">
|
||||
<strong>Genres: </strong>
|
||||
<div v-if="gameGenres">
|
||||
<h4 class="mt-4">Genres: </h4>
|
||||
|
||||
<b-link
|
||||
v-for="(genre, index) in gameGenres"
|
||||
|
@ -21,8 +14,8 @@
|
|||
</b-link>
|
||||
</div>
|
||||
|
||||
<div v-if="gameModes" class="pr-2 pb-2">
|
||||
<strong>{{ $t('board.gameModal.gameModes') }}: </strong>
|
||||
<div v-if="gameModes">
|
||||
<h4 class="mt-4">{{ $t('board.gameModal.gameModes') }}: </h4>
|
||||
|
||||
<b-link
|
||||
v-for="(gameMode, index) in gameModes"
|
||||
|
@ -33,8 +26,8 @@
|
|||
</b-link>
|
||||
</div>
|
||||
|
||||
<div v-if="playerPerspectives" class="pr-2 pb-2">
|
||||
<strong>{{ $t('board.gameModal.perspective') }}: </strong>
|
||||
<div v-if="playerPerspectives">
|
||||
<h4 class="mt-4">{{ $t('board.gameModal.perspective') }}: </h4>
|
||||
|
||||
<b-link
|
||||
v-for="({ id, name }, index) in playerPerspectives"
|
||||
|
@ -46,8 +39,8 @@
|
|||
</div>
|
||||
|
||||
<!-- TODO: restore release dates -->
|
||||
<!-- <div class="pr-2 pb-2">
|
||||
<strong>{{ $t('board.gameModal.releaseDate') }}</strong>
|
||||
<!-- <div>
|
||||
<h4 class="mt-4">{{ $t('board.gameModal.releaseDate') }}</h4>
|
||||
<ol v-if="releaseDates" class="list-unstyled mb-0">
|
||||
<li
|
||||
v-for="{ id, platform, date } in releaseDates"
|
||||
|
@ -62,8 +55,8 @@
|
|||
</div>
|
||||
</div> -->
|
||||
|
||||
<div v-if="user" class="pr-2 pb-2">
|
||||
<strong>Found in: </strong>
|
||||
<div v-if="user">
|
||||
<h4 class="mt-4">Found in: </h4>
|
||||
|
||||
<b-link v-if="!boardsWithGame.length" v-b-modal.addRemoveGameModal>
|
||||
Add to list
|
||||
|
@ -81,15 +74,13 @@
|
|||
<add-remove-game />
|
||||
</div>
|
||||
|
||||
<div v-if="user" class="pr-2 pb-2">
|
||||
<strong>Tags: </strong>
|
||||
<div v-if="user">
|
||||
<h4 class="mt-4">Tags: </h4>
|
||||
|
||||
<b-link v-if="!tagsApplied.length" v-b-modal.gameTagsModal>
|
||||
Add tag
|
||||
</b-link>
|
||||
|
||||
<br />
|
||||
|
||||
<b-button
|
||||
v-for="({ bgColor, textColor, name, index }) in tagsApplied"
|
||||
:key="index"
|
||||
|
@ -107,8 +98,7 @@
|
|||
<game-tags-modal />
|
||||
</div>
|
||||
|
||||
<strong>Other links</strong>
|
||||
<br>
|
||||
<h4 class="mt-4">Other links</h4>
|
||||
|
||||
<b-button
|
||||
v-for="{ url, id, icon, svg } in gameLinks"
|
||||
|
@ -134,16 +124,13 @@
|
|||
/>
|
||||
</b-button>
|
||||
|
||||
<b-button
|
||||
<b-link
|
||||
v-if="officialWebsiteUrl"
|
||||
class="mt-3"
|
||||
v-b-modal.officialWebsite
|
||||
block
|
||||
:title="officialWebsiteUrl"
|
||||
variant="secondary"
|
||||
>
|
||||
Official website
|
||||
</b-button>
|
||||
</b-link>
|
||||
|
||||
<b-modal
|
||||
id="officialWebsite"
|
||||
|
@ -171,7 +158,7 @@
|
|||
/>
|
||||
</b-modal>
|
||||
|
||||
</b-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -202,9 +189,10 @@ export default {
|
|||
return this.boards?.filter(({ lists }) => lists?.some(({ games }) => games?.includes(this.game?.id))) || [];
|
||||
},
|
||||
|
||||
gameHeaderImage() {
|
||||
return this.game?.steam?.header_image;
|
||||
},
|
||||
// TODO: find a good use for game header image
|
||||
// gameHeaderImage() {
|
||||
// return this.game?.steam?.header_image;
|
||||
// },
|
||||
|
||||
officialWebsiteUrl() {
|
||||
return this.gameLinks?.find(({ id }) => id === 'official')?.url;
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<template lang="html">
|
||||
<div class="mt-3">
|
||||
<b-row no-gutters>
|
||||
<div
|
||||
<b-form-row>
|
||||
<b-col
|
||||
v-for="({ imageUrl, isVideo, isCover }, index) in gameMedia"
|
||||
cols="6"
|
||||
sm="4"
|
||||
: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
|
||||
v-if="isVideo"
|
||||
class="fa-solid fa-play video-indicator position-absolute text-white"
|
||||
|
@ -21,12 +23,14 @@
|
|||
<b-img
|
||||
:src="imageUrl"
|
||||
rounded
|
||||
style="height: 180px"
|
||||
class="mb-2"
|
||||
fluid
|
||||
style="max-height: 120px;"
|
||||
@click="viewMedia(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</b-row>
|
||||
</b-col>
|
||||
</b-form-row>
|
||||
|
||||
<b-modal
|
||||
id="mediaModal"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!-- TODO: hide progress if game is not yet released -->
|
||||
<template lang="html">
|
||||
<div class="pr-2 pb-2">
|
||||
<strong>Progress: </strong>
|
||||
<div>
|
||||
<h4 class="mt-4">Progress: </h4>
|
||||
<b-link @click="editing = true">{{ progress }}%</b-link>
|
||||
|
||||
<b-form v-if="editing">
|
||||
|
@ -96,6 +96,3 @@ export default {
|
|||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template lang="html">
|
||||
<section v-if="similarGames.length" class="my-5 bg-light p-3">
|
||||
<h4 class="text-center mb-3">You may also like</h4>
|
||||
<section v-if="similarGames.length" :class="['my-5 px-3 py-5', hasWallpaper ? '' : 'bg-light']">
|
||||
<h4 :class="['text-center mb-3', { 'text-outlined': hasWallpaper }]">You may also like</h4>
|
||||
|
||||
<div class="similar-games">
|
||||
<router-link
|
||||
|
@ -27,6 +27,10 @@ import { getImageUrl } from '@/utils';
|
|||
export default {
|
||||
getImageUrl,
|
||||
|
||||
props: {
|
||||
hasWallpaper: Boolean,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
similarGames: [],
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
<template lang="html">
|
||||
<b-card
|
||||
v-if="game"
|
||||
:img-src="$options.getImageUrl(game)"
|
||||
:img-src="gameCoverUrl"
|
||||
:img-alt="game.name"
|
||||
:class="['mb-3 cursor-pointer', { 'border-selected': selected }]"
|
||||
overlay
|
||||
@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">
|
||||
<i class="fa fa-check" />
|
||||
</div>
|
||||
|
@ -16,11 +23,10 @@
|
|||
<script>
|
||||
import { getImageUrl } from '@/utils';
|
||||
import { mapState } from 'vuex';
|
||||
import { NO_IMAGE_PATH } from '@/constants';
|
||||
import slugify from 'slugify'
|
||||
|
||||
export default {
|
||||
getImageUrl,
|
||||
|
||||
props: {
|
||||
game: {
|
||||
type: Object,
|
||||
|
@ -37,10 +43,18 @@ export default {
|
|||
return this.boards.find(({ id }) => id === boardId);
|
||||
},
|
||||
|
||||
gameCoverUrl() {
|
||||
return getImageUrl(this.game);
|
||||
},
|
||||
|
||||
selected() {
|
||||
return this.selectedList?.games?.includes(this.game.id)
|
||||
},
|
||||
|
||||
noImage() {
|
||||
return NO_IMAGE_PATH === this.gameCoverUrl;
|
||||
},
|
||||
|
||||
selectedList() {
|
||||
const { listIndex } = this.$route.query;
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ export default {
|
|||
},
|
||||
|
||||
isBoardPage() {
|
||||
// TODO: do show blurred background in game page
|
||||
return ['board', 'game'].includes(this.$route.name);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -15,6 +15,7 @@ export const MAX_PROFILE_LENGTH = 20;
|
|||
export const STEAM_CATEGORY_ID = 13;
|
||||
export const GOG_CATEGORY_ID = 17;
|
||||
export const TWITTER_CATEGORY_ID = 5;
|
||||
export const NO_IMAGE_PATH = '/no-image.png';
|
||||
|
||||
export const LIST_VIEW_SINGLE = 'single';
|
||||
export const LIST_VIEW_COVERS = 'covers';
|
||||
|
|
|
@ -16,7 +16,13 @@
|
|||
<b-spinner v-if="loading" class="spinner-centered" />
|
||||
|
||||
<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>
|
||||
<portal to="pageTitle">
|
||||
|
@ -99,123 +105,135 @@
|
|||
<b-col
|
||||
cols="12"
|
||||
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">
|
||||
<h2 :class="{ 'mt-3': backdrop }">{{ game.name }}</h2>
|
||||
</div>
|
||||
<game-description />
|
||||
|
||||
<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">
|
||||
<h4>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"
|
||||
/>
|
||||
<!-- <b-link v-if="!boardsWithGame.length" v-b-modal.addRemoveGameModal>
|
||||
Add to list
|
||||
</b-link> -->
|
||||
|
||||
<span v-else>{{ developer.name }}</span>
|
||||
</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>
|
||||
<game-ratings />
|
||||
|
||||
<small
|
||||
v-if="legalNotice"
|
||||
class="text-muted"
|
||||
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-row>
|
||||
|
||||
<game-media />
|
||||
|
||||
<section v-if="boardsWithGame.length" class="mt-3">
|
||||
<strong :class="{ 'text-outlined': hasWallpaper }">Found in: </strong>
|
||||
|
||||
|
@ -246,7 +264,7 @@
|
|||
</timeline> -->
|
||||
</b-container>
|
||||
|
||||
<similar-games />
|
||||
<similar-games :has-wallpaper="hasWallpaper" />
|
||||
</template>
|
||||
|
||||
<div class="pt-5" v-else>
|
||||
|
|
|
@ -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) => {
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
|
@ -15,7 +15,7 @@ export const getImageUrl = (item, size = IMAGE_SIZE_COVER_BIG) => {
|
|||
|
||||
return imageId
|
||||
? `https://images.igdb.com/igdb/image/upload/${size}/${imageId}.png`
|
||||
: '/placeholder.gif';
|
||||
: NO_IMAGE_PATH;
|
||||
};
|
||||
|
||||
export const getFileExtension = (fileName) => {
|
||||
|
|
Loading…
Reference in a new issue