F# API #18

Merged
danieljsummers merged 7 commits from fs-api into master 2018-08-07 02:21:29 +00:00
9 changed files with 108 additions and 14 deletions
Showing only changes of commit 09a76aba30 - Show all commits

View File

@ -4,8 +4,8 @@
Journaling has a long history; it helps people remember what happened, and the act of writing helps people think about what happened and process it. A prayer journal is not a new concept; it helps you keep track of the requests for which you've prayed, you can use it to pray over things repeatedly, and you can write the result when the answer comes _(or it was "no")_. Journaling has a long history; it helps people remember what happened, and the act of writing helps people think about what happened and process it. A prayer journal is not a new concept; it helps you keep track of the requests for which you've prayed, you can use it to pray over things repeatedly, and you can write the result when the answer comes _(or it was "no")_.
myPrayerJournal was borne of out of a personal desire I (Daniel) had to have something that would help me with my prayer life. When it's time to pray, it's not really time to use an app, so the design goal here is to keep it simple and unobtrusive. It will also help eliminate some of the downsides to a paper prayer journal, like not remembering whether you've prayed for a request, or running out of room to write another update on one. myPrayerJournal was borne of out of a personal desire [Daniel](https://github.com/danieljsummers) had to have something that would help him with his prayer life. When it's time to pray, it's not really time to use an app, so the design goal here is to keep it simple and unobtrusive. It will also help eliminate some of the downsides to a paper prayer journal, like not remembering whether you've prayed for a request, or running out of room to write another update on one.
## Futher Reading ## Futher Reading
The documentation for the site is at <https://danieljsummers.github.io/myPrayerJournal/>. The documentation for the site is at <https://bit-badger.github.io/myPrayerJournal/>.

View File

