4 Commits
0.8.2 ... 0.8.3

Author SHA1 Message Date
Daniel J. Summers
b6d72d691b Docs update
Removed note about issue #5 that is fixed in v0.8.3
2017-10-08 22:08:43 -05:00
Daniel J. Summers
6f49a61822 Version bump (v0.8.3) 2017-10-08 22:07:49 -05:00
Daniel J. Summers
4db6d98011 Implemented auth renewal (#5) 2017-10-08 21:58:36 -05:00
Daniel J. Summers
3acec3dc25 Misc tweaks
- Updated SFCs per Vue Style Guide guidelines
- Added green gradient to header and off-white background color to body
- Changed DJS Consulting to Bit Badger Solutions in the docs
2017-10-08 19:19:24 -05:00
16 changed files with 167 additions and 75 deletions

View File

@@ -1,6 +1,8 @@
# Documentation
## Caveats
_myPrayerJournal is currently alpha software. There likely will be errors, the way things work may change, and parts of the application are unfinished or need polish. I **will** do my best to not lose any data, though; it is backed up the way other DJS Consulting sites have their data backed up. Throughout this document, current gotchas will be called out with italic text, like this notice._
_myPrayerJournal is currently alpha software. There likely will be errors, the way things work may change, and parts of the application are unfinished or need polish. I **will** do my best to not lose any data, though; it is backed up the way other Bit Badger Solutions sites have their data backed up. Throughout this document, current gotchas will be called out with italic text, like this notice._
## Finding the Site
@@ -37,5 +39,3 @@ Next to "Journal" on the top navigation is the word "Answered." This page lists
## Known Issues
See [the GitHub issues list](https://github.com/danieljsummers/myPrayerJournal/issues) for the most up-to-date list.
- _If you try to do something an get an error notification instead of a green checkmark, try logging off and logging back on again. The site currently doesn't check to see if your session has expired, but the server with which it's communicating does._

View File

@@ -1,7 +1,7 @@
{
"name": "my-prayer-journal-api",
"private": true,
"version": "0.8.2",
"version": "0.8.3",
"description": "Server API for myPrayerJournal",
"main": "index.js",
"author": "Daniel J. Summers <daniel@djs-consulting.com>",

View File

@@ -1,6 +1,6 @@
{
"name": "my-prayer-journal",
"version": "0.8.2",
"version": "0.8.3",
"description": "myPrayerJournal - Front End",
"author": "Daniel J. Summers <daniel@djs-consulting.com>",
"private": true,

View File

@@ -6,7 +6,7 @@
vue-progress-bar
toast(ref='toast')
footer
p.text-right: i myPrayerJournal v0.8.2
p.text-right: i myPrayerJournal v0.8.3
</template>
<script>
@@ -31,6 +31,9 @@ export default {
</script>
<style>
html, body {
background-color: whitesmoke;
}
body {
padding-top: 60px;
}
@@ -42,11 +45,13 @@ footer {
footer p {
margin: 0;
}
.mpj-page-title {
border-bottom: solid 1px lightgray;
margin-bottom: 20px;
}
.mpj-request-text {
white-space: pre-line;
}
.bg-mpj {
background-image: -webkit-gradient(linear, left top, left bottom, from(#050), to(whitesmoke));
background-image: -webkit-linear-gradient(top, #050, whitesmoke);
background-image: -moz-linear-gradient(top, #050, whitesmoke);
background-image: linear-gradient(to bottom, #050, whitesmoke);
}
</style>

View File

@@ -5,6 +5,8 @@ import auth0 from 'auth0-js'
import AUTH_CONFIG from './auth0-variables'
import mutations from '@/store/mutation-types'
var tokenRenewalTimeout
export default class AuthService {
constructor () {
@@ -17,7 +19,7 @@ export default class AuthService {
auth0 = new auth0.WebAuth({
domain: AUTH_CONFIG.domain,
clientID: AUTH_CONFIG.clientId,
redirectUri: AUTH_CONFIG.callbackUrl,
redirectUri: AUTH_CONFIG.appDomain + AUTH_CONFIG.callbackUrl,
audience: `https://${AUTH_CONFIG.domain}/userinfo`,
responseType: 'token id_token',
scope: 'openid profile email'
@@ -78,6 +80,16 @@ export default class AuthService {
})
}
scheduleRenewal () {
let expiresAt = JSON.parse(localStorage.getItem('expires_at'))
let delay = expiresAt - Date.now()
if (delay > 0) {
tokenRenewalTimeout = setTimeout(() => {
this.renewToken()
}, delay)
}
}
setSession (authResult) {
// Set the time that the access token will expire at
let expiresAt = JSON.stringify(
@@ -86,10 +98,30 @@ export default class AuthService {
localStorage.setItem('access_token', authResult.accessToken)
localStorage.setItem('id_token', authResult.idToken)
localStorage.setItem('expires_at', expiresAt)
this.scheduleRenewal()
}
renewToken () {
console.log('attempting renewal...')
this.auth0.renewAuth(
{
audience: `https://${AUTH_CONFIG.domain}/userinfo`,
redirectUri: `${AUTH_CONFIG.appDomain}/static/silent.html`,
usePostMessage: true
},
(err, result) => {
if (err) {
console.log(err)
} else {
this.setSession(result)
}
}
)
}
logout (store, router) {
// Clear access token and ID token from local storage
clearTimeout(tokenRenewalTimeout)
localStorage.removeItem('access_token')
localStorage.removeItem('id_token')
localStorage.removeItem('expires_at')

View File

@@ -3,8 +3,10 @@ article
page-title(title='Answered Requests')
p(v-if='!loaded') Loading answered requests...
div(v-if='loaded')
p.mpj-request-text(v-for='req in requests')
b-btn(@click='showFull(req.requestId)' size='sm' variant='outline-secondary')
p.mpj-request-text(v-for='req in requests' :key='req.requestId')
b-btn(@click='showFull(req.requestId)'
size='sm'
variant='outline-secondary')
icon(name='search')
| &nbsp;View Full Request
| &nbsp; &nbsp; {{ req.text }} &nbsp;
@@ -24,6 +26,9 @@ import api from '@/api'
export default {
name: 'answered',
components: {
FullRequest
},
data () {
return {
eventBus: new Vue(),
@@ -31,6 +36,11 @@ export default {
loaded: false
}
},
computed: {
toast () {
return this.$parent.$refs.toast
}
},
async mounted () {
this.$Progress.start()
try {
@@ -45,14 +55,6 @@ export default {
this.loaded = true
}
},
components: {
FullRequest
},
computed: {
toast () {
return this.$parent.$refs.toast
}
},
methods: {
showFull (requestId) {
this.eventBus.$emit('full', requestId)

View File

@@ -1,6 +1,7 @@
<template lang="pug">
article
page-title(title='Welcome!' hideOnPage='true')
page-title(title='Welcome!'
hideOnPage='true')
p &nbsp;
p.
myPrayerJournal is a place where individuals can record their prayer requests, record that they prayed for them,

View File

@@ -12,7 +12,8 @@ article
:toast='toast'
:key='row[0].requestId')
p.text-center(v-if='journal.length === 0'): em No requests found; click the "Add a New Request" button to add one
edit-request(:events='eventBus' :toast='toast' )
edit-request(:events='eventBus'
:toast='toast')
full-request(:events='eventBus')
</template>
@@ -32,17 +33,17 @@ import actions from '@/store/action-types'
export default {
name: 'journal',
data () {
return {
eventBus: new Vue()
}
},
components: {
EditRequest,
FullRequest,
NewRequest,
RequestListItem
},
data () {
return {
eventBus: new Vue()
}
},
computed: {
title () {
return `${this.user.given_name}'s Prayer Journal`

View File

@@ -1,5 +1,8 @@
<template lang="pug">
b-navbar(toggleable='sm' type='dark' variant='mpj' fixed='top')
b-navbar(toggleable='sm'
type='dark'
variant='mpj'
fixed='top')
b-nav-toggle(target='nav_collapse')
b-navbar-brand(to='/')
span(style='font-weight:100;') my
@@ -7,11 +10,15 @@ b-navbar(toggleable='sm' type='dark' variant='mpj' fixed='top')
span(style='font-weight:700;') Journal
b-collapse#nav_collapse(is-nav)
b-nav(is-nav-bar)
b-nav-item(v-if='isAuthenticated' to='/journal') Journal
b-nav-item(v-if='isAuthenticated' to='/answered') Answered
b-nav-item(v-if='isAuthenticated'
to='/journal') Journal
b-nav-item(v-if='isAuthenticated'
to='/answered') Answered
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(href='https://danieljsummers.github.io/myPrayerJournal/' target='_blank' @click.stop='') Docs
b-nav-item(href='https://danieljsummers.github.io/myPrayerJournal/'
target='_blank'
@click.stop='') Docs
</template>
<script>
@@ -27,6 +34,9 @@ export default {
auth0: new AuthService()
}
},
computed: {
...mapState([ 'isAuthenticated' ])
},
methods: {
logOn () {
this.auth0.login()
@@ -34,15 +44,6 @@ export default {
logOff () {
this.auth0.logout(this.$store, this.$router)
}
},
computed: {
...mapState([ 'isAuthenticated' ])
}
}
</script>
<style>
.bg-mpj {
background-color: #1e7e34 !important;
}
</style>

View File

@@ -1,5 +1,6 @@
<template lang="pug">
h2.mpj-page-title(v-if='!hideOnPage' v-html='title')
h2.mpj-page-title(v-if='!hideOnPage'
v-html='title')
</template>
<script>
@@ -15,13 +16,20 @@ export default {
default: false
}
},
created () {
document.title = `${this.title} « myPrayerJournal`
},
watch: {
title () {
document.title = `${this.title} « myPrayerJournal`
}
},
created () {
document.title = `${this.title} « myPrayerJournal`
}
}
</script>
<style scoped>
.mpj-page-title {
border-bottom: solid 1px lightgray;
margin-bottom: 20px;
}
</style>

View File

@@ -1,25 +1,31 @@
<template lang="pug">
span
//- b-btn(@click='openDialog()' title='Edit' size='sm' variant='outline-secondary'): icon(name='pencil')
b-modal(title='Edit Prayer Request'
v-model='editVisible'
size='lg'
header-bg-variant='dark'
b-modal(v-model='editVisible'
header-bg-variant='mpj'
header-text-variant='light'
size='lg'
title='Edit Prayer Request'
@edit='openDialog()'
@shows='focusRequestText')
b-form
b-form-group(label='Prayer Request' label-for='request_text')
b-textarea#request_text(v-model='form.requestText' :rows='10' @blur='trimText()' ref='toFocus')
b-form-group(label='Prayer Request'
label-for='request_text')
b-textarea#request_text(ref='toFocus'
v-model='form.requestText'
:rows='10'
@blur='trimText()')
b-form-group(label='Also Mark As')
b-radio-group(v-model='form.status' buttons)
b-radio-group(v-model='form.status'
buttons)
b-radio(value='Updated') Updated
b-radio(value='Prayed') Prayed
b-radio(value='Answered') Answered
div.w-100.text-right(slot='modal-footer')
b-btn(variant='primary' @click='saveRequest()') Save
b-btn(variant='primary'
@click='saveRequest()') Save
| &nbsp; &nbsp;
b-btn(variant='outline-secondary' @click='closeDialog()') Cancel
b-btn(variant='outline-secondary'
@click='closeDialog()') Cancel
</template>
<script>

View File

@@ -1,15 +1,19 @@
<template lang="pug">
span
b-modal(title='Prayer Request History'
v-model='historyVisible'
size='lg'
header-bg-variant='dark'
b-modal(v-model='historyVisible'
header-bg-variant='mpj'
header-text-variant='light'
size='lg'
title='Prayer Request History'
@shows='focusRequestText')
b-list-group(v-if='null !== full' flush)
full-request-history(v-for='item in full.history' :history='item' :key='item.asOf')
b-list-group(v-if='null !== full'
flush)
full-request-history(v-for='item in full.history'
:key='item.asOf'
:history='item')
div.w-100.text-right(slot='modal-footer')
b-btn(variant='primary' @click='closeDialog()') Close
b-btn(variant='primary'
@click='closeDialog()') Close
</template>
<script>
@@ -21,6 +25,9 @@ import api from '@/api'
export default {
name: 'full-request',
components: {
FullRequestHistory
},
props: {
events: { required: true }
},
@@ -33,9 +40,6 @@ export default {
created () {
this.events.$on('full', this.openDialog)
},
components: {
FullRequestHistory
},
methods: {
closeDialog () {
this.full = null

View File

@@ -3,19 +3,25 @@ div
b-btn(@click='openDialog()' size='sm' variant='primary')
icon(name='plus')
| &nbsp; Add a New Request
b-modal(title='Add a New Prayer Request'
v-model='showNewVisible'
size='lg'
header-bg-variant='dark'
b-modal(v-model='showNewVisible'
header-bg-variant='mpj'
header-text-variant='light'
size='lg'
title='Add a New Prayer Request'
@shown='focusRequestText')
b-form
b-form-group(label='Prayer Request' label-for='request_text')
b-textarea#request_text(v-model='form.requestText' :rows='10' @blur='trimText()' ref='toFocus')
b-form-group(label='Prayer Request'
label-for='request_text')
b-textarea#request_text(ref='toFocus'
v-model='form.requestText'
:rows='10'
@blur='trimText()')
div.w-100.text-right(slot='modal-footer')
b-btn(variant='primary' @click='saveRequest()') Save
b-btn(variant='primary'
@click='saveRequest()') Save
| &nbsp; &nbsp;
b-btn(variant='outline-secondary' @click='closeDialog()') Cancel
b-btn(variant='outline-secondary'
@click='closeDialog()') Cancel
toast(ref='toast')
</template>

View File

@@ -1,7 +1,10 @@
<template lang="pug">
div
b-card-group.w-100(deck)
b-card(v-for='(request, idx) in row' border-variant='dark' no-body)
b-card(v-for='(request, idx) in row'
:key='request.requestId'
border-variant='dark'
no-body)
b-card-body.p-0
p.card-text.mpj-request-text.mb-1.px-3.pt-3
| {{ request.text }}

View File

@@ -34,6 +34,7 @@ export default new Vuex.Store({
state: {
user: JSON.parse(localStorage.getItem('user_profile') || '{}'),
isAuthenticated: (() => {
this.auth0.scheduleRenewal()
if (this.auth0.isAuthenticated()) {
api.setBearer(localStorage.getItem('id_token'))
}

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.auth0.com/js/auth0/8.9/auth0.min.js"></script>
<script>
var webAuth = new auth0.WebAuth({
domain: 'djs-consulting.auth0.com',
clientID: 'Of2s0RQCQ3mt3dwIkOBY5h85J9sXbF2n',
scope: 'openid profile email',
responseType: 'token id_token',
redirectUri: 'http://localhost:3000/static/silent.html'
})
</script>
<script>
webAuth.parseHash(window.location.hash, function (err, response) {
parent.postMessage(err || response, 'http://localhost:3000');
})
</script>
</head>
<body></body>
</html>