diff --git a/frontend/src/client/quasar.conf.js b/frontend/src/client/quasar.conf.js
index 3f4b482..9f78536 100644
--- a/frontend/src/client/quasar.conf.js
+++ b/frontend/src/client/quasar.conf.js
@@ -102,9 +102,8 @@ module.exports = function (ctx) {
short_name: 'Blue Eel',
description: 'Learn to read',
display: 'standalone',
- orientation: 'portrait',
- background_color: '#81d4fa',
- theme_color: '#b3e5fc',
+ background_color: '#178CA4',
+ theme_color: '#178CA4',
icons: [
{
'src': 'statics/icons/icon-128x128.png',
diff --git a/frontend/src/client/src/components/EelCanvas.vue b/frontend/src/client/src/components/EelCanvas.vue
index 6f3a7c9..274da41 100644
--- a/frontend/src/client/src/components/EelCanvas.vue
+++ b/frontend/src/client/src/components/EelCanvas.vue
@@ -1,8 +1,21 @@
-
+
diff --git a/frontend/src/client/src/pages/Index.vue b/frontend/src/client/src/pages/Index.vue
index 79a4663..ef762a1 100644
--- a/frontend/src/client/src/pages/Index.vue
+++ b/frontend/src/client/src/pages/Index.vue
@@ -10,7 +10,7 @@
Writing made simple!
-
+
diff --git a/frontend/src/client/src/pages/Letter.vue b/frontend/src/client/src/pages/Letter.vue
new file mode 100644
index 0000000..92de826
--- /dev/null
+++ b/frontend/src/client/src/pages/Letter.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/client/src/pages/Pattern.vue b/frontend/src/client/src/pages/Pattern.vue
deleted file mode 100644
index eb8bb67..0000000
--- a/frontend/src/client/src/pages/Pattern.vue
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
diff --git a/frontend/src/client/src/pages/Tracing.vue b/frontend/src/client/src/pages/Tracing.vue
deleted file mode 100644
index 0881fa7..0000000
--- a/frontend/src/client/src/pages/Tracing.vue
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
diff --git a/frontend/src/client/src/pages/Word.vue b/frontend/src/client/src/pages/Word.vue
index 2e688c1..dd31705 100644
--- a/frontend/src/client/src/pages/Word.vue
+++ b/frontend/src/client/src/pages/Word.vue
@@ -1,6 +1,6 @@
-
-
+
+
@@ -11,6 +11,9 @@ export default {
name: 'PageWord',
components: {
EelPractice
+ },
+ created: function () {
+ this.$store.dispatch('common/changeLevel', 'word')
}
}
diff --git a/frontend/src/client/src/router/routes.js b/frontend/src/client/src/router/routes.js
index 2aa3ab6..0daaabb 100644
--- a/frontend/src/client/src/router/routes.js
+++ b/frontend/src/client/src/router/routes.js
@@ -4,9 +4,7 @@ const routes = [
component: () => import('layouts/MyLayout.vue'),
children: [
{ name: 'home', path: '', component: () => import('pages/Index.vue') },
- { name: 'tracing', path: 'tracing', component: () => import('pages/Tracing.vue') },
- { name: 'pattern', path: 'pattern', component: () => import('pages/Pattern.vue') },
- { name: 'freeform', path: 'freeform', component: () => import('pages/Freeform.vue') },
+ { name: 'letter', path: 'letter', component: () => import('pages/Letter.vue') },
{ name: 'word', path: 'word', component: () => import('pages/Word.vue') },
{ name: 'congratulations', path: 'congratulations', component: () => import('pages/Congratulations.vue') }
]
diff --git a/frontend/src/client/src/store/common/actions.js b/frontend/src/client/src/store/common/actions.js
index e5182ec..4bcd730 100644
--- a/frontend/src/client/src/store/common/actions.js
+++ b/frontend/src/client/src/store/common/actions.js
@@ -1,5 +1,7 @@
import { axios } from 'boot/axios'
+const STALE_REINTRODUCE_COUNT = 2
+
// //////////////////
// FETCH FROM CLOUD
// //////////////////
@@ -7,14 +9,27 @@ export function fetchLetter (ctx, letter) {
if (ctx.state.patterns[letter] == null) {
axios.get(`https://eel3-data.s3.us-east-2.amazonaws.com/patterns/${letter}/master.json`)
.then(res => {
- ctx.commit('setPattern', res.data)
+ let pattern = {
+ boundary: res.data.boundary || {
+ top: 0,
+ ascenderLine: res.data.dimensions.upperGuidePixels,
+ capLine: res.data.dimensions.upperGuidePixels,
+ meanLine: res.data.dimensions.middleGuidePixels,
+ baseLine: res.data.dimensions.lowerGuidePixels,
+ beardLine: res.data.dimensions.lowestGuidePixels,
+ bottom: res.data.dimensions.heightPixels
+ },
+ letter: res.data.letter,
+ path: res.data.path
+ }
+ ctx.commit('setPattern', pattern)
}).catch(err => err) // TODO: Properly catch error
}
}
export function fetchSequence (ctx) {
let sequence = {
- letters: [
+ expressions: [
['b', 'c', 'd', 'f'],
['g', 'h', 'l', 'r', 's', 't'],
['a', 'e', 'i', 'o', 'u'],
@@ -23,12 +38,12 @@ export function fetchSequence (ctx) {
['q', 'w', 'x', 'y', 'z']
],
words: [
- ['feature', 'coming', 'soon']
+ ['hi', 'have', 'fun']
]
}
- if (ctx.state.user != null && ctx.state.user.sequenceId != null && ctx.state.user.sequenceId === 'Tm9haCBH') {
+ if (ctx.state.user != null && ctx.state.user.sequence != null && ctx.state.user.sequence === 'Tm9haCBH') {
sequence = {
- letters: [
+ expressions: [
['n', 't', 'm', 'f'],
['i', 'a', 'c']
],
@@ -38,11 +53,22 @@ export function fetchSequence (ctx) {
]
}
}
- if (ctx.state.user != null && ctx.state.user.sequenceId != null && ctx.state.user.sequenceId === 'jim') {
+ if (ctx.state.user != null && ctx.state.user.sequence != null && ctx.state.user.sequence === 'QnJlbmRh') {
+ sequence = {
+ expressions: [
+ ['n', 'b', 's', 'r', 'k', 'e', 'p']
+ ],
+ words: [
+ ['trains', 'are', 'cool'],
+ ['planes', 'fly', 'high']
+ ]
+ }
+ }
+ if (ctx.state.user != null && ctx.state.user.sequence != null && ctx.state.user.sequence === 'jim') {
sequence = {
- letters: [
- ['a', 'b'],
- ['c', 'd']
+ expressions: [
+ ['j', 'i', 'm'],
+ ['f']
],
words: [
['jim', 'rocks']
@@ -52,16 +78,32 @@ export function fetchSequence (ctx) {
ctx.commit('setStabilizeCount', 1)
ctx.commit('setReintroduceCount', 1)
}
+ for (let i = 0; i < sequence.expressions.length; i++) {
+ let bunch = sequence.expressions[i]
+ for (let j = 0; j < bunch.length; j++) {
+ ctx.dispatch('fetchLetter', bunch[j])
+ }
+ }
+ for (let i = 0; i < sequence.words.length; i++) {
+ let bunch = sequence.words[i]
+ for (let j = 0; j < bunch.length; j++) {
+ let word = bunch[j]
+ for (let z = 0; z < word.length; z++) {
+ ctx.dispatch('fetchLetter', word[j])
+ }
+ }
+ }
ctx.commit('setSequence', sequence)
+ ctx.commit('resetState', ctx.state.level)
}
// //////////////////
// UPLOAD TO CLOUD
// //////////////////
export function uploadPractice (ctx, update) {
- let sequenceId = ctx.state.user.sequenceId
- if (sequenceId == null || sequenceId === '') {
- sequenceId = 'Standard'
+ let sequence = ctx.state.user.sequence
+ if (sequence == null || sequence === '') {
+ return
}
let data = {
@@ -78,7 +120,7 @@ export function uploadPractice (ctx, update) {
}
}
let filename = `${timestamp}.json`
- axios.put(`https://eel3-data.s3.us-east-2.amazonaws.com/practice/${sequenceId}/${filename}`, data, config)
+ axios.put(`https://eel3-data.s3.us-east-2.amazonaws.com/practice/${sequence}/${filename}`, data, config)
}
// //////////////////
@@ -89,66 +131,89 @@ export function loginUser (ctx, user) {
ctx.dispatch('fetchSequence')
}
-export function startPractice (ctx, level) {
- ctx.commit('resetState', level)
- ctx.dispatch('nextLetter')
+export function changeLevel (ctx, level) {
+ ctx.commit('setLevel', level)
+}
+
+export function startPractice (ctx) {
+ ctx.commit('resetState')
+ ctx.dispatch('nextExpression')
}
export function practiceAttempted (ctx, update) {
- ctx.commit('incrementAttempts', update.letter)
+ ctx.commit('incrementAttempts', update.expression)
if (ctx.state.user.uploading) {
+ update.technique = ctx.state.user.technique
+ update.level = ctx.state.level
ctx.dispatch('uploadPractice', update)
}
if (update.success) {
- ctx.commit('recordSuccess', update.letter)
- ctx.dispatch('nextLetter')
- ctx.dispatch('resetLetter', update.letter)
- } else if (ctx.state.history[update.letter].attempts >= ctx.state.retryLimit) {
- ctx.dispatch('nextLetter')
- ctx.dispatch('resetLetter', update.letter)
- }
- if (ctx.state.history[update.letter].singleAttemptSuccesses >= ctx.state.stabilizeCount) {
- ctx.dispatch('stabilizeLetter', update.letter)
+ ctx.commit('recordSuccess', update.expression)
+ ctx.commit('resetFail')
+ if (ctx.state.history[update.expression].singleAttemptSuccesses >= ctx.state.stabilizeCount) {
+ ctx.commit('stabilizeExpression', update.expression)
+ }
+ ctx.dispatch('nextExpression')
+ ctx.commit('resetExpression', update.expression)
+ } else if (ctx.state.history[update.expression].attempts >= ctx.state.retryLimit) {
+ ctx.commit('incrementFail')
+ if (ctx.state.consecutiveFails >= ctx.state.activeQueue.length) {
+ ctx.dispatch('staleFail')
+ }
+ ctx.dispatch('nextExpression')
+ ctx.commit('resetExpression', update.expression)
}
}
-export function nextLetter (ctx) {
- if (ctx.state.activeQueue.length < 2 && ctx.state.pendingQueue.length > 0) {
- ctx.dispatch('activateLetters')
- ctx.dispatch('nextLetter')
- } else {
- ctx.commit('nextLetter')
+export function nextExpression (ctx) {
+ if (ctx.state.activeQueue.length < 1) {
+ ctx.dispatch('activateExpressions')
}
+ ctx.commit('nextExpression')
}
-export function resetLetter (ctx, letter) {
- ctx.commit('resetLetter', letter)
-}
+export function activateExpressions (ctx) {
+ let countToReintroduce = 0
-export function stabilizeLetter (ctx, letter) {
- ctx.commit('stabilizeLetter', letter)
-}
+ if (ctx.state.pendingQueue.length > 0) {
+ ctx.state.isFinalReview = false
+ ctx.commit('activateNextBunch')
+ countToReintroduce = Math.min(ctx.state.reintroduceCount, ctx.state.stableQueue.length)
+ } else if (!ctx.state.isFinalReview) {
+ ctx.state.isFinalReview = true
+ countToReintroduce = ctx.state.stableQueue.length
+ }
-export function activateLetters (ctx) {
- let countToAdd = Math.min(ctx.state.reintroduceCount, ctx.state.stableQueue.length)
- for (let i = 0; i < countToAdd; i++) {
- const newLetter = ctx.state.stableQueue[0]
- ctx.dispatch('fetchLetter', newLetter)
- ctx.commit('reintroduceLetter', newLetter)
- }
- for (let i = 0; i < ctx.state.pendingQueue[0].length; i++) {
- const newLetter = ctx.state.pendingQueue[0][i]
- for (let j = 0; j < newLetter.length; j++) {
- ctx.dispatch('fetchLetter', newLetter[j])
- }
+ for (let i = 0; i < countToReintroduce; i++) {
+ const newExpression = ctx.state.stableQueue[0]
+ ctx.commit('reintroduceExpression', newExpression)
}
- ctx.commit('activateNextBunch')
}
-export function lowActive (ctx, level) {
-
+export function staleFail (ctx) {
+ if (ctx.state.staleFails > (ctx.state.stableQueue.length / STALE_REINTRODUCE_COUNT)) {
+ ctx.dispatch('activateExpressions')
+ ctx.commit('resetStaleFail')
+ } else if (ctx.state.stableQueue.length >= STALE_REINTRODUCE_COUNT) {
+ for (let i = 0; i < STALE_REINTRODUCE_COUNT; i++) {
+ const newExpression = ctx.state.stableQueue[0]
+ ctx.commit('reintroduceExpression', newExpression)
+ }
+ } else {
+ ctx.dispatch('activateExpressions')
+ }
+ ctx.commit('recordStaleFail')
+ ctx.commit('resetFail')
}
-export function staleFail (ctx, level) {
-
+export function completedTechnique (ctx) {
+ if (ctx.state.user.technique === 'Tracing') {
+ ctx.commit('setTechnique', 'Pattern')
+ } else if (this.level === 'pattern') {
+ ctx.commit('setTechnique', 'Freeform')
+ } else {
+ ctx.commit('setTechnique', 'Tracing')
+ this.$router.push({ name: 'congratulations' })
+ }
+ ctx.dispatch('startPractice')
}
diff --git a/frontend/src/client/src/store/common/mutations.js b/frontend/src/client/src/store/common/mutations.js
index d61a1fe..332052c 100644
--- a/frontend/src/client/src/store/common/mutations.js
+++ b/frontend/src/client/src/store/common/mutations.js
@@ -8,6 +8,14 @@ export function setSequence (state, sequence) {
state.sequence = sequence
}
+export function setLevel (state, level) {
+ state.level = level
+}
+
+export function setTechnique (state, technique) {
+ Vue.set(state.user, 'technique', technique)
+}
+
export function setRetryLimit (state, limit) {
state.retryLimit = limit
}
@@ -24,73 +32,91 @@ export function setPattern (state, pattern) {
Vue.set(state.patterns, pattern.letter, pattern)
}
-export function resetLetter (state, letter) {
- let letterHistory = state.history[letter] || {}
- letterHistory.previousAttempts = letterHistory.attempts || 0
- if (letterHistory.previousAttempts > 1) {
- letterHistory.totalSingleAttemptSuccesses = 0
+export function resetExpression (state, expression) {
+ let expressionHistory = state.history[expression] || {}
+ expressionHistory.previousAttempts = expressionHistory.attempts || 0
+ if (expressionHistory.previousAttempts > 1) {
+ expressionHistory.totalSingleAttemptSuccesses = 0
}
- letterHistory.attempts = 0
- Vue.set(state.history, letter, letterHistory)
+ expressionHistory.attempts = 0
+ Vue.set(state.history, expression, expressionHistory)
}
-export function incrementAttempts (state, letter) {
- let letterHistory = state.history[letter] || {}
- letterHistory.attempts = letterHistory.attempts + 1 || 1
- letterHistory.totalAttempts = letterHistory.totalAttempts + 1 || 1
- Vue.set(state.history, letter, letterHistory)
+export function incrementAttempts (state, expression) {
+ let expressionHistory = state.history[expression] || {}
+ expressionHistory.attempts = expressionHistory.attempts + 1 || 1
+ expressionHistory.totalAttempts = expressionHistory.totalAttempts + 1 || 1
+ Vue.set(state.history, expression, expressionHistory)
}
-export function recordSuccess (state, letter) {
- let letterHistory = state.history[letter] || {}
- letterHistory.success = true
- if (letterHistory.attempts === 1) {
- letterHistory.singleAttemptSuccesses = letterHistory.singleAttemptSuccesses + 1 || 1
- letterHistory.totalSingleAttemptSuccesses = letterHistory.totalSingleAttemptSuccesses + 1 || 1
+export function incrementFail (state) {
+ state.consecutiveFails = state.consecutiveFails + 1 || 1
+}
+
+export function resetFail (state) {
+ state.consecutiveFails = 0
+}
+
+export function recordStaleFail (state) {
+ state.staleFails = state.staleFails + 1 || 1
+}
+
+export function resetStaleFail (state) {
+ state.staleFails = 0
+}
+
+export function recordSuccess (state, expression) {
+ let expressionHistory = state.history[expression] || {}
+ expressionHistory.success = true
+ if (expressionHistory.attempts === 1) {
+ expressionHistory.singleAttemptSuccesses = expressionHistory.singleAttemptSuccesses + 1 || 1
+ expressionHistory.totalSingleAttemptSuccesses = expressionHistory.totalSingleAttemptSuccesses + 1 || 1
+ if (expressionHistory.totalAttempts === 1) { // Bonus for getting it right on your very first try
+ expressionHistory.singleAttemptSuccesses = expressionHistory.singleAttemptSuccesses + 1
+ expressionHistory.totalSingleAttemptSuccesses = expressionHistory.totalSingleAttemptSuccesses + 1
+ }
}
- Vue.set(state.history, letter, letterHistory)
+ Vue.set(state.history, expression, expressionHistory)
}
-export function nextLetter (state) {
+export function nextExpression (state) {
let next = state.activeQueue.shift()
- if (state.letter != null && state.letter !== '') {
- state.activeQueue.push(state.letter)
- }
- state.letter = next
+ state.expression = next
+ state.activeQueue.push(next)
}
-export function stabilizeLetter (state, letter) {
- var index = state.activeQueue.indexOf(letter)
+export function stabilizeExpression (state, expression) {
+ var index = state.activeQueue.indexOf(expression)
if (index !== -1) {
state.activeQueue.splice(index, 1)
}
- state.stableQueue.push(letter)
+ state.stableQueue.push(expression)
}
export function activateNextBunch (state) {
let nextBunch = state.pendingQueue.shift()
while (nextBunch.length > 0) {
- let letter = nextBunch.shift()
- state.activeQueue.push(letter)
+ let expression = nextBunch.shift()
+ state.activeQueue.push(expression)
}
}
-export function reintroduceLetter (state, letter) {
- var index = state.stableQueue.indexOf(letter)
+export function reintroduceExpression (state, expression) {
+ var index = state.stableQueue.indexOf(expression)
if (index !== -1) {
state.stableQueue.splice(index, 1)
}
- state.activeQueue.push(letter)
+ state.activeQueue.push(expression)
}
-export function resetState (state, level) {
+export function resetState (state) {
state.history = {}
- state.letter = ''
+ state.expression = ''
state.activeQueue = []
state.stableQueue = []
- if (level === 'word') {
+ if (state.level === 'word') {
state.pendingQueue = Array.from(state.sequence.words, block => Array.from(block, word => word))
} else {
- state.pendingQueue = Array.from(state.sequence.letters, block => Array.from(block, letter => letter))
+ state.pendingQueue = Array.from(state.sequence.expressions, block => Array.from(block, expression => expression))
}
}
diff --git a/frontend/src/client/src/store/common/state.js b/frontend/src/client/src/store/common/state.js
index 4c03c8a..56a8ca5 100644
--- a/frontend/src/client/src/store/common/state.js
+++ b/frontend/src/client/src/store/common/state.js
@@ -1,17 +1,21 @@
export default {
- version: '1.0.2.0',
+ version: '1.1.0.9',
patternsLoading: true,
patterns: {},
history: {},
- letter: '',
+ expression: '',
+ consecutiveFails: 0,
+ staleFails: 0,
activeQueue: [],
stableQueue: [],
pendingQueue: [],
+ level: '',
sequence: {},
user: {
- sequenceId: 'Standard',
- uploading: false,
- name: ''
+ sequence: '',
+ uploading: true,
+ name: '',
+ technique: 'Tracing'
},
retryLimit: 3,
stabilizeCount: 3,