Add user words logic. Some refactiring.

Add async/await things. Fix back-up json.
This commit is contained in:
2019-03-10 13:36:15 +07:00
parent 915d35e295
commit a364ad84fa
8 changed files with 219 additions and 83 deletions

View File

@@ -111,16 +111,19 @@
],
"articleId": "hp1",
"partTitle": "kapitel 1",
"words": [
{
"origWord": "Hallo",
"transWord": "Привет"
"words": {
"diemutter": {
"origText": "Mutter",
"origPrefix": "die",
"transText": "Мама",
"type": 1
},
{
"transWord": "Привет",
"origWord": "Hallo1"
"etwasmachen": {
"origText": "etwasmachen",
"transText": "Что-то делать",
"type": 2
}
],
},
"articlePartId": "k1",
"youtubeId": "hHW1oY26kxQ",
"articleTitle": "Harry Potter und Stein der Weisen - 1",
@@ -139,12 +142,13 @@
],
"articleId": "hp1",
"partTitle": "kapitel 2",
"words": [
{
"transWord": "Привет",
"origWord": "Hallo2"
"words": {
"hallo": {
"origText": "Hallo",
"transText": "Привет",
"type": 1
}
],
},
"articlePartId": "k2",
"youtubeId": "hHW1oY26kxQ",
"articleTitle": "Harry Potter und Stein der Weisen - 2",
@@ -163,12 +167,7 @@
],
"articleId": "hp1",
"partTitle": "kapitel 3",
"words": [
{
"transWord": "Привет",
"origWord": "Hallo3"
}
],
"words": {},
"articlePartId": "k3",
"youtubeId": "hHW1oY26kxQ",
"articleTitle": "Harry Potter und Stein der Weisen - 3",

View File

@@ -0,0 +1,90 @@
<template>
<v-card>
<v-card-title>
<div class="headline">
<v-tooltip bottom>
<v-avatar v-if="isWord" color="teal" size="45" slot="activator">
<span class="white--text">W</span>
</v-avatar>
<span>Слово / das word</span>
</v-tooltip>
<v-tooltip bottom>
<v-avatar v-if="isRedewndung" color="indigo" size="45" slot="activator">
<span class="white--text">RW</span>
</v-avatar>
<span>Выражение / die Redewendung</span>
</v-tooltip>
{{ getFullOriginalWord(wordEntity) }}
</div>
</v-card-title>
<v-divider></v-divider>
<v-card-text>
{{ wordEntity.transText }}
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn fab dark small color="primary" @click.stop="addWord(wordEntity)" :disabled="isProcessing">
<v-icon class="d-flex" dark>add</v-icon>
</v-btn>
<v-snackbar v-model="snackbar.isEnabled" color="error" bottom light>
<v-icon>warning</v-icon>
{{ snackbar.text }}
</v-snackbar>
</v-card-actions>
</v-card>
</template>
<script>
import { mapGetters } from 'vuex';
import { getFullOriginalWord, WORD_TYPES } from '@/helpers';
export default {
props: {
wordEntity: {
type: Object,
required: true,
},
},
data: () => ({
snackbar: {
isEnabled: false,
text: null,
},
}),
computed: {
...mapGetters(['userData', 'getProcessing']),
isWord() {
return this.wordEntity.type === WORD_TYPES.WORD;
},
isRedewndung() {
return this.wordEntity.type === WORD_TYPES.REDEWNNDUNG;
},
isProcessing() {
return this.getProcessing;
},
},
methods: {
getFullOriginalWord,
addWord(entity) {
const userWords = this.userData.words;
const wordAdded = userWords[entity.key];
if (wordAdded) {
this.snackbar.isEnabled = true;
this.snackbar.text = 'Слово уже было добавлено';
return;
}
if (Object.keys(userWords).length > 100) {
this.snackbar.isEnabled = true;
this.snackbar.text = 'Слишком много добавленных слов';
return;
}
this.$store.dispatch('addUserWord', entity);
},
},
};
</script>

View File

@@ -13,40 +13,43 @@
slot-scope="props"
xs12
sm6
md4
lg3
>
<v-card>
<v-card-title>
<h4>{{ props.item.origWord }}</h4>
</v-card-title>
<v-divider></v-divider>
<v-card-text>
{{ props.item.transWord }}
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn fab dark small color="primary">
<v-icon class="d-flex" dark>add</v-icon>
</v-btn>
</v-card-actions>
</v-card>
<word-card :wordEntity="props.item"></word-card>
</v-flex>
</v-data-iterator>
</v-container>
</template>
<script>
import WordCard from '@/components/Article/Word/Card';
export default {
props: {
words: {
type: Array,
data: {
type: Object,
required: true,
},
},
computed: {
userData() {
return this.$store.getters.userData;
},
words() {
const words = [];
for (let property in this.data) {
if (this.data.hasOwnProperty(property)) {
const word = Object.assign({}, this.data[property], { key: property });
words.push(word);
}
}
return words;
},
},
components: {
WordCard,
},
}
</script>
<style>
</style>

View File

@@ -1,3 +1,4 @@
export * from '@/helpers/article';
export * from '@/helpers/utils';
export * from '@/helpers/formRules';
export * from '@/helpers/utils';
export * from '@/helpers/word';

14
src/helpers/word.js Normal file
View File

@@ -0,0 +1,14 @@
export const getFullOriginalWord = (entity) => {
const { origText } = entity;
if (entity.origPrefix) {
return `${entity.origPrefix} ${origText}`;
}
return origText;
};
export const WORD_TYPES = {
WORD: 1,
REDEWNNDUNG: 2,
};

View File

@@ -29,12 +29,12 @@ export default {
}
},
actions: {
signUp({ commit }, payload) {
commit('setProcessing', true);
async signUp({ commit }, payload) {
await commit('setProcessing', true);
commit('clearError');
const { email, password, name } = payload;
firebase.auth()
await firebase.auth()
.createUserWithEmailAndPassword(email, password)
.then(() => {
firebase.auth().currentUser
@@ -42,29 +42,29 @@ export default {
displayName: name,
})
.then(() => commit('setUserName', name));
commit('setProcessing', false);
})
.catch(function(error) {
const { message } = error;
commit('setProcessing', false);
commit('setError', message);
});
await commit('setProcessing', false);
},
signIn({ commit }, payload) {
commit('setProcessing', true);
async signIn({ commit }, payload) {
await commit('setProcessing', true);
commit('clearError');
const { email, password } = payload;
firebase.auth()
await firebase.auth()
.signInWithEmailAndPassword(email, password)
.then(() => {
commit('setProcessing', false);
})
.catch(function(error) {
const { message } = error;
commit('setProcessing', false);
commit('setError', message);
});
await commit('setProcessing', false);
},
signOut() {
firebase.auth().signOut();
@@ -78,14 +78,14 @@ export default {
commit('unSetUser');
}
},
changeUserProfileData({ commit }, payload) {
async changeUserProfileData({ commit }, payload) {
const { email, password } = payload;
const credential = firebase.auth.EmailAuthProvider.credential(email, password);
commit('setProcessing', true);
await commit('setProcessing', true);
commit('clearError');
firebase.auth().currentUser.reauthenticateAndRetrieveDataWithCredential(credential)
await firebase.auth().currentUser.reauthenticateAndRetrieveDataWithCredential(credential)
.then(() => {
const { changeType } = payload;
const reauthenticatedUser = firebase.auth().currentUser;
@@ -111,14 +111,13 @@ export default {
throw Error('invalid change type');
})
.then(() => commit('setProcessing', false))
.catch((e) => {
window.console.error(e);
const { message } = e;
commit('setProcessing', false);
commit('setError', message);
});
await commit('setProcessing', false);
},
},
getters: {

View File

@@ -10,11 +10,11 @@ export default {
userData: defaultUserData,
},
actions: {
loadUserData({ commit }, userId) {
commit('setProcessing', true);
async loadUserData({ commit }, userId) {
await commit('setProcessing', true);
let userDataRef = Vue.$db.collection('userData').doc(userId);
userDataRef.get()
await userDataRef.get()
.then((data) => {
let userData = data.exists ? data.data() : defaultUserData;
@@ -22,14 +22,18 @@ export default {
userData.articles = {};
}
if (!userData.words) {
userData.words = {};
}
commit('setUserData', userData);
})
.catch(e => window.console.error(e));
commit('setProcessing', false);
await commit('setProcessing', false);
},
addUserArticle({ commit, getters }, articleId) {
commit('setProcessing', true);
async addUserArticle({ commit, getters }, articleId) {
await commit('setProcessing', true);
const userDataRef = Vue.$db.collection('userData').doc(getters.userId);
const article = {
@@ -37,7 +41,7 @@ export default {
parts: {},
};
userDataRef.set({
await userDataRef.set({
articles: {
[articleId]: article,
}
@@ -45,7 +49,45 @@ export default {
.then(() => commit('addUserArticle', { articleId, article }))
.catch(e => window.console.error(e));
commit('setProcessing', false);
await commit('setProcessing', false);
},
async addUserWord({ commit, getters }, wordEntity) {
await commit('setProcessing', true);
const { key, ...wordEntityData } = wordEntity;
const userDataRef = Vue.$db.collection('userData').doc(getters.userId);
const word = Object.assign({}, wordEntityData, {
addedAt: new Date(),
// about this {@see https://en.wikipedia.org/wiki/Leitner_system}
bucket: 1,
nextShowDate: new Date(),
});
await userDataRef.set({
words: {
[key]: word,
}
}, { merge: true })
.then(() => commit('addUserWord', { wordKey: key, word }))
.catch(e => window.console.error(e));
await commit('setProcessing', false);
},
async finishUserArticlePart({ commit, getters }, { articleId, partId, rating }) {
await commit('setProcessing', true);
const userDataRef = Vue.$db.collection('userData').doc(getters.userId);
const timestamp = new Date();
await userDataRef.update({
[`articles.${articleId}.parts.${partId}.finishedAt`]: timestamp,
[`articles.${articleId}.parts.${partId}.rating`]: rating,
})
.then(() => commit('finishUserArticlePart', { articleId, partId, timestamp, rating }))
.catch(e => window.console.error(e));
await commit('setProcessing', false);
},
updateUserArticlePartStats({ commit, getters }, { articleId, partId }) {
const userDataRef = Vue.$db.collection('userData').doc(getters.userId);
@@ -66,21 +108,6 @@ export default {
.then(() => commit('openUserArticlePart', { articleId, partId, timestamp }))
.catch(e => window.console.error(e));
},
finishUserArticlePart({ commit, getters }, { articleId, partId, rating }) {
commit('setProcessing', true);
const userDataRef = Vue.$db.collection('userData').doc(getters.userId);
const timestamp = new Date();
userDataRef.update({
[`articles.${articleId}.parts.${partId}.finishedAt`]: timestamp,
[`articles.${articleId}.parts.${partId}.rating`]: rating,
})
.then(() => commit('finishUserArticlePart', { articleId, partId, timestamp, rating }))
.catch(e => window.console.error(e));
commit('setProcessing', false);
}
},
mutations: {
setUserData(state, payload) {
@@ -97,6 +124,9 @@ export default {
Vue.set(state.userData.articles[articleId].parts, partId, { addedAt: timestamp });
},
addUserWord(state, { wordKey, word }) {
Vue.set(state.userData.words, wordKey, word);
},
openUserArticlePart(state, { articleId, partId, timestamp }) {
Vue.set(state.userData.articles[articleId].parts[partId], 'lastOpenedAt', timestamp);
},

View File

@@ -21,7 +21,7 @@
</v-flex>
<v-flex xs12 sm10 offset-sm1>
<book-part-words :words="part.words"></book-part-words>
<book-part-words :data="part.words"></book-part-words>
</v-flex>
<v-flex xs12 sm10 offset-sm1 class="text-xs-center">
@@ -105,7 +105,7 @@
storedRating() {
const articlePart = this.currentUserArticlePart;
return articlePart ? articlePart.rating : 0;
}
},
},
methods: {
finishWork() {