Version 3 #67
@ -13,6 +13,7 @@
|
|||||||
"vue": "vue-cli-service build --modern && cd ../MyPrayerJournal.Api && dotnet run"
|
"vue": "vue-cli-service build --modern && cd ../MyPrayerJournal.Api && dotnet run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vue/composition-api": "^0.3.2",
|
||||||
"auth0-js": "^9.7.3",
|
"auth0-js": "^9.7.3",
|
||||||
"axios": "^0.19.0",
|
"axios": "^0.19.0",
|
||||||
"core-js": "^3.3.2",
|
"core-js": "^3.3.2",
|
||||||
@ -24,6 +25,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/auth0-js": "^9.10.6",
|
"@types/auth0-js": "^9.10.6",
|
||||||
|
"@types/node": "^12.12.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.8.0",
|
"@typescript-eslint/eslint-plugin": "^2.8.0",
|
||||||
"@typescript-eslint/parser": "^2.8.0",
|
"@typescript-eslint/parser": "^2.8.0",
|
||||||
"@vue/cli-plugin-babel": "^4.0.5",
|
"@vue/cli-plugin-babel": "^4.0.5",
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
span(style='font-weight:700;') Journal
|
span(style='font-weight:700;') Journal
|
||||||
navigation
|
navigation
|
||||||
md-app-content
|
md-app-content
|
||||||
md-progress-bar(v-if='progress.visible'
|
md-progress-bar(v-if='progress.visible.value'
|
||||||
:md-mode='progress.mode')
|
:md-mode='progress.mode.value')
|
||||||
router-view
|
router-view
|
||||||
md-snackbar(:md-active.sync='snackbar.visible'
|
md-snackbar(:md-active.sync='snackbar.visible.value'
|
||||||
md-position='center'
|
md-position='center'
|
||||||
:md-duration='snackbar.interval'
|
:md-duration='snackbar.interval.value'
|
||||||
ref='snackbar') {{ snackbar.message }}
|
ref='snackbar') {{ snackbar.message.value }}
|
||||||
footer
|
footer
|
||||||
p.mpj-muted-text.mpj-text-right
|
p.mpj-muted-text.mpj-text-right
|
||||||
| myPrayerJournal v{{ version }}
|
| myPrayerJournal v{{ version }}
|
||||||
@ -28,82 +28,119 @@
|
|||||||
#[a(href='https://bitbadger.solutions' target='_blank') Bit Badger Solutions]
|
#[a(href='https://bitbadger.solutions' target='_blank') Bit Badger Solutions]
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
'use strict'
|
|
||||||
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
import { computed, ref, onMounted, provide } from '@vue/composition-api'
|
||||||
|
|
||||||
import Navigation from '@/components/common/Navigation'
|
import Navigation from '@/components/common/Navigation.vue'
|
||||||
|
|
||||||
import { Actions } from '@/store/types'
|
import auth from './auth'
|
||||||
import { version } from '../package.json'
|
import router from './router'
|
||||||
|
import store from './store'
|
||||||
|
import { Actions } from './store/types'
|
||||||
|
|
||||||
|
import { provideAuth } from './plugins/auth'
|
||||||
|
import { provideRouter } from './plugins/router'
|
||||||
|
import { provideStore } from './plugins/store'
|
||||||
|
// import { version } = require('../package.json')
|
||||||
|
|
||||||
|
function useSnackbar () {
|
||||||
|
const events = new Vue()
|
||||||
|
const visible = ref(false)
|
||||||
|
const message = ref('')
|
||||||
|
const interval = ref(4000)
|
||||||
|
|
||||||
|
const showSnackbar = (msg: string) => {
|
||||||
|
message.value = msg
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const showInfo = (msg: string) => {
|
||||||
|
interval.value = 4000
|
||||||
|
showSnackbar(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
const showError = (msg: string) => {
|
||||||
|
interval.value = Infinity
|
||||||
|
showSnackbar(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
events.$on('info', showInfo)
|
||||||
|
events.$on('error', showError)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
events,
|
||||||
|
visible,
|
||||||
|
message,
|
||||||
|
interval,
|
||||||
|
showSnackbar,
|
||||||
|
showInfo,
|
||||||
|
showError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useProgress () {
|
||||||
|
const events = new Vue()
|
||||||
|
const visible = ref(false)
|
||||||
|
const mode = ref('query')
|
||||||
|
|
||||||
|
const showProgress = (mod: string) => {
|
||||||
|
mode.value = mod
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideProgress = () => { visible.value = false }
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
events.$on('show', showProgress)
|
||||||
|
events.$on('done', hideProgress)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
events,
|
||||||
|
visible,
|
||||||
|
mode,
|
||||||
|
showProgress,
|
||||||
|
hideProgress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'app',
|
name: 'app',
|
||||||
components: {
|
components: {
|
||||||
Navigation
|
Navigation
|
||||||
},
|
},
|
||||||
data () {
|
setup () {
|
||||||
|
const pkg = require('../package.json')
|
||||||
|
|
||||||
|
provideAuth(auth)
|
||||||
|
provideRouter(router)
|
||||||
|
provideStore(store)
|
||||||
|
|
||||||
|
const version = computed(() =>
|
||||||
|
pkg.version.endsWith('.0')
|
||||||
|
? pkg.version.endsWith('.0.0')
|
||||||
|
? pkg.version.substr(0, pkg.version.length - 4)
|
||||||
|
: pkg.version.substr(0, pkg.version.length - 2)
|
||||||
|
: pkg.version)
|
||||||
|
|
||||||
|
const progress = useProgress()
|
||||||
|
const snackbar = useSnackbar()
|
||||||
|
|
||||||
|
onMounted(async () => store.dispatch(Actions.CheckAuthentication))
|
||||||
|
|
||||||
|
const SnackbarSymbol = Symbol('Snackbar events')
|
||||||
|
provide(SnackbarSymbol, snackbar.events)
|
||||||
|
|
||||||
|
const ProgressSymbol = Symbol('Progress events')
|
||||||
|
provide(ProgressSymbol, progress.events)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
progress: {
|
version,
|
||||||
events: new Vue(),
|
progress,
|
||||||
visible: false,
|
snackbar
|
||||||
mode: 'query'
|
|
||||||
},
|
|
||||||
snackbar: {
|
|
||||||
events: new Vue(),
|
|
||||||
visible: false,
|
|
||||||
message: '',
|
|
||||||
interval: 4000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async mounted () {
|
|
||||||
this.progress.events.$on('show', this.showProgress)
|
|
||||||
this.progress.events.$on('done', this.hideProgress)
|
|
||||||
this.snackbar.events.$on('info', this.showInfo)
|
|
||||||
this.snackbar.events.$on('error', this.showError)
|
|
||||||
await this.$store.dispatch(Actions.CheckAuthentication)
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
version () {
|
|
||||||
return version.endsWith('.0')
|
|
||||||
? version.endsWith('.0.0')
|
|
||||||
? version.substr(0, version.length - 4)
|
|
||||||
: version.substr(0, version.length - 2)
|
|
||||||
: version
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
showSnackbar (message) {
|
|
||||||
this.snackbar.message = message
|
|
||||||
this.snackbar.visible = true
|
|
||||||
},
|
|
||||||
showInfo (message) {
|
|
||||||
this.snackbar.interval = 4000
|
|
||||||
this.showSnackbar(message)
|
|
||||||
},
|
|
||||||
showError (message) {
|
|
||||||
this.snackbar.interval = Infinity
|
|
||||||
this.showSnackbar(message)
|
|
||||||
},
|
|
||||||
showProgress (mode) {
|
|
||||||
this.progress.mode = mode
|
|
||||||
this.progress.visible = true
|
|
||||||
},
|
|
||||||
hideProgress () {
|
|
||||||
this.progress.visible = false
|
|
||||||
},
|
|
||||||
handleLoginEvent (data) {
|
|
||||||
if (!data.loggedIn) {
|
|
||||||
this.showInfo('Logged out successfully')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
provide () {
|
|
||||||
return {
|
|
||||||
messages: this.snackbar.events,
|
|
||||||
progress: this.progress.events
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ const webAuth = new auth0.WebAuth({
|
|||||||
/**
|
/**
|
||||||
* A class to handle all authentication calls and determinations
|
* A class to handle all authentication calls and determinations
|
||||||
*/
|
*/
|
||||||
class AuthService extends EventEmitter {
|
export class AuthService extends EventEmitter {
|
||||||
// Local storage key for our session data
|
// Local storage key for our session data
|
||||||
AUTH_SESSION = 'auth-session'
|
AUTH_SESSION = 'auth-session'
|
||||||
|
|
||||||
@ -34,8 +34,9 @@ class AuthService extends EventEmitter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the user log in flow
|
* Starts the user log in flow
|
||||||
|
* @param customState Application state to be returned after user has authenticated
|
||||||
*/
|
*/
|
||||||
login (customState: any) {
|
login (customState?: any) {
|
||||||
webAuth.authorize({
|
webAuth.authorize({
|
||||||
appState: customState
|
appState: customState
|
||||||
})
|
})
|
||||||
@ -43,6 +44,7 @@ class AuthService extends EventEmitter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Promisified parseHash function
|
* Promisified parseHash function
|
||||||
|
* @returns A promise that resolves with the parsed hash returned from Auth0
|
||||||
*/
|
*/
|
||||||
parseHash (): Promise<Auth0DecodedHash> {
|
parseHash (): Promise<Auth0DecodedHash> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -103,6 +105,7 @@ class AuthService extends EventEmitter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Renew authorzation tokens with Auth0
|
* Renew authorzation tokens with Auth0
|
||||||
|
* @returns A promise with the parsed hash from the Auth0 response
|
||||||
*/
|
*/
|
||||||
renewTokens (): Promise<Auth0DecodedHash> {
|
renewTokens (): Promise<Auth0DecodedHash> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -141,22 +144,32 @@ class AuthService extends EventEmitter {
|
|||||||
this.emit('loginEvent', { loggedIn: false })
|
this.emit('loginEvent', { loggedIn: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the given token is currently valid
|
||||||
|
* @param t The token to be validated
|
||||||
|
* @returns True if the token is valid
|
||||||
|
*/
|
||||||
|
isTokenValid = (t: Token) => t.token !== '' && t.expiry !== 0 && Date.now() < t.expiry
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is there a user authenticated?
|
* Is there a user authenticated?
|
||||||
|
* @returns True if a user is authenticated
|
||||||
*/
|
*/
|
||||||
isAuthenticated () {
|
isAuthenticated () {
|
||||||
return this.session && this.session.id && this.session.id.isValid()
|
return this.session && this.session.id && this.isTokenValid(this.session.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the current access token valid?
|
* Is the current access token valid?
|
||||||
|
* @returns True if the user's access token is valid
|
||||||
*/
|
*/
|
||||||
isAccessTokenValid () {
|
isAccessTokenValid () {
|
||||||
return this.session && this.session.access && this.session.access.isValid()
|
return this.session && this.session.access && this.isTokenValid(this.session.access)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the user's access token, renewing it if required
|
* Get the user's access token, renewing it if required
|
||||||
|
* @returns A promise that resolves to the user's access token
|
||||||
*/
|
*/
|
||||||
async getAccessToken (): Promise<string> {
|
async getAccessToken (): Promise<string> {
|
||||||
if (this.isAccessTokenValid()) {
|
if (this.isAccessTokenValid()) {
|
||||||
|
@ -6,11 +6,6 @@ export class Token {
|
|||||||
* @param expiry The expiration for the token
|
* @param expiry The expiration for the token
|
||||||
*/
|
*/
|
||||||
constructor (public token: string, public expiry: number) { } // eslint-disable-line no-useless-constructor
|
constructor (public token: string, public expiry: number) { } // eslint-disable-line no-useless-constructor
|
||||||
|
|
||||||
/** Whether this token is currently valid */
|
|
||||||
isValid (): boolean {
|
|
||||||
return this.token !== '' && this.expiry !== 0 && Date.now() < this.expiry
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A user's current session */
|
/** A user's current session */
|
||||||
|
@ -12,11 +12,3 @@ md-content(role='main').mpj-main-content
|
|||||||
above, and log on with either a Microsoft or Google account. You can also learn more about the site at the
|
above, and log on with either a Microsoft or Google account. You can also learn more about the site at the
|
||||||
“Docs” link, also above.
|
“Docs” link, also above.
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'home'
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
'use strict'
|
import { computed, createElement, onBeforeUnmount, onMounted, ref } from '@vue/composition-api'
|
||||||
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -19,35 +18,32 @@ export default {
|
|||||||
default: 10000
|
default: 10000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
setup (props: any) {
|
||||||
return {
|
/** Interval ID for updating relative time */
|
||||||
fromNow: moment(this.value).fromNow(),
|
let intervalId: number = 0
|
||||||
intervalId: null
|
|
||||||
|
/** The relative time string */
|
||||||
|
const fromNow = ref(moment(props.value).fromNow())
|
||||||
|
|
||||||
|
/** The actual date/time (used as the title for the relative time) */
|
||||||
|
const actual = computed(() => moment(props.value).format('LLLL'))
|
||||||
|
|
||||||
|
/** Update the relative time string if it is different */
|
||||||
|
const updateFromNow = () => {
|
||||||
|
const newFromNow = moment(props.value).fromNow()
|
||||||
|
if (newFromNow !== fromNow.value) fromNow.value = newFromNow
|
||||||
}
|
}
|
||||||
},
|
|
||||||
computed: {
|
/** Refresh the relative time string to keep it accurate */
|
||||||
actual () {
|
onMounted(() => { intervalId = setInterval(updateFromNow, props.interval) })
|
||||||
return moment(this.value).format('LLLL')
|
|
||||||
}
|
/** Cancel refreshing the time string represented with this component */
|
||||||
},
|
onBeforeUnmount(() => clearInterval(intervalId))
|
||||||
mounted () {
|
|
||||||
this.intervalId = setInterval(this.updateFromNow, this.interval)
|
return () => createElement(props.tag, {
|
||||||
this.$watch('value', this.updateFromNow)
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
|
||||||
clearInterval(this.intervalId)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
updateFromNow () {
|
|
||||||
const newFromNow = moment(this.value).fromNow()
|
|
||||||
if (newFromNow !== this.fromNow) this.fromNow = newFromNow
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render (createElement) {
|
|
||||||
return createElement(this.tag, {
|
|
||||||
domProps: {
|
domProps: {
|
||||||
title: this.actual,
|
title: actual.value,
|
||||||
innerText: this.fromNow
|
innerText: fromNow.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
to='/journal')
|
to='/journal')
|
||||||
md-tab(md-label='Active'
|
md-tab(md-label='Active'
|
||||||
to='/requests/active')
|
to='/requests/active')
|
||||||
md-tab(v-if='hasSnoozed'
|
md-tab(v-if='hasSnoozed.value'
|
||||||
md-label='Snoozed'
|
md-label='Snoozed'
|
||||||
to='/requests/snoozed')
|
to='/requests/snoozed')
|
||||||
md-tab(md-label='Answered'
|
md-tab(md-label='Answered'
|
||||||
@ -26,33 +26,48 @@
|
|||||||
@click.prevent='showHelp()')
|
@click.prevent='showHelp()')
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
'use strict'
|
import { computed } from '@vue/composition-api'
|
||||||
|
import { Store } from 'vuex' // eslint-disable-line no-unused-vars
|
||||||
|
|
||||||
import { mapState } from 'vuex'
|
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 {
|
||||||
name: 'navigation',
|
setup () {
|
||||||
data () {
|
/** The Vuex store */
|
||||||
return {}
|
const store = useStore() as Store<AppState>
|
||||||
},
|
|
||||||
computed: {
|
/** The auth service */
|
||||||
hasSnoozed () {
|
const auth = useAuth() as AuthService
|
||||||
return this.isAuthenticated &&
|
|
||||||
Array.isArray(this.journal) &&
|
/** Whether the user has any snoozed requests */
|
||||||
this.journal.filter(req => req.snoozedUntil > Date.now()).length > 0
|
const hasSnoozed = computed(() =>
|
||||||
},
|
store.state.isAuthenticated &&
|
||||||
...mapState(['isAuthenticated', 'journal'])
|
Array.isArray(store.state.journal) &&
|
||||||
},
|
store.state.journal.filter(req => req.snoozedUntil > Date.now()).length > 0)
|
||||||
methods: {
|
|
||||||
logOn () {
|
/** Log a user on using Auth0 */
|
||||||
this.$auth.login()
|
const logOn = () => auth.login()
|
||||||
},
|
|
||||||
logOff () {
|
/** Log a user off using Auth0 */
|
||||||
this.$auth.logout(this.$store, this.$router)
|
const logOff = () => {
|
||||||
},
|
auth.logout(store)
|
||||||
showHelp () {
|
const router = useRouter()
|
||||||
window.open('https://docs.prayerjournal.me', '_blank')
|
router.push('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Open a new window/tab with help */
|
||||||
|
const showHelp = () => { window.open('https://docs.prayerjournal.me', '_blank') }
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasSnoozed,
|
||||||
|
logOn,
|
||||||
|
logOff,
|
||||||
|
showHelp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,10 @@ h1(v-if='!hideOnPage'
|
|||||||
v-html='title').md-title
|
v-html='title').md-title
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
import { watch } from '@vue/composition-api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'page-title',
|
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -16,13 +17,11 @@ export default {
|
|||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
setup (props: any) {
|
||||||
title () {
|
watch(props.title, (title, prevTitle) => {
|
||||||
document.title = `${this.title.replace('’', "'")} « myPrayerJournal`
|
document.title = `${props.title.replace('’', "'")} « myPrayerJournal`
|
||||||
}
|
})
|
||||||
},
|
return { }
|
||||||
created () {
|
|
||||||
document.title = `${this.title.replace('’', "'")} « myPrayerJournal`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -4,20 +4,37 @@ article.mpj-main-content(role='main')
|
|||||||
p Logging you on...
|
p Logging you on...
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
'use strict'
|
import { 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 {
|
||||||
name: 'log-on',
|
setup () {
|
||||||
inject: ['progress'],
|
/** Auth service instance */
|
||||||
async created () {
|
const auth = useAuth() as AuthService
|
||||||
this.progress.$emit('show', 'indeterminate')
|
|
||||||
await this.$auth.handleAuthentication(this.$store)
|
/** Store instance */
|
||||||
},
|
const store = useStore() as Store<AppState>
|
||||||
methods: {
|
|
||||||
handleLoginEvent (data) {
|
/** Router instance */
|
||||||
this.$router.push(data.state.target || '/journal')
|
const router = useRouter() as VueRouter
|
||||||
}
|
|
||||||
|
/** Navigate on auth completion */
|
||||||
|
auth.on('loginEvent', (data: any) => {
|
||||||
|
router.push(data.state.target || '/journal')
|
||||||
|
})
|
||||||
|
|
||||||
|
// this.progress.$emit('show', 'indeterminate')
|
||||||
|
onBeforeMount(async () => { await auth.handleAuthentication(store) })
|
||||||
|
|
||||||
|
return { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,34 +1,33 @@
|
|||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
// Vue packages and components
|
// Vue packages and components
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { MdApp,
|
import VueCompositionApi from '@vue/composition-api'
|
||||||
MdButton,
|
import {
|
||||||
MdCard,
|
MdApp,
|
||||||
MdContent,
|
MdButton,
|
||||||
MdDatepicker,
|
MdCard,
|
||||||
MdDialog,
|
MdContent,
|
||||||
MdEmptyState,
|
MdDatepicker,
|
||||||
MdField,
|
MdDialog,
|
||||||
MdIcon,
|
MdEmptyState,
|
||||||
MdLayout,
|
MdField,
|
||||||
MdProgress,
|
MdIcon,
|
||||||
MdRadio,
|
MdLayout,
|
||||||
MdSnackbar,
|
MdProgress,
|
||||||
MdTable,
|
MdRadio,
|
||||||
MdTabs,
|
MdSnackbar,
|
||||||
MdToolbar,
|
MdTable,
|
||||||
MdTooltip } from 'vue-material/dist/components'
|
MdTabs,
|
||||||
|
MdToolbar,
|
||||||
|
MdTooltip
|
||||||
|
} from 'vue-material/dist/components'
|
||||||
|
|
||||||
// myPrayerJournal components
|
// myPrayerJournal components
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import DateFromNow from './components/common/DateFromNow.vue'
|
import DateFromNow from './components/common/DateFromNow.vue'
|
||||||
import PageTitle from './components/common/PageTitle.vue'
|
import PageTitle from './components/common/PageTitle.vue'
|
||||||
import AuthPlugin from './plugins/auth'
|
import AuthPlugin from './plugins/auth'
|
||||||
|
|
||||||
/* eslint-enable */
|
|
||||||
|
|
||||||
// Styles
|
// Styles
|
||||||
import 'vue-material/dist/vue-material.min.css'
|
import 'vue-material/dist/vue-material.min.css'
|
||||||
@ -54,6 +53,7 @@ Vue.use(MdTabs)
|
|||||||
Vue.use(MdToolbar)
|
Vue.use(MdToolbar)
|
||||||
Vue.use(MdTooltip)
|
Vue.use(MdTooltip)
|
||||||
Vue.use(AuthPlugin)
|
Vue.use(AuthPlugin)
|
||||||
|
Vue.use(VueCompositionApi)
|
||||||
Vue.component('date-from-now', DateFromNow)
|
Vue.component('date-from-now', DateFromNow)
|
||||||
Vue.component('page-title', PageTitle)
|
Vue.component('page-title', PageTitle)
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import authService from '@/auth'
|
import { inject, provide } from '@vue/composition-api'
|
||||||
|
import authService, { AuthService } from '@/auth'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
install (Vue) {
|
install (Vue: any) {
|
||||||
Vue.prototype.$auth = authService
|
Vue.prototype.$auth = authService
|
||||||
|
|
||||||
Vue.mixin({
|
Vue.mixin({
|
||||||
@ -18,3 +19,18 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AuthSymbol = Symbol('Auth service')
|
||||||
|
|
||||||
|
export function provideAuth (auth: AuthService) {
|
||||||
|
provide(AuthSymbol, auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Use the auth service */
|
||||||
|
export function useAuth (): AuthService {
|
||||||
|
const auth = inject(AuthSymbol)
|
||||||
|
if (!auth) {
|
||||||
|
throw new Error('Auth not configured!')
|
||||||
|
}
|
||||||
|
return auth as AuthService
|
||||||
|
}
|
||||||
|
16
src/app/src/plugins/router.ts
Normal file
16
src/app/src/plugins/router.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import VueRouter from 'vue-router'
|
||||||
|
import { inject, provide } from '@vue/composition-api'
|
||||||
|
|
||||||
|
const RouterSymbol = Symbol('Vue router')
|
||||||
|
|
||||||
|
export function provideRouter (router: VueRouter) {
|
||||||
|
provide(RouterSymbol, router)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRouter (): VueRouter {
|
||||||
|
const router = inject(RouterSymbol)
|
||||||
|
if (!router) {
|
||||||
|
throw new Error('Router not configured!')
|
||||||
|
}
|
||||||
|
return router as VueRouter
|
||||||
|
}
|
20
src/app/src/plugins/store.ts
Normal file
20
src/app/src/plugins/store.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { provide, inject } from '@vue/composition-api'
|
||||||
|
import { Store } from 'vuex'
|
||||||
|
|
||||||
|
import { AppState } from '@/store/types'
|
||||||
|
|
||||||
|
const StoreSymbol = Symbol('Vuex store')
|
||||||
|
|
||||||
|
/** Configure the store provided by this plugin */
|
||||||
|
export function provideStore (store: Store<AppState>) {
|
||||||
|
provide(StoreSymbol, store)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Use the provided store */
|
||||||
|
export function useStore (): Store<AppState> {
|
||||||
|
const store = inject(StoreSymbol)
|
||||||
|
if (!store) {
|
||||||
|
throw new Error('No store configured!')
|
||||||
|
}
|
||||||
|
return store as Store<AppState>
|
||||||
|
}
|
1
src/app/src/untyped-modules.d.ts
vendored
Normal file
1
src/app/src/untyped-modules.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
declare module 'vue-material/dist/components'
|
@ -8,8 +8,8 @@
|
|||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"noImplicitAny": false,
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"types": [
|
"types": [
|
||||||
"webpack-env"
|
"webpack-env"
|
||||||
|
@ -837,6 +837,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.11.tgz#bec2961975888d964196bf0016a2f984d793d3ce"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.11.tgz#bec2961975888d964196bf0016a2f984d793d3ce"
|
||||||
integrity sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ==
|
integrity sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ==
|
||||||
|
|
||||||
|
"@types/node@^12.12.12":
|
||||||
|
version "12.12.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.12.tgz#529bc3e73dbb35dd9e90b0a1c83606a9d3264bdb"
|
||||||
|
integrity sha512-MGuvYJrPU0HUwqF7LqvIj50RZUX23Z+m583KBygKYUZLlZ88n6w28XRNJRJgsHukLEnLz6w6SvxZoLgbr5wLqQ==
|
||||||
|
|
||||||
"@types/normalize-package-data@^2.4.0":
|
"@types/normalize-package-data@^2.4.0":
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
|
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
|
||||||
@ -1129,6 +1134,13 @@
|
|||||||
source-map "~0.6.1"
|
source-map "~0.6.1"
|
||||||
vue-template-es2015-compiler "^1.9.0"
|
vue-template-es2015-compiler "^1.9.0"
|
||||||
|
|
||||||
|
"@vue/composition-api@^0.3.2":
|
||||||
|
version "0.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/composition-api/-/composition-api-0.3.2.tgz#2d797028e489bf7812f08c7bb33ffd03ef23c617"
|
||||||
|
integrity sha512-fD4dn9cJX62QSP2TMFLXCOQOa+Bu2o7kWDjrU/FNLkNqPPcCKBLxCH/Lc+gNCRBKdEUGyI3arjAw7j0Yz1hnvw==
|
||||||
|
dependencies:
|
||||||
|
tslib "^1.9.3"
|
||||||
|
|
||||||
"@vue/eslint-config-standard@^5.0.0":
|
"@vue/eslint-config-standard@^5.0.0":
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/eslint-config-standard/-/eslint-config-standard-5.0.0.tgz#2ffabb4056205a86782cd5a641cdbcd330d905b4"
|
resolved "https://registry.yarnpkg.com/@vue/eslint-config-standard/-/eslint-config-standard-5.0.0.tgz#2ffabb4056205a86782cd5a641cdbcd330d905b4"
|
||||||
@ -8789,7 +8801,7 @@ ts-loader@^6.2.0:
|
|||||||
micromatch "^4.0.0"
|
micromatch "^4.0.0"
|
||||||
semver "^6.0.0"
|
semver "^6.0.0"
|
||||||
|
|
||||||
tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
|
tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
|
||||||
version "1.10.0"
|
version "1.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
|
||||||
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
|
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
|
||||||
|
Loading…
Reference in New Issue
Block a user