@ -4,7 +4,7 @@
Journaling has a long history; it helps people remember what happened, and the act of writing helps people think about what happened and process it. A prayer journal is not a new concept; it helps you keep track of the requests for which you've prayed, you can use it to pray over things repeatedly, and you can write the result when the answer comes _(or it was "no")_. Journaling has a long history; it helps people remember what happened, and the act of writing helps people think about what happened and process it. A prayer journal is not a new concept; it helps you keep track of the requests for which you've prayed, you can use it to pray over things repeatedly, and you can write the result when the answer comes _(or it was "no")_.
myPrayerJournal was borne of out of a personal desire I (Daniel) had to have something that would help me with my prayer life. When it's time to pray, it's not really time to use an app, so the design goal here is to keep it simple and unobtrusive. It will also help eliminate some of the downsides to a paper prayer journal, like not remembering whether you've prayed for a request, or running out of room to write another update on one. myPrayerJournal was borne of out of a personal desire [Daniel](https://github.com/danieljsummers) had to have something that would help him with his prayer life. When it's time to pray, it's not really time to use an app, so the design goal here is to keep it simple and unobtrusive. It will also help eliminate some of the downsides to a paper prayer journal, like not remembering whether you've prayed for a request, or running out of room to write another update on one.
## Finding the Site ## Finding the Site
@ -38,12 +38,16 @@ The third button for each request has an icon that looks like a piece of paper w
myPrayerJournal tracks all of the actions related to a request; the fourth button, with the magnifying glass icon, will show you the entire history, including the text as it changed, and all the times "Prayed" was recorded. myPrayerJournal tracks all of the actions related to a request; the fourth button, with the magnifying glass icon, will show you the entire history, including the text as it changed, and all the times "Prayed" was recorded.
## Snoozing Requests
There may be a time where a request does not need to appear. The fifth button, with the clock icon, allows you to snooze requests until the day you specify. Additionally, if you have any snoozed requests, a "Snoozed" menu item will appear next to the "Journal" one; this page allows you to see what requests are snoozed, and return them to your journal by canceling the snooze.
## Answered Requests ## Answered Requests
Next to "Journal" on the top navigation is the word "Answered." This page lists all answered requests, from most recent to least recent, along with the text of the request at the time it was marked as answered. It will also show you when it was marked answered. The button at the bottom of each request, with the magnifying glass and the words "Show Full Request", link to a page that shows that request's complete history and notes, along with a few statistics about that request. The history and notes are listed from most recent to least recent; if you want to read it chronologically, just press the "End" key on your keyboard and read it from the bottom up. Next to "Journal" on the top navigation is the word "Answered." This page lists all answered requests, from most recent to least recent, along with the text of the request at the time it was marked as answered. It will also show you when it was marked answered. The button at the bottom of each request, with the magnifying glass and the words "Show Full Request", link to a page that shows that request's complete history and notes, along with a few statistics about that request. The history and notes are listed from most recent to least recent; if you want to read it chronologically, just press the "End" key on your keyboard and read it from the bottom up.
## Final Notes ## Final Notes
- myPrayerJournal is currently in public beta. If you encounter errors, please [file an issue on GitHub](https://github.com/danieljsummers/myPrayerJournal/issues) with as much detail as possible. You can also browse the list of issues to see what has been done and what is still left to do. - myPrayerJournal is currently in public beta. If you encounter errors, please [file an issue on GitHub](https://github.com/bit-badger/myPrayerJournal/issues) with as much detail as possible. You can also browse the list of issues to see what has been done and what is still left to do.
- Prayer requests and their history are securely backed up nightly along with other Bit Badger Solutions data. - Prayer requests and their history are securely backed up nightly along with other Bit Badger Solutions data.
- Prayer changes things - most of all, the one doing the praying. I pray that this tool enables you to deepen and strengthen your prayer life. - Prayer changes things - most of all, the one doing the praying. I pray that this tool enables you to deepen and strengthen your prayer life.

View File

@ -18,13 +18,12 @@ module Error =
/// Handle 404s from the API, sending known URL paths to the Vue app so that they can be handled there /// Handle 404s from the API, sending known URL paths to the Vue app so that they can be handled there
let notFound : HttpHandler = let notFound : HttpHandler =
fun next ctx -> fun next ctx ->
let vueApp () = htmlFile "wwwroot/index.html" next ctx [ "/answered"; "/journal"; "/snoozed"; "/user" ]
match true with |> List.filter ctx.Request.Path.Value.StartsWith
| _ when ctx.Request.Path.Value.StartsWith "/answered" -> vueApp () |> List.length
| _ when ctx.Request.Path.Value.StartsWith "/journal" -> vueApp () |> function
| _ when ctx.Request.Path.Value.StartsWith "/user" -> vueApp () | 0 -> (setStatusCode 404 >=> json ([ "error", "not found" ] |> dict)) next ctx
| _ when ctx.Request.Path.Value = "/" -> vueApp () | _ -> htmlFile "wwwroot/index.html" next ctx
| _ -> (setStatusCode 404 >=> json ([ "error", "not found" ] |> dict)) next ctx
/// Handler helpers /// Handler helpers

View File

@ -12,7 +12,7 @@
em: small. em: small.
#[router-link(:to="{ name: 'PrivacyPolicy' }") Privacy Policy] &bull; #[router-link(:to="{ name: 'PrivacyPolicy' }") Privacy Policy] &bull;
#[router-link(:to="{ name: 'TermsOfService' }") Terms of Service] &bull; #[router-link(:to="{ name: 'TermsOfService' }") Terms of Service] &bull;
#[a(href='https://github.com/danieljsummers/myprayerjournal') Developed] and hosted by #[a(href='https://github.com/bit-badger/myprayerjournal') Developed] and hosted by
#[a(href='https://bitbadger.solutions') Bit Badger Solutions] #[a(href='https://bitbadger.solutions') Bit Badger Solutions]
</template> </template>

View File

@ -12,11 +12,13 @@ b-navbar(toggleable='sm'
b-navbar-nav b-navbar-nav
b-nav-item(v-if='isAuthenticated' b-nav-item(v-if='isAuthenticated'
to='/journal') Journal to='/journal') Journal
b-nav-item(v-if='hasSnoozed'
to='/snoozed') Snoozed
b-nav-item(v-if='isAuthenticated' b-nav-item(v-if='isAuthenticated'
to='/answered') Answered to='/answered') Answered
b-nav-item(v-if='isAuthenticated'): a(@click.stop='logOff()') Log Off b-nav-item(v-if='isAuthenticated'): a(@click.stop='logOff()') Log Off
b-nav-item(v-if='!isAuthenticated'): a(@click.stop='logOn()') Log On b-nav-item(v-if='!isAuthenticated'): a(@click.stop='logOn()') Log On
b-nav-item(href='https://danieljsummers.github.io/myPrayerJournal/' b-nav-item(href='https://bit-badger.github.io/myPrayerJournal/'
target='_blank' target='_blank'
@click.stop='') Docs @click.stop='') Docs
</template> </template>
@ -35,7 +37,12 @@ export default {
} }
}, },
computed: { computed: {
...mapState([ 'isAuthenticated' ]) hasSnoozed () {
return this.isAuthenticated &&
Array.isArray(this.journal) &&
this.journal.filter(req => req.snoozedUntil > Date.now()).length > 0
},
...mapState([ 'journal', 'isAuthenticated' ])
}, },
methods: { methods: {
logOn () { logOn () {

View File

@ -0,0 +1,76 @@
<template lang="pug">
article
page-title(title='Snoozed Requests')
p(v-if='!loaded') Loading journal...
div(v-if='loaded').mpj-snoozed-list
p.text-center(v-if='requests.length === 0'): em.
No snoozed requests found; return to #[router-link(:to='{ name: "Journal" } ') your journal]
p.mpj-snoozed-text(v-for='req in requests' :key='req.requestId')
| {{ req.text }}
br
br
b-btn(@click='cancelSnooze(req.requestId)'
size='sm'
variant='outline-secondary')
icon(name='times')
= ' Cancel Snooze'
small.text-muted: em.
&nbsp; Snooze expires #[date-from-now(:value='req.snoozedUntil')]
</template>
<script>
'use static'
import { mapState } from 'vuex'
import actions from '@/store/action-types'
export default {
name: 'answered',
data () {
return {
requests: [],
loaded: false
}
},
computed: {
toast () {
return this.$parent.$refs.toast
},
...mapState(['journal', 'isLoadingJournal'])
},
methods: {
async ensureJournal () {
if (!Array.isArray(this.journal)) {
this.loaded = false
await this.$store.dispatch(actions.LOAD_JOURNAL, this.$Progress)
}
this.requests = this.journal
.filter(req => req.snoozedUntil > Date.now())
.sort((a, b) => a.snoozedUntil - b.snoozedUntil)
this.loaded = true
},
async cancelSnooze (requestId) {
await this.$store.dispatch(actions.SNOOZE_REQUEST, {
progress: this.$Progress,
requestId: requestId,
until: 0
})
this.toast.showToast('Request un-snoozed', { theme: 'success' })
this.ensureJournal()
}
},
async mounted () {
await this.ensureJournal()
}
}
</script>
<style>
.mpj-snoozed-list p {
border-top: solid 1px lightgray;
}
.mpj-snoozed-list p:first-child {
border-top: none;
}
</style>

View File

@ -17,6 +17,7 @@ import 'vue-awesome/icons/file-text-o'
import 'vue-awesome/icons/pencil' import 'vue-awesome/icons/pencil'
import 'vue-awesome/icons/plus' import 'vue-awesome/icons/plus'
import 'vue-awesome/icons/search' import 'vue-awesome/icons/search'
import 'vue-awesome/icons/times'
import App from './App' import App from './App'
import router from './router' import router from './router'

View File

@ -7,6 +7,7 @@ import Home from '@/components/Home'
import Journal from '@/components/Journal' import Journal from '@/components/Journal'
import LogOn from '@/components/user/LogOn' import LogOn from '@/components/user/LogOn'
import PrivacyPolicy from '@/components/legal/PrivacyPolicy' import PrivacyPolicy from '@/components/legal/PrivacyPolicy'
import Snoozed from '@/components/Snoozed'
import TermsOfService from '@/components/legal/TermsOfService' import TermsOfService from '@/components/legal/TermsOfService'
Vue.use(Router) Vue.use(Router)
@ -45,6 +46,11 @@ export default new Router({
name: 'TermsOfService', name: 'TermsOfService',
component: TermsOfService component: TermsOfService
}, },
{
path: '/snoozed',
name: 'Snoozed',
component: Snoozed
},
{ {
path: '/user/log-on', path: '/user/log-on',
name: 'LogOn', name: 'LogOn',

View File

@ -116,6 +116,7 @@ export default new Vuex.Store({
await api.snoozeRequest(requestId, until) await api.snoozeRequest(requestId, until)
const request = await api.getRequest(requestId) const request = await api.getRequest(requestId)
commit(mutations.REQUEST_UPDATED, request.data) commit(mutations.REQUEST_UPDATED, request.data)
progress.finish()
} catch (err) { } catch (err) {
logError(err) logError(err)
progress.fail() progress.fail()