Refactor tags

This commit is contained in:
Gamebrary 2022-08-11 23:34:40 -07:00
parent 97d7513c96
commit d619ac9796
17 changed files with 181 additions and 274 deletions

View file

@ -7,7 +7,7 @@
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<!-- <link rel="manifest" href="/site.webmanifest"> -->
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">

View file

@ -100,10 +100,6 @@ export default {
isGamePage() {
return this.$route.name === 'game';
},
isBoard() {
return ['public.board', 'board'].includes(this.$route.name);
},
},
watch: {

View file

@ -37,8 +37,9 @@
height="6px"
/>
<template v-if="showGameTags">
<b-badge
<!-- TODO: use correct tags -->
<!-- <pre>{{ gameTags }}</pre> -->
<!-- <b-badge
v-for="({ hex, tagTextColor }, name) in gameTags"
:key="name"
pill
@ -48,8 +49,7 @@
:style="`background-color: ${hex}; color: ${tagTextColor}`"
>
<small>{{ name }}</small>
</b-badge>
</template>
</b-badge> -->
</b-card-body>
</b-col>
</b-row>

View file

@ -42,14 +42,14 @@
<small v-else>{{ gameProgress }}%</small>
</b-badge>
<template v-if="showGameTags">
<template v-if="tagsApplied.length">
<b-badge
v-for="({ hex, tagTextColor }, name) in gameTags"
v-for="({ bgColor, textColor, name }) in tagsApplied"
:key="name"
pill
class="mr-1"
variant="primary"
:style="`background-color: ${hex}; color: ${tagTextColor}`"
:style="`background-color: ${bgColor}; color: ${textColor}`"
>
<small>{{ name }}</small>
</b-badge>

View file

@ -21,7 +21,8 @@
height="6px"
/>
<template v-if="showGameTags">
<!-- TODO: use correct tags -->
<!-- <template v-if="showGameTags">
<b-badge
v-for="({ hex, tagTextColor }, name) in gameTags"
:key="name"
@ -32,7 +33,7 @@
>
<small>{{ name }}</small>
</b-badge>
</template>
</template> -->
<b-badge variant="warning" v-if="gameNotes">
<i class="far fa-sticky-note fa-fw" />

View file

@ -23,7 +23,8 @@
height="6px"
/>
<template v-if="showGameTags">
<!-- TODO: use correct tags -->
<!-- <template v-if="showGameTags">
<b-badge
v-for="({ hex, tagTextColor }, name) in gameTags"
:key="name"
@ -34,7 +35,7 @@
>
<small class="font-weight-bold">{{ name }}</small>
</b-badge>
</template>
</template> -->
</b-card-body>
</b-row>
</b-card>

View file

@ -1,22 +1,16 @@
<template lang="html">
<b-row>
<b-col
v-for="({ hex, tagTextColor, name }, index) in tags"
@click="$router.push({ name: 'tag.edit', params: { id: index } })"
cols="6"
xl="4"
class="mb-3"
:key="name"
>
<b-row class="p-3">
<b-button
v-for="({ textColor, bgColor, name }, index) in tags"
@click="$router.push({ name: 'tag.edit', params: { id: index } })"
rounded
block
variant="outline-light"
:style="`background-color: ${hex}; color: ${tagTextColor}`"
:style="`background-color: ${bgColor}; color: ${textColor}`"
:key="name"
>
{{ name }}
</b-button>
</b-col>
</b-row>
</template>
@ -43,13 +37,5 @@ export default {
this.loading = false;
},
methods: {
openGame(gameId) {
const { id, slug } = this.games[gameId];
this.$router.push({ name: 'game.tags', params: { id, slug } })
},
},
};
</script>

View file

