Version 3 #67
@ -13,6 +13,7 @@
|
||||
"vue": "vue-cli-service build --modern && cd ../MyPrayerJournal.Api && dotnet run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/composition-api": "^0.3.2",
|
||||
"auth0-js": "^9.7.3",
|
||||
"axios": "^0.19.0",
|
||||
"core-js": "^3.3.2",
|
||||
@ -24,6 +25,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/auth0-js": "^9.10.6",
|
||||
"@types/node": "^12.12.12",
|
||||
"@typescript-eslint/eslint-plugin": "^2.8.0",
|
||||
"@typescript-eslint/parser": "^2.8.0",
|
||||
"@vue/cli-plugin-babel": "^4.0.5",
|
||||
|
@ -10,13 +10,13 @@
|
||||
span(style='font-weight:700;') Journal
|
||||
navigation
|
||||
md-app-content
|
||||
md-progress-bar(v-if='progress.visible'
|
||||
:md-mode='progress.mode')
|
||||
md-progress-bar(v-if='progress.visible.value'
|
||||
:md-mode='progress.mode.value')
|
||||
router-view
|
||||
md-snackbar(:md-active.sync='snackbar.visible'
|
||||
md-snackbar(:md-active.sync='snackbar.visible.value'
|
||||
md-position='center'
|
||||
:md-duration='snackbar.interval'
|
||||
ref='snackbar') {{ snackbar.message }}
|
||||
:md-duration='snackbar.interval.value'
|
||||
ref='snackbar') {{ snackbar.message.value }}
|
||||
footer
|
||||
p.mpj-muted-text.mpj-text-right
|
||||
| myPrayerJournal v{{ version }}
|
||||
@ -28,82 +28,119 @@
|
||||
#[a(href='https://bitbadger.solutions' target='_blank') Bit Badger Solutions]
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
<script lang="ts">
|
||||
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 { version } from '../package.json'
|
||||
import auth from './auth'
|
||||
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 {
|
||||
name: 'app',
|
||||
components: {
|
||||
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 {
|
||||
progress: {
|
||||
events: new Vue(),
|
||||
visible: false,
|
||||
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
|
||||
version,
|
||||
progress,
|
||||
snackbar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ const webAuth = new auth0.WebAuth({
|
||||
/**
|
||||
* 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
|
||||
AUTH_SESSION = 'auth-session'
|
||||
|
||||
@ -34,8 +34,9 @@ class AuthService extends EventEmitter {
|
||||
|
||||
/**
|
||||
* 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({
|
||||
appState: customState
|
||||
})
|
||||
@ -43,6 +44,7 @@ class AuthService extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Promisified parseHash function
|
||||
* @returns A promise that resolves with the parsed hash returned from Auth0
|
||||
*/
|
||||
parseHash (): Promise<Auth0DecodedHash> {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -103,6 +105,7 @@ class AuthService extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Renew authorzation tokens with Auth0
|
||||
* @returns A promise with the parsed hash from the Auth0 response
|
||||
*/
|
||||
renewTokens (): Promise<Auth0DecodedHash> {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -141,22 +144,32 @@ class AuthService extends EventEmitter {
|
||||
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?
|
||||
* @returns True if a user is authenticated
|
||||
*/
|
||||
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?
|
||||
* @returns True if the user's access token is valid
|
||||
*/
|
||||
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
|
||||
* @returns A promise that resolves to the user's access token
|
||||
*/
|
||||
async getAccessToken (): Promise<string> {
|
||||
if (this.isAccessTokenValid()) {
|
||||
|
@ -6,11 +6,6 @@ export class Token {
|
||||
* @param expiry The expiration for the token
|
||||
*/
|
||||
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 */
|
||||
|
@ -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
|
||||
“Docs” link, also above.
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
export default {
|
||||
name: 'home'
|
||||
}
|
||||
</script>
|
||||
|
@ -1,6 +1,5 @@
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, createElement, onBeforeUnmount, onMounted, ref } from '@vue/composition-api'
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
@ -19,35 +18,32 @@ export default {
|
||||
default: 10000
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
fromNow: moment(this.value).fromNow(),
|
||||
intervalId: null
|
||||
setup (props: any) {
|
||||
/** Interval ID for updating relative time */
|
||||
let intervalId: number = 0
|
||||
|
||||
/** 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: {
|
||||
actual () {
|
||||
return moment(this.value).format('LLLL')
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.intervalId = setInterval(this.updateFromNow, this.interval)
|
||||
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, {
|
||||
|
||||
/** Refresh the relative time string to keep it accurate */
|
||||
onMounted(() => { intervalId = setInterval(updateFromNow, props.interval) })
|
||||
|
||||
/** Cancel refreshing the time string represented with this component */
|
||||
onBeforeUnmount(() => clearInterval(intervalId))
|
||||
|
||||
return () => createElement(props.tag, {
|
||||
domProps: {
|
||||
title: this.actual,
|
||||
innerText: this.fromNow
|
||||
title: actual.value,
|
||||
innerText: fromNow.value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
to='/journal')
|
||||
md-tab(md-label='Active'
|
||||
to='/requests/active')
|
||||
md-tab(v-if='hasSnoozed'
|
||||
md-tab(v-if='hasSnoozed.value'
|
||||
md-label='Snoozed'
|
||||
to='/requests/snoozed')
|
||||
md-tab(md-label='Answered'
|
||||
@ -26,33 +26,48 @@
|
||||
@click.prevent='showHelp()')
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
<script lang="ts">
|
||||
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 {
|
||||
name: 'navigation',
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
hasSnoozed () {
|
||||
return this.isAuthenticated &&
|
||||
Array.isArray(this.journal) &&
|
||||
this.journal.filter(req => req.snoozedUntil > Date.now()).length > 0
|
||||
},
|
||||
...mapState(['isAuthenticated', 'journal'])
|
||||
},
|
||||
methods: {
|
||||
logOn () {
|
||||
this.$auth.login()
|
||||
},
|
||||
logOff () {
|
||||
this.$auth.logout(this.$store, this.$router)
|
||||
},
|
||||
showHelp () {
|
||||
window.open('https://docs.prayerjournal.me', '_blank')
|
||||
setup () {
|
||||
/** The Vuex store */
|
||||
const store = useStore() as Store<AppState>
|
||||
|
||||
/** The auth service */
|
||||
const auth = useAuth() as AuthService
|
||||
|
||||
/** Whether the user has any snoozed requests */
|
||||
const hasSnoozed = computed(() =>
|
||||
store.state.isAuthenticated &&
|
||||
Array.isArray(store.state.journal) &&
|
||||
store.state.journal.filter(req => req.snoozedUntil > Date.now()).length > 0)
|
||||
|
||||
/** Log a user on using Auth0 */
|
||||
const logOn = () => auth.login()
|
||||
|
||||
/** Log a user off using Auth0 */
|
||||
const logOff = () => {
|
||||
auth.logout(store)
|
||||
const router = useRouter()
|
||||
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
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { watch } from '@vue/composition-api'
|
||||
|
||||
export default {
|
||||
name: 'page-title',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
@ -16,13 +17,11 @@ export default {
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
title () {
|
||||
document.title = `${this.title.replace('’', "'")} « myPrayerJournal`
|
||||
}
|
||||
},
|
||||
created () {
|
||||
document.title = `${this.title.replace('’', "'")} « myPrayerJournal`
|
||||
setup (props: any) {
|
||||
watch(props.title, (title, prevTitle) => {
|
||||
document.title = `${props.title.replace('’', "'")} « myPrayerJournal`
|
||||
})
|
||||
return { }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -4,20 +4,37 @@ article.mpj-main-content(role='main')
|
||||
p Logging you on...
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
<script lang="ts">
|
||||
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 {
|
||||
name: 'log-on',
|
||||
inject: ['progress'],
|
||||
async created () {
|
||||
this.progress.$emit('show', 'indeterminate')
|
||||
await this.$auth.handleAuthentication(this.$store)
|
||||
},
|
||||
methods: {
|
||||
handleLoginEvent (data) {
|
||||
this.$router.push(data.state.target || '/journal')
|
||||
}
|
||||
setup () {
|
||||
/** Auth service instance */
|
||||
const auth = useAuth() as AuthService
|
||||
|
||||
/** Store instance */
|
||||
const store = useStore() as Store<AppState>
|
||||
|
||||
/** Router instance */
|
||||
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>
|
||||
|
@ -1,34 +1,33 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// Vue packages and components
|
||||
import Vue from 'vue'
|
||||
import { MdApp,
|
||||
MdButton,
|
||||
MdCard,
|
||||
MdContent,
|
||||
MdDatepicker,
|
||||
MdDialog,
|
||||
MdEmptyState,
|
||||
MdField,
|
||||
MdIcon,
|
||||
MdLayout,
|
||||
MdProgress,
|
||||
MdRadio,
|
||||
MdSnackbar,
|
||||
MdTable,
|
||||
MdTabs,
|
||||
MdToolbar,
|
||||
MdTooltip } from 'vue-material/dist/components'
|
||||
import VueCompositionApi from '@vue/composition-api'
|
||||
import {
|
||||
MdApp,
|
||||
MdButton,
|
||||
MdCard,
|
||||
MdContent,
|
||||
MdDatepicker,
|
||||
MdDialog,
|
||||
MdEmptyState,
|
||||
MdField,
|
||||
MdIcon,
|
||||
MdLayout,
|
||||
MdProgress,
|
||||
MdRadio,
|
||||
MdSnackbar,
|
||||
MdTable,
|
||||
MdTabs,
|
||||
MdToolbar,
|
||||
MdTooltip
|
||||
} from 'vue-material/dist/components'
|
||||
|
||||
// myPrayerJournal components
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import DateFromNow from './components/common/DateFromNow.vue'
|
||||
import PageTitle from './components/common/PageTitle.vue'
|
||||
import AuthPlugin from './plugins/auth'
|
||||
|
||||
/* eslint-enable */
|
||||
import PageTitle from './components/common/PageTitle.vue'
|
||||
import AuthPlugin from './plugins/auth'
|
||||
|
||||
// Styles
|
||||
import 'vue-material/dist/vue-material.min.css'
|
||||
@ -54,6 +53,7 @@ Vue.use(MdTabs)
|
||||
Vue.use(MdToolbar)
|
||||
Vue.use(MdTooltip)
|
||||
Vue.use(AuthPlugin)
|
||||
Vue.use(VueCompositionApi)
|
||||
Vue.component('date-from-now', DateFromNow)
|
||||
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 {
|
||||
install (Vue) {
|
||||
install (Vue: any) {
|
||||
Vue.prototype.$auth = authService
|
||||
|
||||
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",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": false,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env"
|
||||
|
@ -837,6 +837,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.11.tgz#bec2961975888d964196bf0016a2f984d793d3ce"
|
||||
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":
|
||||
version "2.4.0"
|
||||
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"
|
||||
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":
|
||||
version "5.0.0"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
|
||||
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
|
||||
|
Loading…
Reference in New Issue
Block a user