WIP on comp API (#37)

approaching the end of this task
This commit is contained in:
Daniel J. Summers 2019-11-27 20:55:51 -06:00
parent 321ba83ab0
commit 9d44c90de6
16 changed files with 532 additions and 347 deletions

View File

@ -1 +0,0 @@
[{"tagName":"script","closeTag":true,"attributes":{"type":"text/javascript","src":"/js/chunk-vendors-legacy.12c801f9.js"}},{"tagName":"script","closeTag":true,"attributes":{"type":"text/javascript","src":"/js/app-legacy.3c452240.js"}}]

View File

@ -112,9 +112,7 @@ const ProgressSymbol = Symbol('Progress events')
export default createComponent({
name: 'app',
components: {
Navigation
},
components: { Navigation },
setup () {
const pkg = require('../package.json')

View File

@ -1,9 +1,8 @@
<script lang="ts">
import { computed, createElement, onBeforeUnmount, onMounted, ref } from '@vue/composition-api'
import { computed, createComponent, createElement, onBeforeUnmount, onMounted, ref } from '@vue/composition-api'
import moment from 'moment'
export default {
name: 'date-from-now',
export default createComponent({
props: {
tag: {
type: String,
@ -18,7 +17,7 @@ export default {
default: 10000
}
},
setup (props: any) {
setup (props) {
/** Interval ID for updating relative time */
let intervalId: number = 0
@ -47,5 +46,5 @@ export default {
}
})
}
}
})
</script>

View File

@ -27,7 +27,7 @@
</template>
<script lang="ts">
import { computed } from '@vue/composition-api'
import { computed, createComponent } from '@vue/composition-api'
import { Store } from 'vuex' // eslint-disable-line no-unused-vars
import { AppState } from '../../store/types' // eslint-disable-line no-unused-vars
@ -36,7 +36,7 @@ import { useAuth } from '../../plugins/auth'
import { useRouter } from '../../plugins/router'
import { useStore } from '../../plugins/store'
export default {
export default createComponent({
setup () {
/** The Vuex store */
const store = useStore() as Store<AppState>
@ -72,5 +72,5 @@ export default {
showHelp
}
}
}
})
</script>

View File

@ -4,9 +4,9 @@ h1(v-if='!hideOnPage'
</template>
<script lang="ts">
import { watch } from '@vue/composition-api'
import { createComponent, watch, ref } from '@vue/composition-api'
export default {
export default createComponent({
props: {
title: {
type: String,
@ -17,11 +17,11 @@ export default {
default: false
}
},
setup (props: any) {
watch(props.title, (title, prevTitle) => {
setup (props) {
watch(ref(props.title), (title: string, prevTitle: string) => {
document.title = `${props.title.replace('&rsquo;', "'")} « myPrayerJournal`
})
return { }
}
}
})
</script>

View File

@ -14,7 +14,7 @@ md-content(role='main').mpj-main-content
</template>
<script lang="ts">
import { ref, onMounted } from '@vue/composition-api'
import { createComponent, onMounted, ref } from '@vue/composition-api'
import RequestList from './RequestList.vue'
@ -22,7 +22,7 @@ import api from '../../api'
import { JournalRequest } from '../../store/types' // eslint-disable-line no-unused-vars
import { useProgress, useSnackbar } from '../../App.vue'
export default {
export default createComponent({
components: {
RequestList
},
@ -59,5 +59,5 @@ export default {
isLoaded
}
}
}
})
</script>

View File

@ -1,6 +1,6 @@
<template lang="pug">
md-content(role='main').mpj-narrow
page-title(:title='title')
page-title(:title='title.value')
md-field
label(for='request_text') Prayer Request
md-textarea(v-model='form.requestText'
@ -8,7 +8,7 @@ md-content(role='main').mpj-narrow
md-autogrow
autofocus).mpj-full-width
br
template(v-if='!isNew')
template(v-if='!isNew.value')
label Also Mark As
br
md-radio(v-model='form.status'
@ -34,141 +34,191 @@ md-content(role='main').mpj-narrow
label Count
md-input(v-model='form.recur.count'
type='number'
:disabled='!showRecurrence')
:disabled='!showRecurrence.value')
.md-layout-item.md-size-20
md-field
label Interval
md-select(v-model='form.recur.other'
:disabled='!showRecurrence')
:disabled='!showRecurrence.value')
md-option(value='Hours') hours
md-option(value='Days') days
md-option(value='Weeks') weeks
.mpj-text-right
md-button(:disabled='!isValidRecurrence'
md-button(:disabled='!isValidRecurrence.value'
@click.stop='saveRequest()').md-primary.md-raised #[md-icon save] Save
md-button(@click.stop='goBack()').md-raised #[md-icon arrow_back] Cancel
</template>
<script>
'use strict'
<script lang="ts">
import { createComponent, ref, computed, onMounted } from '@vue/composition-api'
import { Store } from 'vuex' // eslint-disable-line no-unused-vars
import { mapState } from 'vuex'
import { Actions, AppState, AddRequestAction, UpdateRequestAction } from '../../store/types' // eslint-disable-line no-unused-vars
import { useProgress, useSnackbar } from '../../App.vue'
import { useStore } from '../../plugins/store'
import { useRouter } from '../../plugins/router'
import { Actions } from '@/store/types'
/** The recurrence settings for the request */
class RecurrenceForm {
/** The type of recurrence */
typ = 'Immediate'
export default {
name: 'edit-request',
inject: [
'messages',
'progress'
],
/** The type of recurrence (other than immediate) */
other = ''
/** The count of non-immediate intervals */
count = ''
/**
* The recurrence represented by the given form
* @param x The recurrence form
*/
static recurrence = (x: RecurrenceForm) => x.typ === 'Immediate' ? 'Immediate' : x.other
/**
* The interval represented by the given form
* @param x The recurrence form
*/
static interval = (x: RecurrenceForm) => x.typ === 'Immediate' ? 0 : Number.parseInt(x.count)
}
/** The form for editing the request */
class EditForm {
/** The ID of the request */
requestId = ''
/** The text of the request */
requestText = ''
/** The status associated with this update */
status = 'Updated'
/** The recurrence for the request */
recur = new RecurrenceForm()
}
export default createComponent({
props: {
id: {
type: String,
required: true
}
},
data () {
return {
title: 'Edit Prayer Request',
isNew: false,
form: {
requestId: '',
requestText: '',
status: 'Updated',
recur: {
typ: 'Immediate',
other: '',
count: ''
}
}
}
},
computed: {
isValidRecurrence () {
if (this.form.recur.typ === 'Immediate') return true
const count = Number.parseInt(this.form.recur.count)
if (isNaN(count) || this.form.recur.other === '') return false
if (this.form.recur.other === 'Hours' && count > (365 * 24)) return false
if (this.form.recur.other === 'Days' && count > 365) return false
if (this.form.recur.other === 'Weeks' && count > 52) return false
setup (props) {
/** The Vuex store */
const store = useStore() as Store<AppState>
/** The snackbar component properties */
const snackbar = useSnackbar()
/** The progress bar component properties */
const progress = useProgress()
/** The application router */
const router = useRouter()
/** The page title */
const title = ref('Edit Prayer Request')
/** Whether this is a new request */
const isNew = ref(false)
/** The input form */
const form = new EditForm()
/** Is the selected recurrence a valid recurrence? */
const isValidRecurrence = computed(() => {
if (form.recur.typ === 'Immediate') return true
const count = Number.parseInt(form.recur.count)
if (isNaN(count) || form.recur.other === '') return false
if (form.recur.other === 'Hours' && count > (365 * 24)) return false
if (form.recur.other === 'Days' && count > 365) return false
if (form.recur.other === 'Weeks' && count > 52) return false
return true
},
showRecurrence () {
return this.form.recur.typ !== 'Immediate'
},
...mapState(['journal'])
},
async mounted () {
await this.ensureJournal()
if (this.id === 'new') {
this.title = 'Add Prayer Request'
this.isNew = true
this.form.requestId = ''
this.form.requestText = ''
this.form.status = 'Created'
this.form.recur.typ = 'Immediate'
this.form.recur.other = ''
this.form.recur.count = ''
} else {
this.title = 'Edit Prayer Request'
this.isNew = false
if (this.journal.length === 0) {
await this.$store.dispatch(Actions.LoadJournal, this.progress)
})
/** Whether the recurrence should be shown */
const showRecurrence = computed(() => form.recur.typ !== 'Immediate')
/** Go back 1 in browser history */
const goBack = () => { router.go(-1) }
/** Trim the request text */
const trimText = () => { form.requestText = form.requestText.trim() }
/** Save the edited request */
const saveRequest = async () => {
if (isNew.value) {
const opts: AddRequestAction = {
progress,
requestText: form.requestText,
recurType: RecurrenceForm.recurrence(form.recur),
recurCount: RecurrenceForm.interval(form.recur)
}
const req = this.journal.filter(r => r.requestId === this.id)[0]
this.form.requestId = this.id
this.form.requestText = req.text
this.form.status = 'Updated'
await store.dispatch(Actions.AddRequest, opts)
snackbar.events.$emit('info', 'New prayer request added')
} else {
const opts: UpdateRequestAction = {
progress,
requestId: form.requestId,
updateText: form.requestText,
status: form.status,
recurType: RecurrenceForm.recurrence(form.recur),
recurCount: RecurrenceForm.interval(form.recur)
}
await store.dispatch(Actions.UpdateRequest, opts)
if (form.status === 'Answered') {
snackbar.events.$emit('info', 'Request updated and removed from active journal')
} else {
snackbar.events.$emit('info', 'Request updated')
}
}
goBack()
}
onMounted(async () => {
if (!Array.isArray(store.state.journal)) {
await store.dispatch(Actions.LoadJournal, progress)
}
if (props.id === 'new') {
title.value = 'Add Prayer Request'
isNew.value = true
form.requestId = ''
form.requestText = ''
form.status = 'Created'
form.recur.typ = 'Immediate'
form.recur.other = ''
form.recur.count = ''
} else {
title.value = 'Edit Prayer Request'
isNew.value = false
const req = store.state.journal.filter(r => r.requestId === props.id)[0]
form.requestId = props.id
form.requestText = req.text
form.status = 'Updated'
if (req.recurType === 'Immediate') {
this.form.recur.typ = 'Immediate'
this.form.recur.other = ''
this.form.recur.count = ''
form.recur.typ = 'Immediate'
form.recur.other = ''
form.recur.count = ''
} else {
this.form.recur.typ = 'other'
this.form.recur.other = req.recurType
this.form.recur.count = req.recurCount
form.recur.typ = 'other'
form.recur.other = req.recurType
form.recur.count = req.recurCount.toString()
}
}
},
methods: {
goBack () {
this.$router.go(-1)
},
trimText () {
this.form.requestText = this.form.requestText.trim()
},
async ensureJournal () {
if (!Array.isArray(this.journal)) {
await this.$store.dispatch(Actions.LoadJournal, this.progress)
}
},
async saveRequest () {
if (this.isNew) {
await this.$store.dispatch(Actions.AddRequest, {
progress: this.progress,
requestText: this.form.requestText,
recurType: this.form.recur.typ === 'Immediate' ? 'Immediate' : this.form.recur.other,
recurCount: this.form.recur.typ === 'Immediate' ? 0 : Number.parseInt(this.form.recur.count)
})
this.messages.$emit('info', 'New prayer request added')
} else {
await this.$store.dispatch(Actions.UpdateRequest, {
progress: this.progress,
requestId: this.form.requestId,
updateText: this.form.requestText,
status: this.form.status,
recurType: this.form.recur.typ === 'Immediate' ? 'Immediate' : this.form.recur.other,
recurCount: this.form.recur.typ === 'Immediate' ? 0 : Number.parseInt(this.form.recur.count)
return {
form,
goBack,
isNew,
isValidRecurrence,
journal: store.state.journal,
saveRequest,
showRecurrence,
title,
trimText
}
}
})
if (this.form.status === 'Answered') {
this.messages.$emit('info', 'Request updated and removed from active journal')
} else {
this.messages.$emit('info', 'Request updated')
}
}
this.goBack()
}
}
}
</script>

View File

@ -23,7 +23,7 @@ md-content(role='main').mpj-main-content
</template>
<script lang="ts">
import { createComponent, onMounted, computed } from '@vue/composition-api'
import { computed, createComponent, onMounted } from '@vue/composition-api'
import moment from 'moment'
import api from '../../api'
@ -45,13 +45,16 @@ export default createComponent({
const progress = useProgress()
/** The request being displayed */
let request: JournalRequest = undefined
let request = new JournalRequest()
/** The history entry where the request was marked as answered */
const answer = computed(() => request.history.find(hist => hist.status === 'Answered'))
/** Whether this request is answered */
const isAnswered = computed(() => request.history.find(hist => hist.status === 'Answered'))
const isAnswered = computed(() => answer.value !== undefined)
/** The date/time this request was answered */
const answered = computed(() => request.history.find(hist => hist.status === 'Answered').asOf)
const answered = computed(() => answer.value ? answer.value.asOf : undefined)
/** The last recorded text for the request */
const lastText = computed(() => request.history.filter(hist => hist.text).sort(asOfDesc)[0].text)
@ -68,8 +71,9 @@ export default createComponent({
/** The number of days this request [was|has been] open */
const openDays = computed(() => {
const asOf = isAnswered.value ? answered.value : Date.now()
return Math.floor((asOf - request.history.find(hist => hist.status === 'Created').asOf) / 1000 / 60 / 60 / 24)
const asOf = answered.value ? answered.value : Date.now()
return Math.floor(
(asOf - request.history.filter(hist => hist.status === 'Created')[0].asOf) / 1000 / 60 / 60 / 24)
})
/** How many times this request has been prayed for */
@ -82,7 +86,7 @@ export default createComponent({
progress.events.$emit('show', 'indeterminate')
try {
const req = await api.getFullRequest(props.id)
request = req.data
request = req.data as JournalRequest
} catch (e) {
console.log(e) // eslint-disable-line no-console
} finally {

View File

@ -11,7 +11,7 @@ md-dialog(:md-active.sync='notesVisible').mpj-note-dialog
md-button(@click='saveNotes()').md-primary #[md-icon save] Save
md-button(@click='closeDialog()') #[md-icon undo] Cancel
md-dialog-content(md-scrollbar='true').mpj-dialog-content
div(v-if='hasPriorNotes')
div(v-if='hasPriorNotes.value')
p.mpj-text-center: strong Prior Notes for This Request
.mpj-note-list
p(v-for='note in priorNotes'
@ -19,88 +19,125 @@ md-dialog(:md-active.sync='notesVisible').mpj-note-dialog
small.mpj-muted-text: date-from-now(:value='note.asOf')
br
span.mpj-request-text {{ note.notes }}
div(v-else-if='noPriorNotes').mpj-text-center.mpj-muted-text There are no prior notes for this request
div(v-else-if='noPriorNotes.value').mpj-text-center.mpj-muted-text There are no prior notes for this request
div(v-else).mpj-text-center
hr
md-button(@click='loadNotes()') #[md-icon cloud_download] Load Prior Notes
</template>
<script>
'use strict'
<script lang="ts">
import { computed, createComponent, ref } from '@vue/composition-api'
import api from '@/api'
import api from '../../api'
export default {
name: 'notes-edit',
inject: [
'journalEvents',
'messages',
'progress'
],
data () {
return {
notesVisible: false,
form: {
requestId: '',
notes: ''
},
priorNotes: [],
priorNotesLoaded: false
import { useEvents } from '../Journal.vue'
import { NotesEntry, JournalRequest } from '../../store/types' // eslint-disable-line no-unused-vars
import { useProgress, useSnackbar } from '../../App.vue'
/** The input form for the notes dialog */
class NotesForm {
/** The ID of the request */
requestId = ''
/** The actual notes */
notes = ''
}
},
computed: {
hasPriorNotes () {
return this.priorNotesLoaded && this.priorNotes.length > 0
},
noPriorNotes () {
return this.priorNotesLoaded && this.priorNotes.length === 0
/** The prior notes for this request */
class PriorNotes {
/** The prior notes */
notes: NotesEntry[] = []
/** Have the prior notes been loaded? */
isLoaded = false
}
},
created () {
this.journalEvents.$on('notes', this.openDialog)
},
methods: {
closeDialog () {
this.form.requestId = ''
this.form.notes = ''
this.priorNotes = []
this.priorNotesLoaded = false
this.notesVisible = false
},
async loadNotes () {
this.progress.$emit('show', 'indeterminate')
export default createComponent({
setup () {
/** The event bus for the journal page */
const events = useEvents()
/** The snackbar component properties */
const snackbar = useSnackbar()
/** The progress bar component properties */
const progress = useProgress()
/** Is this dialog visible? */
const notesVisible = ref(false)
/** The input form */
const form = new NotesForm()
/** The prior notes */
const prior = new PriorNotes()
/** Are there prior notes? */
const hasPriorNotes = computed(() => prior.isLoaded && prior.notes.length > 0)
/** Are there no prior notes? */
const noPriorNotes = computed(() => prior.isLoaded && prior.notes.length === 0)
/** Close this dialog */
const closeDialog = () => {
form.requestId = ''
form.notes = ''
prior.notes = []
prior.isLoaded = false
notesVisible.value = false
}
/** Load the notes for this request */
const loadNotes = async () => {
progress.events.$emit('show', 'indeterminate')
try {
const notes = await api.getNotes(this.form.requestId)
this.priorNotes = notes.data.sort((a, b) => b.asOf - a.asOf)
this.progress.$emit('done')
const notes = await api.getNotes(form.requestId)
prior.notes = (notes.data as NotesEntry[]).sort((a, b) => b.asOf - a.asOf)
} catch (e) {
console.error(e) // eslint-disable-line no-console
this.progress.$emit('done')
} finally {
this.priorNotesLoaded = true
progress.events.$emit('done')
prior.isLoaded = true
}
},
openDialog (request) {
this.form.requestId = request.requestId
this.notesVisible = true
},
async saveNotes () {
this.progress.$emit('show', 'indeterminate')
}
/** Open this dialog */
const openDialog = (request: JournalRequest) => {
form.requestId = request.requestId
notesVisible.value = true
}
/** Save the notes entered on this dialog */
const saveNotes = async () => {
progress.events.$emit('show', 'indeterminate')
try {
await api.addNote(this.form.requestId, this.form.notes)
this.progress.$emit('done')
this.messages.$emit('info', 'Added notes')
this.closeDialog()
await api.addNote(form.requestId, form.notes)
snackbar.events.$emit('info', 'Added notes')
closeDialog()
} catch (e) {
console.error(e) // eslint-disable-line no-console
this.progress.$emit('done')
}
},
trimText () {
this.form.notes = this.form.notes.trim()
} finally {
progress.events.$emit('done')
}
}
/** Trim the note text */
const trimText = () => { form.notes = form.notes.trim() }
events.$on('notes', openDialog)
return {
closeDialog,
form,
hasPriorNotes,
loadNotes,
noPriorNotes,
notesVisible,
openDialog,
prior,
saveNotes,
trimText
}
}
})
</script>
<style lang="sass">

View File

@ -24,48 +24,78 @@ md-card(v-if='shouldDisplay'
p.mpj-text-right: small.mpj-muted-text: em (last activity #[date-from-now(:value='request.asOf')])
</template>
<script>
'use strict'
<script lang="ts">
import { createComponent, computed } from '@vue/composition-api'
import { Actions } from '@/store/types'
import { Actions, JournalRequest, UpdateRequestAction } from '../../store/types' // eslint-disable-line no-unused-vars
export default {
name: 'request-card',
inject: [
'journalEvents',
'messages',
'progress'
],
import { useEvents } from '../Journal.vue'
import { useStore } from '../../plugins/store'
import { useProgress, useSnackbar } from '../../App.vue'
import { useRouter } from '../../plugins/router'
export default createComponent({
props: {
request: { required: true }
request: {
type: JournalRequest,
required: true
}
},
computed: {
shouldDisplay () {
setup (props) {
/** The Vuex store */
const store = useStore()
/** The application router */
const router = useRouter()
/** The progress bar component properties */
const progress = useProgress()
/** The snackbar component properties */
const snackbar = useSnackbar()
/** The journal event bus */
const events = useEvents()
/** Should this request be displayed? */
const shouldDisplay = computed(() => {
const now = Date.now()
return Math.max(now, this.request.showAfter, this.request.snoozedUntil) === now
}
},
methods: {
async markPrayed () {
await this.$store.dispatch(Actions.UpdateRequest, {
progress: this.progress,
requestId: this.request.requestId,
status: 'Prayed',
updateText: ''
return Math.max(now, props.request.showAfter, props.request.snoozedUntil) === now
})
this.messages.$emit('info', 'Request marked as prayed')
},
showEdit () {
this.$router.push({ name: 'EditRequest', params: { id: this.request.requestId } })
},
showNotes () {
this.journalEvents.$emit('notes', this.request)
},
snooze () {
this.journalEvents.$emit('snooze', this.request.requestId)
}
/** Mark the request as prayed */
const markPrayed = async () => {
const opts: UpdateRequestAction = {
progress,
requestId: props.request.requestId,
status: 'Prayed',
updateText: '',
recurType: '',
recurCount: 0
}
await store.dispatch(Actions.UpdateRequest, opts)
snackbar.events.$emit('info', 'Request marked as prayed')
}
/** Show the edit page for this request */
const showEdit = () => { router.push({ name: 'EditRequest', params: { id: props.request.requestId } }) }
/** Show the request notes dialog */
const showNotes = () => events.$emit('notes', props.request)
/** Show the snooze request dialog */
const snooze = () => events.$emit('snooze', props.request.requestId)
return {
markPrayed,
request: props.request,
shouldDisplay,
showEdit,
showNotes,
snooze
}
}
})
</script>
<style lang="sass">

View File

@ -77,9 +77,7 @@ export default createComponent({
}
await store.dispatch(Actions.SnoozeRequest, opts)
snackbar.events.$emit('info', 'Request un-snoozed')
if (parent) {
parent.$emit('requestUnsnoozed')
}
if (parent) parent.$emit('requestUnsnoozed')
}
/** Edit the given request */
@ -94,9 +92,7 @@ export default createComponent({
}
await store.dispatch(Actions.ShowRequestNow, opts)
snackbar.events.$emit('info', 'Recurrence skipped; request now shows in journal')
if (parent) {
parent.$emit('requestNowShown')
}
if (parent) parent.$emit('requestNowShown')
}
/** View the full request */

View File

@ -1,5 +1,5 @@
<template lang="pug">
md-dialog(:md-active.sync='snoozeVisible').mpj-skinny
md-dialog(:md-active.sync='isVisible.value').mpj-skinny
md-dialog-title Snooze Prayer Request
md-content.mpj-dialog-content
span.mpj-text-muted Until
@ -7,63 +7,90 @@ md-dialog(:md-active.sync='snoozeVisible').mpj-skinny
:md-disabled-dates='datesInPast'
md-immediately)
md-dialog-actions
md-button(:disabled='!isValid'
md-button(:disabled='!isValid.value'
@click='snoozeRequest()').md-primary #[md-icon snooze] Snooze
md-button(@click='closeDialog()') #[md-icon undo] Cancel
</template>
<script>
'use strict'
<script lang="ts">
import { createComponent, ref, computed } from '@vue/composition-api'
import { Actions } from '@/store/types'
import { Actions, SnoozeRequestAction } from '../../store/types' // eslint-disable-line no-unused-vars
import { useProgress, useSnackbar } from '../../App.vue'
import { useEvents } from '../Journal.vue'
import { useStore } from '../../plugins/store'
/** The input form */
class SnoozeForm {
/** The ID of the request */
requestId = ''
/** The date until which the request will be snoozed */
snoozedUntil = ''
}
export default createComponent({
setup () {
/** The Vuex store */
const store = useStore()
/** The progress bar component properties */
const progress = useProgress()
/** The snackbar component properties */
const snackbar = useSnackbar()
/** The journal event bus */
const events = useEvents()
/** Whether this dialog is visible */
const isVisible = ref(false)
/** The input form */
const form = new SnoozeForm()
/** Is the input date valid? */
const isValid = computed(() => !isNaN(Date.parse(form.snoozedUntil)))
/** Close the dialog */
const closeDialog = () => {
form.requestId = ''
form.snoozedUntil = ''
isVisible.value = false
}
/**
* Open the dialog
* @param requestId The ID of the request to be snoozed
*/
const openDialog = (requestId: string) => {
form.requestId = requestId
isVisible.value = true
}
const snoozeRequest = async () => {
const opts: SnoozeRequestAction = {
progress,
requestId: form.requestId,
until: Date.parse(form.snoozedUntil)
}
await store.dispatch(Actions.SnoozeRequest, opts)
snackbar.events.$emit('info', `Request snoozed until ${form.snoozedUntil}`)
closeDialog()
}
events.$on('snooze', openDialog)
export default {
name: 'snooze-request',
inject: [
'journalEvents',
'messages',
'progress'
],
props: {
events: { required: true }
},
data () {
return {
snoozeVisible: false,
datesInPast: date => date < new Date(),
form: {
requestId: '',
snoozedUntil: ''
closeDialog,
datesInPast: (date: Date) => date < new Date(),
form,
isValid,
isVisible,
openDialog,
snoozeRequest
}
}
},
created () {
this.journalEvents.$on('snooze', this.openDialog)
},
computed: {
isValid () {
return !isNaN(Date.parse(this.form.snoozedUntil))
}
},
methods: {
closeDialog () {
this.form.requestId = ''
this.form.snoozedUntil = ''
this.snoozeVisible = false
},
openDialog (requestId) {
this.form.requestId = requestId
this.snoozeVisible = true
},
async snoozeRequest () {
await this.$store.dispatch(Actions.SnoozeRequest, {
progress: this.progress,
requestId: this.form.requestId,
until: Date.parse(this.form.snoozedUntil)
})
this.messages.$emit('info', `Request snoozed until ${this.form.snoozedUntil}`)
this.closeDialog()
}
}
}
</script>

View File

@ -2,7 +2,7 @@
article.mpj-main-content(role='main')
page-title(title='Snoozed Requests'
hide-on-page=true)
template(v-if='loaded')
template(v-if='isLoaded.value')
md-empty-state(v-if='requests.length === 0'
md-icon='sentiment_dissatisfied'
md-label='No Snoozed Requests'
@ -14,47 +14,51 @@ article.mpj-main-content(role='main')
p(v-else) Loading journal...
</template>
<script>
'use strict'
<script lang="ts">
import { createComponent, ref, onMounted } from '@vue/composition-api'
import { Store } from 'vuex' // eslint-disable-line no-unused-vars
import { mapState } from 'vuex'
import RequestList from './RequestList.vue'
import { Actions } from '@/store/types'
import { Actions, AppState, JournalRequest } from '../../store/types' // eslint-disable-line no-unused-vars
import { useStore } from '../../plugins/store'
import { useProgress } from '../../App.vue'
import RequestList from '@/components/request/RequestList'
export default createComponent({
components: { RequestList },
setup () {
/** The Vuex store */
const store = useStore() as Store<AppState>
export default {
name: 'snoozed-requests',
inject: ['progress'],
components: {
RequestList
},
data () {
return {
requests: [],
loaded: false
/** The progress bar component properties */
const progress = useProgress()
/** The snoozed requests */
let requests: JournalRequest[] = []
/** Have snoozed requests been loaded? */
const isLoaded = ref(false)
/** Ensure the latest journal is loaded, and filter it to snoozed requests */
const ensureJournal = async () => {
if (!Array.isArray(store.state.journal)) {
isLoaded.value = false
await store.dispatch(Actions.LoadJournal, progress)
}
},
computed: {
...mapState(['journal', 'isLoadingJournal'])
},
created () {
this.$on('requestUnsnoozed', this.ensureJournal)
},
methods: {
async ensureJournal () {
if (!Array.isArray(this.journal)) {
this.loaded = false
await this.$store.dispatch(Actions.LoadJournal, this.progress)
}
this.requests = this.journal
requests = store.state.journal
.filter(req => req.snoozedUntil > Date.now())
.sort((a, b) => a.snoozedUntil - b.snoozedUntil)
this.loaded = true
isLoaded.value = true
}
},
async mounted () {
await this.ensureJournal()
onMounted(ensureJournal)
// this.$on('requestUnsnoozed', ensureJournal)
return {
requests,
isLoaded
}
}
})
</script>

View File

@ -5,17 +5,18 @@ article.mpj-main-content(role='main')
</template>
<script lang="ts">
import { onBeforeMount } from '@vue/composition-api'
import { createComponent, onBeforeMount } from '@vue/composition-api'
import VueRouter from 'vue-router' // eslint-disable-line no-unused-vars
import { Store } from 'vuex' // eslint-disable-line no-unused-vars
import { AppState } from '../../store/types' // eslint-disable-line no-unused-vars
import { AuthService } from '../../auth' // eslint-disable-line no-unused-vars
import { useAuth } from '../../plugins/auth'
import { useRouter } from '../../plugins/router'
import { useStore } from '../../plugins/store'
export default {
export default createComponent({
setup () {
/** Auth service instance */
const auth = useAuth() as AuthService
@ -36,5 +37,5 @@ export default {
return { }
}
}
})
</script>

View File

@ -10,7 +10,9 @@ import {
JournalRequest,
Mutations,
SnoozeRequestAction,
ShowRequestAction
ShowRequestAction,
AddRequestAction,
UpdateRequestAction
} from './types'
import { ProgressProps } from '@/types'
@ -101,16 +103,17 @@ const store : StoreOptions<AppState> = {
}
},
actions: {
async [Actions.AddRequest] ({ commit }, { progress, requestText, recurType, recurCount }) {
progress.$emit('show', 'indeterminate')
async [Actions.AddRequest] ({ commit }, p: AddRequestAction) {
const { progress, requestText, recurType, recurCount } = p
progress.events.$emit('show', 'indeterminate')
try {
await setBearer()
const newRequest = await api.addRequest(requestText, recurType, recurCount)
commit(Mutations.RequestAdded, newRequest.data)
progress.$emit('done')
} catch (err) {
logError(err)
progress.$emit('done')
} finally {
progress.events.$emit('done')
}
},
async [Actions.CheckAuthentication] ({ commit }) {
@ -129,19 +132,19 @@ const store : StoreOptions<AppState> = {
try {
const jrnl = await api.journal()
commit(Mutations.LoadedJournal, jrnl.data)
progress.events.$emit('done')
} catch (err) {
logError(err)
progress.events.$emit('done')
} finally {
progress.events.$emit('done')
commit(Mutations.LoadingJournal, false)
}
},
async [Actions.UpdateRequest] ({ commit, state }, { progress, requestId, status, updateText, recurType, recurCount }) {
progress.$emit('show', 'indeterminate')
async [Actions.UpdateRequest] ({ commit, state }, p: UpdateRequestAction) {
const { progress, requestId, status, updateText, recurType, recurCount } = p
progress.events.$emit('show', 'indeterminate')
try {
await setBearer()
const oldReq: any = (state.journal.filter(req => req.requestId === requestId) || [])[0] || {}
const oldReq = (state.journal.filter(req => req.requestId === requestId) || [])[0] || {}
if (!(status === 'Prayed' && updateText === '')) {
if (status !== 'Answered' && (oldReq.recurType !== recurType || oldReq.recurCount !== recurCount)) {
await api.updateRecurrence(requestId, recurType, recurCount)
@ -152,10 +155,10 @@ const store : StoreOptions<AppState> = {
}
const request = await api.getRequest(requestId)
commit(Mutations.RequestUpdated, request.data)
progress.$emit('done')
} catch (err) {
logError(err)
progress.$emit('done')
} finally {
progress.events.$emit('done')
}
},
async [Actions.ShowRequestNow] ({ commit }, p: ShowRequestAction) {
@ -166,9 +169,9 @@ const store : StoreOptions<AppState> = {
await api.showRequest(requestId, showAfter)
const request = await api.getRequest(requestId)
commit(Mutations.RequestUpdated, request.data)
progress.events.$emit('done')
} catch (err) {
logError(err)
} finally {
progress.events.$emit('done')
}
},
@ -180,9 +183,9 @@ const store : StoreOptions<AppState> = {
await api.snoozeRequest(requestId, until)
const request = await api.getRequest(requestId)
commit(Mutations.RequestUpdated, request.data)
progress.events.$emit('done')
} catch (err) {
logError(err)
} finally {
progress.events.$emit('done')
}
}

View File

@ -12,6 +12,15 @@ export class HistoryEntry {
text?: string = undefined
}
/** An entry with notes for a request */
export class NotesEntry {
/** The date/time the notes were recorded */
asOf = 0
/** The notes */
notes = ''
}
/** A prayer request that is part of the user's journal */
export class JournalRequest {
/** The ID of the request (just the CUID part) */
@ -45,7 +54,7 @@ export class JournalRequest {
history: HistoryEntry[] = []
/** Note entries for the request */
notes: any[] = [] // Note list
notes: NotesEntry[] = []
}
/** The state of myPrayerJournal */
@ -108,9 +117,21 @@ const mutations = {
}
export { mutations as Mutations }
/** The shape of the parameter to the add request action */
export interface AddRequestAction {
/** The progress bar component properties */
progress: ProgressProps
/** The text of the request */
requestText: string
/** The recurrence type */
recurType: string
/** The number of intervals for non-immediate recurrence */
recurCount: number
}
/** The shape of the parameter to the show request action */
export interface ShowRequestAction {
/** The progress bar component instance */
/** The progress bar component properties */
progress: ProgressProps
/** The ID of the prayer request being shown */
requestId: string
@ -120,10 +141,26 @@ export interface ShowRequestAction {
/** The shape of the parameter to the snooze request action */
export interface SnoozeRequestAction {
/** The progress bar component instance */
/** The progress bar component properties */
progress: ProgressProps
/** The ID of the prayer request being snoozed/unsnoozed */
requestId: string
/** The date/time after which the request will be once again shown */
until: number
}
/** The shape of the parameter to the update request action */
export interface UpdateRequestAction {
/** The progress bar component properties */
progress: ProgressProps
/** The ID of the prayer request */
requestId: string
/** The text of the update */
updateText: string
/** The status associated with the update */
status: string
/** The type of recurrence for the request */
recurType: string
/** The number of intervals for non-immediate recurrence */
recurCount: number
}