@ -74,7 +74,7 @@ export const GENRE_ICONS = {
export const KEYBOARD_SHORTCUTS = {
'MODAL_keyboard-shortcuts': ['shift', '?'],
'MODAL_create-board': ['shift', 'c'],
MODAL_devTools: ['shift', 'd'],
// MODAL_devTools: ['shift', 'd'],
ROUTE_boards: ['shift', 'b'],
ROUTE_tags: ['shift', 't'],
ROUTE_notes: ['shift', 'n'],

View file

@ -17,25 +17,10 @@ export default {
return this.gameProgress && Number(this.gameProgress) === 100;
},
gameTags() {
const tagsArray = Object.entries(this.tags);
const filteredTags = tagsArray.filter(([key, value]) => {
return value.games.includes(this.gameId);
});
const filteredTagsObject = Object.fromEntries(filteredTags);
return filteredTagsObject;
},
showGameProgress() {
return this.gameProgress > 0;
},
showGameTags() {
return this.list?.settings?.showGameTags && this.gameTags;
},
showReleaseDates() {
return this.list?.settings?.showReleaseDates;
},
@ -48,6 +33,10 @@ export default {
: 0;
},
tagsApplied() {
return this.tags?.filter((tag) => tag?.games?.includes(this.game?.id))
},
gameNotes() {
const { settings } = this.list;

View file

@ -4,112 +4,85 @@
Create tag
</portal>
<form
ref="newTagForm"
@submit.stop.prevent="submit"
>
<b-form-row class="mb-3">
<b-col cols="8" md="9">
<form @submit.prevent="submit">
<label for="tagName">Tag name:</label>
<b-form-input
id="tagName"
v-model.trim="tag.name"
class="mb-3 field"
maxlength="20"
:placeholder="$t('tags.form.inputPlaceholder')"
required
v-model.trim="tagName"
trim
/>
<b-form-text v-if="tagName" tag="span">
{{ $t('tags.form.preview') }}
<p>Background color</p>
<v-swatches v-model="tag.bgColor" show-fallback />
<b-badge :style="`background-color: ${hex}; color: ${tagTextColor}`">
{{ tagName }}
</b-badge>
</b-form-text>
</b-col>
<p>Text color</p>
<v-swatches v-model="tag.textColor" show-fallback />
<b-col cols="4" md="3">
<b-input-group>
<b-form-input
v-model="hex"
type="color"
required
/>
<p>Preview</p>
<b-form-input
v-model="tagTextColor"
type="color"
required
/>
</b-input-group>
</b-col>
</b-form-row>
<b-button
rounded
block
size="sm"
class="mr-2 mb-2 field"
variant="outline-light"
:style="`background-color: ${tag.bgColor}; color: ${tag.textColor}`"
>
{{ tag.name || 'Tag preview' }}
</b-button>
<b-button
variant="primary"
class="d-flex ml-auto"
:disabled="isDuplicate || saving || !Boolean(tagName)"
@click="submit"
:disabled="saving"
type="submit"
>
<b-spinner small v-if="saving" />
<span v-else>{{ $t('tags.form.addTag')}}</span>
<span v-else>Create</span>
</b-button>
<b-alert
class="mt-3 mb-0"
:show="isDuplicate"
variant="warning"
>
{{ $t('tags.form.duplicateMessage', { tagName }) }}
<strong>{{ tagName }}</strong>
</b-alert>
</form>
</b-container>
</template>
<script>
import { mapState } from 'vuex';
import VSwatches from 'vue-swatches'
export default {
data() {
return {
colorCombinations: [
['#0d1137', '#e52165'],
['#ffffff', '#000000'],
['#101820', '#FEE715'],
['#F2AA4C', '#101820'],
['#F93822', '#FDD20E'],
],
tagTextColor: '#F4B41A',
tagName: '',
tag: {
name: '',
textColor: '#DDE6E8',
bgColor: '#1FBC9C',
games: [],
},
saving: false,
}
},
mounted() {
this.setRandomColors();
components: {
VSwatches,
},
computed: {
...mapState(['tags', 'platform', 'games']),
// isDuplicate() {
// const { tagName, localTags } = this;
//
// const tagNames = Object.keys(localTags)
// .filter(name => name !== tagName)
// .map(name => name.toLowerCase());
//
// return tagNames.includes(tagName.toLowerCase());
// },
...mapState(['tags']),
},
methods: {
setRandomColors() {
const { colorCombinations } = this;
async submit() {
this.$store.commit('CREATE_TAG', this.tag);
this.saving = true;
const randomNumber = Math.floor(Math.random() * colorCombinations.length);
await this.$store.dispatch('SAVE_GAME_TAGS')
.catch(() => {});
this.tagTextColor = colorCombinations[randomNumber][0];
this.hex = colorCombinations[randomNumber][1];
this.saving = true;
this.$router.push({ name: 'tags' })
},
},
};
</script>

View file

@ -34,14 +34,6 @@
ref="form"
@submit="saveTag"
>
<b-alert
class="mt-3 mb-0"
:show="isEditedNameDuplicate && !saving"
variant="warning"
>
You already have a tag named <strong>{{ tag.name }}</strong>
</b-alert>
<label for="tagName">Tag name:</label>
<b-form-input
@ -57,15 +49,14 @@
<p>Background color</p>
<v-swatches
v-model="tag.hex"
v-model="tag.bgColor"
show-fallback
popover-x="left"
/>
<p>Text color</p>
<v-swatches
v-model="tag.tagTextColor"
v-model="tag.textColor"
show-fallback
popover-x="left"
/>
@ -79,7 +70,7 @@
size="sm"
class="mr-2 mb-2 field"
variant="outline-light"
:style="`background-color: ${tag.hex}; color: ${tag.tagTextColor}`"
:style="`background-color: ${tag.bgColor}; color: ${tag.textColor}`"
>
{{ tag.name }}
</b-button>
@ -88,6 +79,11 @@
<p>Games tagged</p>
<b-alert :show="tag.games.length === 0" variant="light" class="field">
No games tagged
</b-alert>
<!-- TODO: add quick game picker -->
<div class="tagged-games">
<b-img
v-for="game in tag.games"
@ -103,7 +99,7 @@
<b-button
variant="primary"
:disabled="isEditedNameDuplicate || saving"
:disabled="saving"
type="submit"
>
<b-spinner small v-if="saving" />
@ -122,7 +118,6 @@ export default {
return {
tag: {},
loading: true,
originalTagName: '',
localTags: {},
saving: false,
}
@ -135,18 +130,6 @@ export default {
computed: {
...mapState(['tags', 'games']),
tagNames() {
const sanitizedNames = this.tags?.map(({ name }) => name.toLowerCase());
return sanitizedNames.length > 0
? sanitizedNames.filter(name => name?.toLowerCase() !== this.originalTagName?.toLowerCase())
: [];
},
isEditedNameDuplicate() {
return this.tagNames?.includes(this.tag?.name?.toLowerCase());
},
tagIndex() {
return this.$route?.params?.id;
},
@ -172,7 +155,6 @@ export default {
const { tags, tagIndex } = this;
this.tag = JSON.parse(JSON.stringify(tags[tagIndex]));
this.originalTagName = JSON.parse(JSON.stringify(this.tag.name));
this.loading = false;
},
@ -194,20 +176,15 @@ export default {
},
deleteTag(tagName) {
this.$delete(this.localTags, tagName);
this.saveTags(true);
},
removeTag(tagName) {
this.$store.commit('REMOVE_GAME_TAG', { tagName, gameId: this.gameId });
this.saveTags();
// TODO: call mutation to remove tag and save tags
// this.saveTags(true);
},
async saveTag(e) {
e.preventDefault();
if (this.$refs.form.checkValidity()) {
const { tag, tags, originalTagName } = this;
const { tag, tags } = this;
tags[this.tagIndex] = tag;

View file

@ -96,14 +96,12 @@
</header>
<b-button
v-for="({ hex, tagTextColor, name }) in tags"
v-for="({ bgColor, textColor, name }) in tagsApplied"
:key="name"
rounded
size="sm"
variant="outline-light"
class="mr-1 my-2"
:disabled="saving"
:style="`background-color: ${hex}; color: ${tagTextColor}`"
:style="`background-color: ${bgColor}; color: ${textColor}`"
@click="$router.push({ name: 'game.tags', params: { id: game.id, slug: game.slug } })"
>
{{ name }}
@ -335,6 +333,10 @@ export default {
},
tagsApplied() {
return this.tags?.filter((tag) => tag?.games?.includes(this.game?.id))
},
legalNotice() {
return this.game?.steam?.legal_notice;
},

View file

@ -51,22 +51,20 @@
<section>
<h3 class="mb-3">Tags applied to {{ game.name }}</h3>
<b-alert
v-if="tagsSelected.length === 0"
show
variant="light"
>
<b-alert :show="noneSelected" variant="light">
No tags applied
</b-alert>
<!-- TODO: use correct tags -->
<b-button
v-for="{ name, hex, tagTextColor } in tags"
v-for="({ selected, name, bgColor, textColor }, index) in formattedTags"
:key="name"
rounded
block
variant="outline-light"
:style="`background-color: ${hex}; color: ${tagTextColor}`"
@click="removeTag"
:class="{ 'd-none': !selected }"
:disabled="saving"
:style="`background-color: ${bgColor}; color: ${textColor}`"
@click="removeTag(index)"
>
{{ name }}
</b-button>
@ -76,19 +74,19 @@
<h3 class="my-3">Tags available</h3>
<pre>{{ tags }}</pre>
<!-- <b-button
v-for="({ name, hex, tagTextColor }, index) in tags"
<b-button
v-for="({ selected, name, bgColor, textColor }, index) in formattedTags"
:key="name"
rounded
block
variant="outline-light"
:class="{ 'd-none': selected }"
:disabled="saving"
:style="`background-color: ${hex}; color: ${tagTextColor}`"
:style="`background-color: ${bgColor}; color: ${textColor}`"
@click="addTag(index)"
>
{{ name }}
</b-button> -->
</b-button>
</section>
</b-col>
</b-row>
@ -131,36 +129,25 @@ export default {
return Object.keys(this.tags).length === 0;
},
tagsSelected() {
return this.tags?.filter(({ games }) => {
return games?.includes(this.game?.id);
})
formattedTags() {
return this.tags.map((tag) => ({
...tag,
selected: tag.games.includes(Number(this.$route.params.id)),
}));
},
tagsAvailable() {
return Object.entries(this.tags).map((t) => {
const [name, tag] = t;
return { name: name, ...tag };
})
.filter(({ games }) => {
return !games.includes(this.game.id);
})
noneSelected() {
return !this.formattedTags.some(({ selected }) => Boolean(selected));
},
},
mounted() {
if (this.game?.id !== this.$route.params.id) {
this.loadGame();
} else {
this.loading = false;
}
this.load();
},
methods: {
async loadGame() {
async load() {
this.loading = true;
this.$store.commit('CLEAR_GAME');
await this.$store.dispatch('LOAD_GAME', this.$route.params.id);
await this.$store.dispatch('LOAD_TAGS')
@ -172,36 +159,28 @@ export default {
},
async addTag(index) {
console.log('add this tag');
// TODO: use commit instead?
// const gameId = this.game.id;
//
// if (!gameId) return;
//
// const tags = JSON.parse(JSON.stringify(this.tags)) ;
//
// tags[index].games.push(gameId)
//
// console.log(`game id ${gameId} should be included`, tags[index].games);
this.$store.commit('APPLY_TAG_TO_GAME', index);
this.saving = true;
// this.saving = true;
await this.$store.dispatch('SAVE_GAME_TAGS')
.catch(() => {
this.saving = false;
});
// await this.$store.dispatch('SAVE_TAGS', tags)
// .catch((e) => {
// console.log(e);
// });
// this.saving = false;
// this.$store.commit('ADD_GAME_TAG', { tagName, gameId });
// await this.saveTags();
this.saving = false;
},
async removeTag(tagName) {
const gameId = this.game.id;
async removeTag(index) {
this.$store.commit('REMOVE_GAME_TAG', index);
// this.$store.commit('REMOVE_GAME_TAG', { tagName, gameId });
// await this.saveTags();
this.saving = true;
await this.$store.dispatch('SAVE_GAME_TAGS')
.catch(() => {
this.saving = false;
});
this.saving = false;
},
manageTags() {

View file

@ -7,6 +7,7 @@
<portal to="headerActions">
<b-button
v-if="!showEmptyState"
class="mr-2"
variant="light"
:to="{ name: 'tag.create' }"
@ -27,9 +28,7 @@
</b-button>
</empty-state>
<b-col v-else>
<tags-list />
</b-col>
<tags-list v-else />
</b-row>
</b-container>
</template>

View file

@ -581,6 +581,18 @@ export default {
});
},
SAVE_GAME_TAGS({ state }) {
const db = firestore();
return new Promise((resolve, reject) => {
db.collection('tags')
.doc(state.user.uid)
.set({ tags: state.tags }, { merge: false })
.then(() => resolve())
.catch(reject);
});
},
GET_TWITCH_TOKEN({ commit }) {
const db = firestore();
@ -726,14 +738,13 @@ export default {
const { tags } = doc.data();
if (typeof tags === 'object') {
console.warn('Legacy tag detected');
// console.warn('Legacy tag detected');
const formattedTags = Object.entries(tags).map(([ ,tag]) => ({ ...tag }));
commit('SET_TAGS', formattedTags);
resolve(formattedTags);
} else {
console.log('is type', typeof tags);
// console.log('is type', typeof tags);
}
});
});

View file

@ -9,19 +9,6 @@ export default {
return board?.owner === user?.uid;
},
gameTags: ({ tags, game }) => {
if (!game?.id) return [];
// TODO: refactor architecture, don't use tag name as key
const tagsArray = Object.entries(tags);
const filteredTags = tagsArray.filter(([key, value]) => {
return value.games.includes(game.id);
});
const filteredTagsObject = Object.fromEntries(filteredTags);
return filteredTagsObject;
},
// Arabic is the only ltr language supported at the moment
isRTL: ({ settings }) => settings && settings.language !== 'ar',

View file

@ -175,21 +175,21 @@ export default {
state.settings = settings;
},
UPDATE_TAG(state, { tagName, tagHex, tempTag }) {
const updatedTag = {
...state.tags[tempTag.tagName],
hex: tagHex,
};
const renaming = tagName !== tempTag.tagName;
if (renaming) {
Vue.set(state.tags, tagName, updatedTag);
Vue.delete(state.tags, tempTag.tagName);
} else {
state.tags[tempTag.tagName] = updatedTag;
}
},
// UPDATE_TAG(state, { tagName, tagHex, tempTag }) {
// const updatedTag = {
// ...state.tags[tempTag.tagName],
// hex: tagHex,
// };
//
// const renaming = tagName !== tempTag.tagName;
//
// if (renaming) {
// Vue.set(state.tags, tagName, updatedTag);
// Vue.delete(state.tags, tempTag.tagName);
// } else {
// state.tags[tempTag.tagName] = updatedTag;
// }
// },
SET_TAGS(state, tags) {
state.tags = tags;
@ -247,12 +247,18 @@ export default {
}
},
ADD_GAME_TAG(state, { tagName, gameId }) {
state.tags[tagName].games.push(gameId);
APPLY_TAG_TO_GAME(state, tagIndex) {
state.tags[tagIndex].games.push(state.game.id);
},
REMOVE_GAME_TAG(state, { tagName, gameId }) {
state.tags[tagName].games.splice(state.tags[tagName].games.indexOf(gameId), 1);
CREATE_TAG(state, tag) {
state.tags.push(tag);
},
REMOVE_GAME_TAG(state, tagIndex) {
const gameIndex = state.tags[tagIndex].games.indexOf(state.game.id);
state.tags[tagIndex].games.splice(gameIndex, 1);
},
SET_SEARCH_RESULTS(state, results) {