Consolidate game images from gog, steam, and igdb

This commit is contained in:
Gamebrary 2022-04-04 20:50:09 -07:00
parent 9dc7c100f5
commit c6d7771618
14 changed files with 153 additions and 359 deletions

View file

@ -1,8 +1,8 @@
<template lang="html">
<div>
<template v-if="pinnedBoards.length">
<nav>
<template v-if="boards.length">
<b-button
v-for="{ id, name, backgroundColor, backgroundUrl } in pinnedBoards"
v-for="{ id, name, backgroundColor, backgroundUrl } in boards"
:key="id"
block
:title="name"
@ -36,7 +36,7 @@
<hr class="my-1">
</template>
</div>
</nav>
</template>
<script>
@ -94,6 +94,12 @@ export default {
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
nav {
overflow-y: auto;
max-height: calc(100vh - 100px);
}
.pinned-board {
background-size: cover;

View file

@ -1,7 +1,7 @@
<!-- TODO: add speedruns -->
<!-- TODO: use v-observe-visibility -->
<template lang="html">
<b-container fluid>
<b-container fluid class="p-0">
<b-row>
<b-col cols="3">
<div class="position-relative">
@ -26,7 +26,6 @@
/>
<template v-else>
<game-images ref="gameImages" :game="game" />
<!-- <game-videos :videos="game.videos" v-if="game.videos" /> -->
</template>
@ -44,14 +43,16 @@
</b-col>
<b-col cols="7">
<!-- <pre class="text-dark small">{{ game.gog.price }}</pre> -->
<h3 class="mb-2">
{{ game.name }}
<b-badge variant="success" v-if="steamGame && steamGame.metacritic">{{ steamGame.metacritic.score }}</b-badge>
<!-- <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>
<!-- TODO: get from {{game}} -->
<!-- <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
@ -72,7 +73,7 @@
battle-royale -->
<game-genres :game="game" />
<game-description :game="game" :steam-game="steamGame" />
<!-- <game-description :game="game" :steam-game="steamGame" /> -->
<game-platforms />
<!-- <game-news :game="game" /> -->
@ -117,7 +118,6 @@
<b-col cols="12" lg="2">
<similar-games
:game="game"
:loading="loading"
class="mb-2"
/>
@ -136,9 +136,9 @@ import GameRating from '@/components/Game/GameRating';
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 GameImages from '@/components/Game/GameImages';
// import GameVideos from '@/components/Game/GameVideos';
import { mapGetters, mapState } from 'vuex';
import { mapState } from 'vuex';
// import { Timeline } from 'vue-tweet-embed'
export default {
@ -148,7 +148,7 @@ export default {
GameDetails,
GamePlatforms,
GameRating,
GameImages,
// GameImages,
// GameNotes,
GameGenres,
// GameNews,
@ -163,13 +163,12 @@ export default {
// type: Object,
// required: true,
// },
gog: Object,
steamGame: Object,
// gog: Object,
// steamGame: Object,
loading: Boolean,
},
computed: {
...mapGetters(['activeGameCoverUrl']),
...mapState(['game', 'progresses', 'tags']),
twitterHandle() {

View file

@ -1,228 +1,18 @@
<!-- TODO: Mix media from other sources (e.g. instagram, wikipedia, youtube, twitter, etc... ) -->
<template lang="html">
<b-form-row v-if="screenshots" class="mt-2">
<b-col
v-for="(thumbnail, index) in thumbnails"
:key="index"
cols="2"
class="position-relative thumb"
@click.stop="openModal(index + 1)"
>
<b-img
:src="thumbnail"
rounded
class="w-100 h-auto mb-2"
/>
<span
v-if="index === maxThumbnails - 1 && screenshots.length > maxThumbnails"
class="more-images rounded text-white"
>
<i class="fas fa-images fa-fw" aria-hidden />
</span>
</b-col>
<b-modal
id="game-images"
size="xl"
hide-footer
>
<template v-slot:modal-header="{ close }">
<modal-header
title="Images"
:subtitle="subtitle"
@close="close"
>
<!-- TODO Display image source igdb, instagram, etc... -->
<template v-slot:header>
<b-img
:src="activeGameCoverUrl"
:alt="game.name"
class="float-left mr-2"
height="40"
rounded
/>
</template>
<b-button
v-if="isBoardOwner"
class="d-none d-sm-inline"
@click="setAsWallpaper"
>
<i
v-if="saving"
class="d-sm-fas fa-sync fa-spin fa-fw"
aria-hidden
/>
<span v-else>Set as wallpaper</span>
</b-button>
<b-button
@click="previous"
>
<i class="fas fa-angle-left fa-fw" aria-hidden />
</b-button>
<b-button
@click="next"
>
<i class="fas fa-angle-right fa-fw" aria-hidden />
</b-button>
</modal-header>
</template>
<b-carousel
ref="screenshots"
no-animation
v-model="activeIndex"
>
<b-carousel-slide
v-for="(screenshot, index) in slides"
:key="index"
content-tag="rounded"
>
<template #img>
<b-img
rounded
class="mw-100 d-block ml-auto mr-auto"
:src="screenshot"
/>
</template>
</b-carousel-slide>
</b-carousel>
</b-modal>
</b-form-row>
<div>
gameImages
<pre>{{ gameImages }}</pre>
</div>
</template>
<script>
import { mapGetters, mapState } from 'vuex';
import { mapGetters } from 'vuex';
export default {
props: {
game: Object,
},
data() {
return {
activeIndex: 0,
maxThumbnails: 3,
saving: false,
};
},
computed: {
...mapGetters(['activeGameCoverUrl', 'isBoardOwner']),
...mapState(['board']),
thumbnails() {
// eslint-disable-next-line
return this.trimmedScreenshots.map(({ image_id }) => `https://images.igdb.com/igdb/image/upload/t_thumb/${image_id}.jpg`)
},
slides() {
return [
this.activeGameCoverUrl,
// eslint-disable-next-line
...this.screenshots.map(({ image_id }) => `https://images.igdb.com/igdb/image/upload/t_screenshot_huge_2x/${image_id}.jpg`),
];
},
screenshots() {
return this.game && this.game.screenshots ? this.game.screenshots : [];
},
trimmedScreenshots() {
return this.screenshots.length > this.maxThumbnails
? this.screenshots.slice(0, this.maxThumbnails)
: this.screenshots;
},
subtitle() {
if (this.activeIndex === 0) {
return 'Game cover';
}
return `Screenshot ${this.activeIndex} of ${this.slides.length - 1}`;
},
},
methods: {
async setAsWallpaper() {
this.saving = true;
const payload = {
...this.board,
backgroundUrl: this.slides[this.activeIndex],
};
this.$store.commit('SET_ACTIVE_BOARD', payload);
await this.$store.dispatch('SAVE_BOARD')
.catch(() => {
this.saving = false;
this.$bvToast.toast('There was an error renaming list', { variant: 'danger' });
});
this.$bus.$emit('LOAD_BOARD_BACKGROUND');
this.saving = false;
this.$bvToast.toast('Wallpaper set');
this.$bvModal.hide('game-images');
},
previous() {
this.$refs.screenshots.prev();
},
next() {
this.$refs.screenshots.next();
},
setSlide(index) {
this.$refs.screenshots.setSlide(index);
},
openModal(index = 0) {
this.activeIndex = index;
this.$bvModal.show('game-images');
},
...mapGetters(['gameImages']),
// ...mapState(['board']),
},
};
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
.thumb {
cursor: pointer;
}
.more-images {
position: absolute;
left: 5px;
top: 0;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
width: calc(100% - 10px);
height: calc(100% - .5rem);
// padding-top: 1rem;
background-color: rgba(0, 0, 0, .5);
> small {
margin: 0 2px;
}
> i {
margin: 0 2px;
}
> span {
display: flex;
flex-direction: column;
justify-content: center;
margin-top: auto;
text-align: center;
}
}
</style>

View file

@ -35,7 +35,6 @@ export default {
},
props: {
game: Object,
loading: Boolean,
},
@ -46,7 +45,7 @@ export default {
},
computed: {
...mapState(['games', 'activeGame']),
...mapState(['game', 'games', 'activeGame']),
similarGameIds() {
return this.game && this.game.similar_games;
@ -54,9 +53,7 @@ export default {
},
mounted() {
if (this.similarGameIds) {
this.loadGames();
}
if (this.similarGameIds) this.loadGames();
},
methods: {

View file

@ -12,6 +12,7 @@
<b-button
class="align-self-baseline"
variant="light"
@click="$emit('close')"
>
<i class="fas fa-times fa-fw" aria-hidden />

View file

@ -1,16 +1,16 @@
<template lang="html">
<div class="p-2 game-page" ref="gamePage">
<!-- <pre>{{ steamGame }}</pre> -->
<b-container fluid class="p-2">
<!-- <pre>{{ speedruns }}</pre> -->
<b-skeleton v-if="loading" />
<game v-else-if="game" :game="game" :gog="gog" :steam-game="steamGame" />
<game v-else-if="game" :game="game" />
<!-- <div class="game-backdrop" :style="`background-image: url(${backdropUrl})`" /> -->
</div>
</b-container>
</template>
<script>
import Game from '@/components/Game';
import { mapState } from 'vuex';
export default {
components: {
@ -19,7 +19,6 @@ export default {
data() {
return {
game: {},
gog: null,
speedruns: null,
steamGame: null,
@ -28,17 +27,19 @@ export default {
},
computed: {
...mapState(['game']),
gameId() {
return this.$route.params.gameId;
},
backdropUrl() {
const screenshots = this.game && this.game.screenshots;
return screenshots && screenshots.length
? `https://images.igdb.com/igdb/image/upload/t_screenshot_huge_2x/${screenshots[0].image_id}.jpg`
: '';
},
// backdropUrl() {
// const screenshots = this.game && this.game.screenshots;
//
// return screenshots && screenshots.length
// ? `https://images.igdb.com/igdb/image/upload/t_screenshot_huge_2x/${screenshots[0].image_id}.jpg`
// : '';
// },
},
watch: {
@ -59,11 +60,13 @@ export default {
methods: {
async loadGame() {
if (!this.gameId) return;
const gameCached = this.game.id && this.game.id === this.gameId;
if (!this.gameId || gameCached) return;
this.loading = true;
this.game = await this.$store.dispatch('LOAD_GAME', this.gameId)
await this.$store.dispatch('LOAD_GAME', this.gameId)
.catch(() => {
this.loading = false;
this.$bvToast.toast('Error loading game', { variant: 'error' });
@ -78,41 +81,30 @@ export default {
const gogCategoryId = 17;
const steamCategoryId = 13;
const gogPage = this.game && this.game.websites
? this.game.websites.find(({ category }) => category === gogCategoryId)
: null;
const steamPage = this.game && this.game.websites
? this.game.websites.find(({ category }) => category === steamCategoryId)
: null;
// const hasGog =
[this.speedruns] = (await this.$store.dispatch('LOAD_GAME_SPEEDRUNS', this.game.name)).data;
this.gog = gogPage
? await this.$store.dispatch('LOAD_GOG_GAME', this.game.name)
: null;
// console.log(steamPage);
// [this.speedruns] = (await this.$store.dispatch('LOAD_GAME_SPEEDRUNS', this.game.name)).data;
// TODO: use regex or more elegant way to get id from url
const steamGameId = steamPage
? steamPage.url.split('app/')[1].split('/')[0]
: null;
this.steamGame = steamGameId
? await this.$store.dispatch('LOAD_STEAM_GAME', steamGameId)
const gogPage = this.game && this.game.websites
? this.game.websites.find(({ category }) => category === gogCategoryId)
: null;
if (steamGameId) await this.$store.dispatch('LOAD_STEAM_GAME', steamGameId);
if (gogPage) await this.$store.dispatch('LOAD_GOG_GAME', this.game.name);
},
},
};
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
.game-page {
z-index: 1;
}
.game-backdrop {
width: 100%;
height: 100vw;

View file

@ -1,10 +1,6 @@
<template lang="html">
<<<<<<< Updated upstream
<b-container fluid>
<page-title title="General settings" />
=======
<b-container>
>>>>>>> Stashed changes
<game-detail-settings />
<!-- <dock-settings /> -->
<language-settings />

View file

@ -1,17 +1,5 @@
<!-- TODO: pagination? -->
<template lang="html">
<<<<<<< Updated upstream
<b-container fluid>
<page-title title="Notes" />
<!-- TODO: add 'add note' -->
<b-form-input
v-if="!showEmptyState"
type="search"
style="max-width: 200px"
placeholder="Search notes"
v-model="search"
/>
=======
<b-container>
<page-title
title="Notes"
@ -28,7 +16,6 @@
Add note
</b-button> -->
</page-title>
>>>>>>> Stashed changes
<empty-state
v-if="showEmptyState"

View file

@ -43,14 +43,9 @@
<small>Releases</small>
</b-list-group-item>
<<<<<<< Updated upstream
<hr />
<b-list-group-item :to="{ name: 'profiles' }">
=======
<b-list-group-item exact exact-active-class="bg-primary text-white" :to="{ name: 'profiles' }">
>>>>>>> Stashed changes
<i class="fa-solid fa-people-group fa-fw" aria-hidden />
<small>Profiles</small>
</b-list-group-item>

View file

@ -1,10 +1,6 @@
<!-- TODO: break this up into components -->
<template lang="html">
<<<<<<< Updated upstream
<b-container fluid>
=======
<b-container>
>>>>>>> Stashed changes
<empty-state
v-if="showEmptyState"
:title="$t('tags.title')"

View file

@ -1,6 +1,5 @@
<template lang="html">
<div>
<pre>{{ loading }}</pre>
<stripe-checkout
ref="checkoutRef"
mode="subscription"
@ -35,8 +34,9 @@ export default {
},
methods: {
load(status) {
console.log(status);
// load(status) {
load() {
// console.log(status);
},
},
};

View file

@ -1,5 +1,6 @@
<!-- TODO: Mix media from other sources (e.g. instagram, wikipedia, youtube, twitter, etc... ) -->
<!-- TODO: get images from article and add them to media page -->
<!-- TODO: add loading placeholder -->
<template lang="html">
<b-container fluid class="p-2">
<b-form-row>
@ -14,21 +15,28 @@
</b-col>
<b-col cols="9">
{{ game.name }}
<b-button
@click="openGame"
class="mb-2"
>
Back to Game
</b-button>
<hr />
<b-img
v-for="(thumbnail, index) in thumbnails"
v-for="(thumbnail, index) in gameThumbnails"
:key="index"
:src="thumbnail"
img-alt="Image"
width="200"
img-top
class="m-2"
rounded
@click.stop="openModal(index)"
/>
</b-col>
</b-form-row>
<b-button @click="openGame">Back to Game</b-button>
<b-modal
id="game-images"
size="xl"
@ -40,10 +48,9 @@
:subtitle="subtitle"
@close="close"
>
<!-- TODO Display image source igdb, instagram, etc... -->
<template v-slot:header>
<b-img
:src="activeGameCoverUrl"
:src="gameCoverThumbUrl"
:alt="game.name"
class="float-left mr-2"
height="40"
@ -54,6 +61,7 @@
<b-button
v-if="isBoardOwner"
class="d-none d-sm-inline"
variant="light"
@click="setAsWallpaper"
>
<i
@ -65,12 +73,14 @@
</b-button>
<b-button
variant="transparent"
@click="previous"
>
<i class="fas fa-angle-left fa-fw" aria-hidden />
</b-button>
<b-button
variant="transparent"
@click="next"
>
<i class="fas fa-angle-right fa-fw" aria-hidden />
@ -84,7 +94,7 @@
v-model="activeIndex"
>
<b-carousel-slide
v-for="(screenshot, index) in slides"
v-for="(screenshot, index) in gameImages"
:key="index"
content-tag="rounded"
>
@ -129,6 +139,12 @@ export default {
: '/static/no-image.jpg';
},
gameCoverThumbUrl() {
return this.game.cover
? `https://images.igdb.com/igdb/image/upload/t_cover_small_2x/${this.game.cover.image_id}.jpg`
: '/static/no-image.jpg';
},
slides() {
return [
// eslint-disable-next-line
@ -141,11 +157,65 @@ export default {
},
subtitle() {
if (this.activeIndex === 0) {
return 'Game cover';
}
return `Screenshot ${this.activeIndex} of ${this.gameImages.length - 1}`;
},
return `Screenshot ${this.activeIndex} of ${this.slides.length - 1}`;
gameThumbnails() {
const gogImages = this.game.gog && this.game.gog.gallery
// eslint-disable-next-line
? this.game.gog.gallery.map((image) => {
const imageId = image.split('.com/')[1];
return imageId
? `https://images.gog-statics.com/${imageId}.jpg`
: null;
})
: [];
const steamImages = this.game.steam && this.game.steam.screenshots
// eslint-disable-next-line
? this.game.steam.screenshots.map(({ path_thumbnail }) => path_thumbnail)
: [];
const igdbImages = this.game && this.game.screenshots
// eslint-disable-next-line
? this.game.screenshots.map(({ image_id }) => `https://images.igdb.com/igdb/image/upload/t_screenshot_med_2x/${image_id}.jpg`)
: [];
return [
...gogImages,
...steamImages,
...igdbImages,
];
},
gameImages() {
const gogImages = this.game.gog && this.game.gog.gallery
// eslint-disable-next-line
? this.game.gog.gallery.map((image) => {
const imageId = image.split('.com/')[1];
return imageId
? `https://images.gog-statics.com/${imageId}.jpg`
: null;
})
: [];
const steamImages = this.game.steam && this.game.steam.screenshots
// eslint-disable-next-line
? this.game.steam.screenshots.map(({ path_full }) => path_full)
: [];
const igdbImages = this.game && this.game.screenshots
// eslint-disable-next-line
? this.game.screenshots.map(({ image_id }) => `https://images.igdb.com/igdb/image/upload/t_screenshot_huge_2x/${image_id}.jpg`)
: [];
return [
...gogImages,
...steamImages,
...igdbImages,
];
},
},
@ -203,39 +273,3 @@ export default {
},
};
</script>
<style lang="scss" rel="stylesheet/scss" scoped>
.thumb {
cursor: pointer;
}
.more-images {
position: absolute;
left: 5px;
top: 0;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
width: calc(100% - 10px);
height: calc(100% - .5rem);
// padding-top: 1rem;
background-color: rgba(0, 0, 0, .5);
> small {
margin: 0 2px;
}
> i {
margin: 0 2px;
}
> span {
display: flex;
flex-direction: column;
justify-content: center;
margin-top: auto;
text-align: center;
}
}
</style>

View file

@ -66,7 +66,7 @@ export default {
});
},
LOAD_STEAM_GAME({ state }, steamGameId) {
LOAD_STEAM_GAME({ commit }, steamGameId) {
return new Promise((resolve, reject) => {
axios.get(`${API_BASE}/steam-game?gameId=${steamGameId}`)
.then(({ data }) => {
@ -77,10 +77,7 @@ export default {
? steamGameData.data
: null;
// console.log('steam', gameData);
// console.log(state.game);
// TODO: finish mutation logic
// commit('APPEND_STEAM_GAME_DATA', gameData)
commit('APPEND_STEAM_GAME_DATA', gameData);
resolve(gameData);
}).catch(reject);
@ -480,7 +477,7 @@ export default {
});
},
LOAD_GOG_GAME(context, search) {
LOAD_GOG_GAME({ commit }, search) {
return new Promise((resolve, reject) => {
axios.get(`${API_BASE}/gog?search=${search}`)
.then(({ data }) => {
@ -488,7 +485,9 @@ export default {
? data.products[0]
: null;
resolve(game);
console.log('gog', game);
commit('APPEND_GOG_GAME_DATA', game);
}).catch(reject);
});
},

View file

@ -126,11 +126,13 @@ export default {
state.game = game;
},
// APPEND_STEAM_GAME_DATA(state, steamGameData) {
// // TODO: finish this, merge data
// // console.log(steamGameData);
// // state.game = steamGameData;
// },
APPEND_STEAM_GAME_DATA(state, data) {
state.game.steam = data;
},
APPEND_GOG_GAME_DATA(state, data) {
state.game.gog = data;
},
CLEAR_GAME_MODAL_DATA(state) {
state.activeGame = null;