2018-08-28 22:50:22 +02:00
|
|
|
<template>
|
2019-11-03 13:44:40 +01:00
|
|
|
<div>
|
2020-05-08 20:43:51 +02:00
|
|
|
<div v-if="online">
|
2019-11-03 13:44:40 +01:00
|
|
|
<!-- This is a workaround to get the sw to "see" the to-be-cached version of the offline background image -->
|
|
|
|
<div class="offline" style="height: 0;width: 0;"></div>
|
2020-05-31 21:17:10 +02:00
|
|
|
<nav
|
|
|
|
class="navbar main-theme is-fixed-top"
|
|
|
|
:class="{'has-background': background}"
|
|
|
|
role="navigation"
|
|
|
|
aria-label="main navigation"
|
|
|
|
v-if="userAuthenticated && (userInfo && userInfo.type === authTypes.USER)">
|
2019-11-03 13:44:40 +01:00
|
|
|
<div class="navbar-brand">
|
|
|
|
<router-link :to="{name: 'home'}" class="navbar-item logo">
|
2020-06-14 22:43:08 +02:00
|
|
|
<img src="/images/logo-full-pride.svg" alt="Vikunja" v-if="(new Date()).getMonth() === 5"/>
|
|
|
|
<img src="/images/logo-full.svg" alt="Vikunja" v-else/>
|
2019-11-03 13:44:40 +01:00
|
|
|
</router-link>
|
2020-06-25 23:56:41 +02:00
|
|
|
<a
|
|
|
|
@click="menuActive = true"
|
|
|
|
class="menu-show-button"
|
|
|
|
:class="{'is-visible': !menuActive}"
|
|
|
|
>
|
|
|
|
<icon icon="bars"></icon>
|
|
|
|
</a>
|
2019-11-03 13:44:40 +01:00
|
|
|
</div>
|
2020-06-25 23:56:41 +02:00
|
|
|
<a
|
|
|
|
@click="menuActive = true"
|
|
|
|
class="menu-show-button"
|
|
|
|
>
|
2020-06-12 19:32:37 +02:00
|
|
|
<icon icon="bars"></icon>
|
|
|
|
</a>
|
2020-05-31 21:17:10 +02:00
|
|
|
<div class="list-title" v-if="currentList.id">
|
|
|
|
<h1
|
|
|
|
class="title"
|
|
|
|
:style="{ 'opacity': currentList.title === '' ? '0': '1' }">
|
|
|
|
{{ currentList.title === '' ? 'Loading...': currentList.title}}
|
|
|
|
</h1>
|
2020-08-11 20:18:59 +02:00
|
|
|
<router-link
|
|
|
|
:to="{ name: 'list.edit', params: { id: currentList.id } }"
|
|
|
|
class="icon"
|
|
|
|
v-if="canWriteCurrentList">
|
2020-05-31 21:17:10 +02:00
|
|
|
<icon icon="cog" size="2x"/>
|
|
|
|
</router-link>
|
|
|
|
</div>
|
2019-11-03 13:44:40 +01:00
|
|
|
<div class="navbar-end">
|
2019-12-18 22:30:20 +01:00
|
|
|
<div v-if="updateAvailable" class="update-notification">
|
|
|
|
<p>There is an update for Vikunja available!</p>
|
|
|
|
<a @click="refreshApp()" class="button is-primary noshadow">Update Now</a>
|
|
|
|
</div>
|
2019-11-03 13:44:40 +01:00
|
|
|
<div class="user">
|
2020-08-02 19:17:29 +02:00
|
|
|
<img :src="userAvatar" class="avatar" alt=""/>
|
2019-11-03 13:44:40 +01:00
|
|
|
<div class="dropdown is-right is-active">
|
|
|
|
<div class="dropdown-trigger">
|
2020-07-29 22:01:54 +02:00
|
|
|
<button class="button noshadow" @click.stop="userMenuActive = !userMenuActive">
|
2020-05-08 20:43:51 +02:00
|
|
|
<span class="username">{{userInfo.username}}</span>
|
2019-11-03 13:44:40 +01:00
|
|
|
<span class="icon is-small">
|
2019-04-23 21:50:37 +02:00
|
|
|
<icon icon="chevron-down"/>
|
|
|
|
</span>
|
2019-11-03 13:44:40 +01:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<transition name="fade">
|
|
|
|
<div class="dropdown-menu" v-if="userMenuActive">
|
|
|
|
<div class="dropdown-content">
|
2020-06-17 22:24:37 +02:00
|
|
|
<router-link :to="{name: 'user.settings'}" class="dropdown-item">
|
2020-04-17 22:46:50 +02:00
|
|
|
Settings
|
|
|
|
</router-link>
|
2020-07-18 21:39:30 +02:00
|
|
|
<a :href="imprintUrl" v-if="imprintUrl" class="dropdown-item" target="_blank">Imprint</a>
|
|
|
|
<a :href="privacyPolicyUrl" v-if="privacyPolicyUrl" class="dropdown-item" target="_blank">Privacy policy</a>
|
2020-07-25 16:52:04 +02:00
|
|
|
<a @click="keyboardShortcutsActive = true" class="dropdown-item">Keyboard Shortcuts</a>
|
2019-11-03 13:44:40 +01:00
|
|
|
<a @click="logout()" class="dropdown-item">
|
|
|
|
Logout
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</transition>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</nav>
|
2020-05-08 20:43:51 +02:00
|
|
|
<div v-if="userAuthenticated && (userInfo && userInfo.type === authTypes.USER)">
|
2020-06-25 23:56:41 +02:00
|
|
|
<a @click="menuActive = false" class="menu-hide-button" v-if="menuActive">
|
2019-11-03 13:44:40 +01:00
|
|
|
<icon icon="times"></icon>
|
|
|
|
</a>
|
2020-05-31 21:17:10 +02:00
|
|
|
<div
|
|
|
|
class="app-container"
|
|
|
|
:class="{'has-background': background}"
|
|
|
|
:style="{'background-image': `url(${background})`}"
|
|
|
|
>
|
2020-06-25 23:56:41 +02:00
|
|
|
<div class="namespace-container" :class="{'is-active': menuActive}">
|
2019-11-03 13:44:40 +01:00
|
|
|
<div class="menu top-menu">
|
2020-02-09 15:29:55 +01:00
|
|
|
<router-link :to="{name: 'home'}" class="logo">
|
|
|
|
<img src="/images/logo-full.svg" alt="Vikunja"/>
|
|
|
|
</router-link>
|
2019-11-03 13:44:40 +01:00
|
|
|
<ul class="menu-list">
|
|
|
|
<li>
|
|
|
|
<router-link :to="{ name: 'home'}">
|
2018-12-25 16:03:51 +01:00
|
|
|
<span class="icon">
|
|
|
|
<icon icon="calendar"/>
|
|
|
|
</span>
|
2019-11-03 13:44:40 +01:00
|
|
|
Overview
|
|
|
|
</router-link>
|
|
|
|
</li>
|
|
|
|
<li>
|
2020-06-17 22:24:37 +02:00
|
|
|
<router-link :to="{ name: 'tasks.range', params: {type: 'week'}}">
|
2018-12-25 23:41:55 +01:00
|
|
|
<span class="icon">
|
2020-02-14 17:38:48 +01:00
|
|
|
<icon icon="calendar-week"/>
|
2018-12-25 23:41:55 +01:00
|
|
|
</span>
|
2020-02-14 17:38:48 +01:00
|
|
|
Next Week
|
2019-11-03 13:44:40 +01:00
|
|
|
</router-link>
|
|
|
|
</li>
|
|
|
|
<li>
|
2020-06-17 22:24:37 +02:00
|
|
|
<router-link :to="{ name: 'tasks.range', params: {type: 'month'}}">
|
2018-12-25 23:41:55 +01:00
|
|
|
<span class="icon">
|
2020-02-14 17:38:48 +01:00
|
|
|
<icon :icon="['far', 'calendar-alt']"/>
|
2018-12-25 23:41:55 +01:00
|
|
|
</span>
|
2020-02-14 17:38:48 +01:00
|
|
|
Next Month
|
2019-11-03 13:44:40 +01:00
|
|
|
</router-link>
|
|
|
|
</li>
|
|
|
|
<li>
|
2020-06-17 22:24:37 +02:00
|
|
|
<router-link :to="{ name: 'teams.index'}">
|
2018-12-25 16:03:51 +01:00
|
|
|
<span class="icon">
|
|
|
|
<icon icon="users"/>
|
|
|
|
</span>
|
2019-11-03 13:44:40 +01:00
|
|
|
Teams
|
|
|
|
</router-link>
|
|
|
|
</li>
|
|
|
|
<li>
|
2020-06-17 00:20:37 +02:00
|
|
|
<router-link :to="{ name: 'namespaces.index'}">
|
2018-12-25 16:03:51 +01:00
|
|
|
<span class="icon">
|
|
|
|
<icon icon="layer-group"/>
|
|
|
|
</span>
|
2020-06-17 00:20:37 +02:00
|
|
|
Namespaces & Lists
|
2019-11-03 13:44:40 +01:00
|
|
|
</router-link>
|
|
|
|
</li>
|
|
|
|
<li>
|
2020-06-17 22:24:37 +02:00
|
|
|
<router-link :to="{ name: 'labels.index'}">
|
2019-03-07 20:48:40 +01:00
|
|
|
<span class="icon">
|
|
|
|
<icon icon="tags"/>
|
|
|
|
</span>
|
2019-11-03 13:44:40 +01:00
|
|
|
Labels
|
|
|
|
</router-link>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</div>
|
2020-07-25 16:52:04 +02:00
|
|
|
|
|
|
|
<a @click="menuActive = false" class="collapse-menu-button" v-shortkey="['ctrl', 'e']" @shortkey="() => menuActive = !menuActive">
|
|
|
|
Collapse Menu
|
|
|
|
</a>
|
|
|
|
|
2019-11-03 13:44:40 +01:00
|
|
|
<aside class="menu namespaces-lists">
|
|
|
|
<div class="spinner" :class="{ 'is-loading': namespaceService.loading}"></div>
|
|
|
|
<template v-for="n in namespaces">
|
|
|
|
<div :key="n.id">
|
2020-06-12 20:02:32 +02:00
|
|
|
<router-link
|
|
|
|
v-tooltip.right="'Settings'"
|
2020-06-17 22:24:37 +02:00
|
|
|
:to="{name: 'namespace.edit', params: {id: n.id} }"
|
2020-06-12 20:02:32 +02:00
|
|
|
class="nsettings"
|
2019-11-03 13:44:40 +01:00
|
|
|
v-if="n.id > 0">
|
2018-12-25 16:03:51 +01:00
|
|
|
<span class="icon">
|
|
|
|
<icon icon="cog"/>
|
|
|
|
</span>
|
2019-11-03 13:44:40 +01:00
|
|
|
</router-link>
|
2020-06-12 20:02:32 +02:00
|
|
|
<router-link
|
|
|
|
v-tooltip="'Add a new list in the ' + n.title + ' namespace'"
|
2020-06-17 22:24:37 +02:00
|
|
|
:to="{ name: 'list.create', params: { id: n.id} }"
|
2020-06-12 20:02:32 +02:00
|
|
|
class="nsettings"
|
2020-06-17 22:24:37 +02:00
|
|
|
:key="n.id + 'list.create'"
|
2020-06-12 20:02:32 +02:00
|
|
|
v-if="n.id > 0">
|
2018-12-25 16:03:51 +01:00
|
|
|
<span class="icon">
|
|
|
|
<icon icon="plus"/>
|
|
|
|
</span>
|
2019-11-03 13:44:40 +01:00
|
|
|
</router-link>
|
2020-06-12 20:02:32 +02:00
|
|
|
<label
|
|
|
|
class="menu-label"
|
|
|
|
v-tooltip="n.title + ' (' + n.lists.length + ')'"
|
|
|
|
:for="n.id + 'checker'">
|
2020-03-25 22:27:29 +01:00
|
|
|
<span class="name">
|
2020-06-12 20:02:32 +02:00
|
|
|
<span
|
|
|
|
class="color-bubble"
|
|
|
|
v-if="n.hexColor !== ''"
|
|
|
|
:style="{ backgroundColor: n.hexColor }">
|
|
|
|
</span>
|
2020-05-16 12:31:16 +02:00
|
|
|
{{n.title}} ({{n.lists.length}})
|
2020-03-22 21:40:13 +01:00
|
|
|
</span>
|
2019-11-03 13:44:40 +01:00
|
|
|
</label>
|
|
|
|
</div>
|
2020-06-12 20:02:32 +02:00
|
|
|
<input
|
|
|
|
:key="n.id + 'checker'"
|
|
|
|
type="checkbox"
|
|
|
|
checked="checked"
|
|
|
|
:id="n.id + 'checker'"
|
|
|
|
class="checkinput"/>
|
2019-11-03 13:44:40 +01:00
|
|
|
<div class="more-container" :key="n.id + 'child'">
|
2020-06-12 20:02:32 +02:00
|
|
|
<ul class="menu-list can-be-hidden">
|
2019-11-03 13:44:40 +01:00
|
|
|
<li v-for="l in n.lists" :key="l.id">
|
2020-06-12 20:02:32 +02:00
|
|
|
<router-link
|
|
|
|
:to="{ name: 'list.index', params: { listId: l.id} }"
|
|
|
|
:class="{'router-link-exact-active': currentList.id === l.id}">
|
2020-03-25 22:27:29 +01:00
|
|
|
<span class="name">
|
2020-06-12 20:02:32 +02:00
|
|
|
<span
|
|
|
|
class="color-bubble"
|
|
|
|
v-if="l.hexColor !== ''"
|
|
|
|
:style="{ backgroundColor: l.hexColor }">
|
|
|
|
</span>
|
2020-03-25 22:27:29 +01:00
|
|
|
{{l.title}}
|
|
|
|
</span>
|
2019-11-03 13:44:40 +01:00
|
|
|
</router-link>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
<label class="hidden-hint" :for="n.id + 'checker'">
|
|
|
|
Show hidden lists ({{n.lists.length}})...
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</aside>
|
2020-01-31 17:33:34 +01:00
|
|
|
<a class="menu-bottom-link" target="_blank" href="https://vikunja.io">Powered by Vikunja</a>
|
2019-11-03 13:44:40 +01:00
|
|
|
</div>
|
2020-06-25 23:56:41 +02:00
|
|
|
<div
|
|
|
|
class="app-content"
|
|
|
|
:class="{
|
|
|
|
'fullpage-overlay': fullpage,
|
|
|
|
'is-menu-enabled': menuActive,
|
|
|
|
}"
|
|
|
|
>
|
|
|
|
<a class="mobile-overlay" v-if="menuActive" @click="menuActive = false"></a>
|
2019-11-03 13:44:40 +01:00
|
|
|
<transition name="fade">
|
|
|
|
<router-view/>
|
|
|
|
</transition>
|
2020-07-25 16:52:04 +02:00
|
|
|
<a class="keyboard-shortcuts-button" @click="keyboardShortcutsActive = true">
|
|
|
|
<icon icon="keyboard"/>
|
|
|
|
</a>
|
2019-11-03 13:44:40 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2020-05-31 21:31:42 +02:00
|
|
|
<div
|
|
|
|
v-else-if="userAuthenticated && (userInfo && userInfo.type === authTypes.LINK_SHARE)"
|
|
|
|
class="link-share-container"
|
|
|
|
:class="{'has-background': background}"
|
|
|
|
:style="{'background-image': `url(${background})`}"
|
|
|
|
>
|
2019-11-03 13:44:40 +01:00
|
|
|
<div class="container has-text-centered link-share-view">
|
|
|
|
<div class="column is-10 is-offset-1">
|
|
|
|
<img src="/images/logo-full.svg" alt="Vikunja" class="logo"/>
|
2020-05-31 21:31:42 +02:00
|
|
|
<h1
|
|
|
|
class="title"
|
|
|
|
:style="{ 'opacity': currentList.title === '' ? '0': '1' }">
|
|
|
|
{{ currentList.title === '' ? 'Loading...': currentList.title}}
|
|
|
|
</h1>
|
|
|
|
<div class="box has-text-left view">
|
2019-11-03 13:44:40 +01:00
|
|
|
<div class="logout">
|
2020-05-31 21:31:42 +02:00
|
|
|
<a @click="logout()" class="button">
|
2019-11-03 13:44:40 +01:00
|
|
|
<span>Logout</span>
|
|
|
|
<span class="icon is-small">
|
2019-09-09 19:55:43 +02:00
|
|
|
<icon icon="sign-out-alt"/>
|
|
|
|
</span>
|
2019-11-03 13:44:40 +01:00
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
<router-view/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-else>
|
2020-05-12 15:18:37 +02:00
|
|
|
<div class="noauth-container">
|
|
|
|
<img src="/images/logo-full.svg" alt="Vikunja"/>
|
|
|
|
<div class="message is-info" v-if="motd !== ''">
|
|
|
|
<div class="message-header">
|
|
|
|
<p>Info</p>
|
|
|
|
</div>
|
|
|
|
<div class="message-body">
|
|
|
|
{{ motd }}
|
2019-12-25 17:38:49 +01:00
|
|
|
</div>
|
2019-11-03 13:44:40 +01:00
|
|
|
</div>
|
2020-05-12 15:18:37 +02:00
|
|
|
<router-view/>
|
2019-11-03 13:44:40 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
2020-03-02 21:19:26 +01:00
|
|
|
<notification/>
|
2019-11-03 13:44:40 +01:00
|
|
|
</div>
|
|
|
|
<div class="app offline" v-else>
|
|
|
|
<div class="offline-message">
|
|
|
|
<h1>You are offline.</h1>
|
|
|
|
<p>Please check your network connection and try again.</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
2020-07-25 16:52:04 +02:00
|
|
|
|
|
|
|
<transition name="fade">
|
|
|
|
<keyboard-shortcuts v-if="keyboardShortcutsActive" @close="keyboardShortcutsActive = false"/>
|
|
|
|
</transition>
|
2019-11-03 13:44:40 +01:00
|
|
|
</div>
|
2018-08-28 22:50:22 +02:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2019-03-02 11:25:10 +01:00
|
|
|
import router from './router'
|
2020-05-08 20:43:51 +02:00
|
|
|
import {mapState} from 'vuex'
|
2019-09-09 19:55:43 +02:00
|
|
|
|
2019-03-02 11:25:10 +01:00
|
|
|
import NamespaceService from './services/namespace'
|
2019-09-09 19:55:43 +02:00
|
|
|
import authTypes from './models/authTypes'
|
2020-08-11 20:18:59 +02:00
|
|
|
import Rights from './models/rights.json'
|
2018-09-07 08:42:17 +02:00
|
|
|
|
2019-12-18 22:30:20 +01:00
|
|
|
import swEvents from './ServiceWorker/events'
|
2020-06-17 22:15:59 +02:00
|
|
|
import Notification from './components/misc/notification'
|
2020-05-08 21:07:33 +02:00
|
|
|
import {CURRENT_LIST, IS_FULLPAGE, ONLINE} from './store/mutation-types'
|
2020-07-25 16:52:04 +02:00
|
|
|
import KeyboardShortcuts from './components/misc/keyboard-shortcuts'
|
2019-12-18 22:30:20 +01:00
|
|
|
|
2019-03-02 11:25:10 +01:00
|
|
|
export default {
|
|
|
|
name: 'app',
|
2020-04-01 22:13:57 +02:00
|
|
|
components: {
|
2020-07-25 16:52:04 +02:00
|
|
|
KeyboardShortcuts,
|
2020-04-01 22:13:57 +02:00
|
|
|
Notification,
|
|
|
|
},
|
2019-03-02 11:25:10 +01:00
|
|
|
data() {
|
|
|
|
return {
|
2019-03-03 13:50:06 +01:00
|
|
|
namespaceService: NamespaceService,
|
2020-06-25 23:56:41 +02:00
|
|
|
menuActive: true,
|
2018-12-25 23:41:55 +01:00
|
|
|
currentDate: new Date(),
|
2019-04-23 21:50:37 +02:00
|
|
|
userMenuActive: false,
|
2019-09-09 19:55:43 +02:00
|
|
|
authTypes: authTypes,
|
2020-07-25 16:52:04 +02:00
|
|
|
keyboardShortcutsActive: false,
|
2019-12-18 22:30:20 +01:00
|
|
|
|
|
|
|
// Service Worker stuff
|
|
|
|
updateAvailable: false,
|
|
|
|
registration: null,
|
|
|
|
refreshing: false,
|
2019-03-02 11:25:10 +01:00
|
|
|
}
|
|
|
|
},
|
2018-11-01 22:34:29 +01:00
|
|
|
beforeMount() {
|
2019-10-16 23:27:21 +02:00
|
|
|
// Check if the user is offline, show a message then
|
2020-05-08 20:43:51 +02:00
|
|
|
this.$store.commit(ONLINE, navigator.onLine)
|
|
|
|
window.addEventListener('online', () => this.$store.commit(ONLINE, navigator.onLine));
|
|
|
|
window.addEventListener('offline', () => this.$store.commit(ONLINE, navigator.onLine));
|
2019-10-16 23:27:21 +02:00
|
|
|
|
2019-03-02 11:25:10 +01:00
|
|
|
// Password reset
|
2019-10-16 23:27:21 +02:00
|
|
|
if (this.$route.query.userPasswordReset !== undefined) {
|
2018-11-01 22:34:29 +01:00
|
|
|
localStorage.removeItem('passwordResetToken') // Delete an eventually preexisting old token
|
|
|
|
localStorage.setItem('passwordResetToken', this.$route.query.userPasswordReset)
|
2020-06-17 22:24:37 +02:00
|
|
|
router.push({name: 'user.password-reset.reset'})
|
2018-11-01 22:34:29 +01:00
|
|
|
}
|
2019-03-02 11:25:10 +01:00
|
|
|
// Email verification
|
2019-10-16 23:27:21 +02:00
|
|
|
if (this.$route.query.userEmailConfirm !== undefined) {
|
2019-03-02 11:25:10 +01:00
|
|
|
localStorage.removeItem('emailConfirmToken') // Delete an eventually preexisting old token
|
|
|
|
localStorage.setItem('emailConfirmToken', this.$route.query.userEmailConfirm)
|
2020-06-17 22:24:37 +02:00
|
|
|
router.push({name: 'user.login'})
|
2018-11-01 23:58:59 +01:00
|
|
|
}
|
2018-11-01 22:34:29 +01:00
|
|
|
},
|
2020-05-12 15:08:17 +02:00
|
|
|
beforeCreate() {
|
2020-05-08 20:43:51 +02:00
|
|
|
this.$store.dispatch('config/update')
|
|
|
|
this.$store.dispatch('auth/checkAuth')
|
2020-05-16 12:02:30 +02:00
|
|
|
.then(() => {
|
|
|
|
// Check if the user is already logged in, if so, redirect them to the homepage
|
|
|
|
if (
|
|
|
|
!this.userAuthenticated &&
|
2020-06-17 22:24:37 +02:00
|
|
|
this.$route.name !== 'user.login' &&
|
|
|
|
this.$route.name !== 'user.password-reset.request' &&
|
|
|
|
this.$route.name !== 'user.password-reset.reset' &&
|
|
|
|
this.$route.name !== 'user.register' &&
|
|
|
|
this.$route.name !== 'link-share.auth'
|
2020-05-16 12:02:30 +02:00
|
|
|
) {
|
2020-06-17 22:24:37 +02:00
|
|
|
router.push({name: 'user.login'})
|
2020-05-16 12:02:30 +02:00
|
|
|
}
|
2020-05-08 20:43:51 +02:00
|
|
|
|
2020-05-16 12:02:30 +02:00
|
|
|
if (this.userAuthenticated && this.userInfo.type === authTypes.USER && (this.$route.params.name === 'home' || this.namespaces.length === 0)) {
|
|
|
|
this.loadNamespaces()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
created() {
|
2019-12-18 22:30:20 +01:00
|
|
|
|
|
|
|
// Service worker communication
|
2020-05-08 20:43:51 +02:00
|
|
|
document.addEventListener(swEvents.SW_UPDATED, this.showRefreshUI, {once: true})
|
2019-12-18 22:30:20 +01:00
|
|
|
|
2020-06-21 13:19:54 +02:00
|
|
|
if (navigator && navigator.serviceWorker) {
|
|
|
|
navigator.serviceWorker.addEventListener(
|
|
|
|
'controllerchange', () => {
|
|
|
|
if (this.refreshing) return;
|
|
|
|
this.refreshing = true;
|
|
|
|
window.location.reload();
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2019-12-19 21:50:07 +01:00
|
|
|
|
2020-06-25 23:56:41 +02:00
|
|
|
// Hide the menu by default on mobile
|
|
|
|
if (window.innerWidth < 770) {
|
|
|
|
this.menuActive = false
|
|
|
|
}
|
|
|
|
|
2020-06-12 20:02:32 +02:00
|
|
|
// Try renewing the token every time vikunja is loaded initially
|
|
|
|
// (When opening the browser the focus event is not fired)
|
|
|
|
this.$store.dispatch('auth/renewToken')
|
|
|
|
|
|
|
|
// Check if the token is still valid if the window gets focus again to maybe renew it
|
|
|
|
window.addEventListener('focus', () => {
|
2020-06-12 20:11:23 +02:00
|
|
|
|
2020-06-21 13:19:54 +02:00
|
|
|
if (!this.userAuthenticated) {
|
2020-06-12 20:11:23 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const expiresIn = this.userInfo.exp - +new Date() / 1000
|
|
|
|
|
|
|
|
// If the token expiry is negative, it is already expired and we have no choice but to redirect
|
|
|
|
// the user to the login page
|
|
|
|
if (expiresIn < 0) {
|
|
|
|
this.$store.dispatch('auth/checkAuth')
|
2020-06-17 22:24:37 +02:00
|
|
|
router.push({name: 'user.login'})
|
2020-06-12 20:11:23 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-12 20:02:32 +02:00
|
|
|
// Check if the token is valid for less than 60 hours and renew if thats the case
|
2020-06-12 20:11:23 +02:00
|
|
|
if (expiresIn < 60 * 3600) {
|
2020-06-12 20:02:32 +02:00
|
|
|
this.$store.dispatch('auth/renewToken')
|
|
|
|
console.log('renewed token')
|
|
|
|
}
|
|
|
|
})
|
2020-07-29 22:01:54 +02:00
|
|
|
|
|
|
|
// This will hide the menu once clicked outside of it
|
|
|
|
this.$nextTick(() => document.addEventListener('click', () => this.userMenuActive = false))
|
2019-03-02 11:25:10 +01:00
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
// call the method again if the route changes
|
2020-04-26 01:11:34 +02:00
|
|
|
'$route': 'doStuffAfterRoute',
|
2019-03-02 11:25:10 +01:00
|
|
|
},
|
2020-05-08 20:43:51 +02:00
|
|
|
computed: mapState({
|
|
|
|
userInfo: state => state.auth.info,
|
2020-08-02 19:17:29 +02:00
|
|
|
userAvatar: state => state.auth.avatarUrl,
|
2020-05-08 20:43:51 +02:00
|
|
|
userAuthenticated: state => state.auth.authenticated,
|
|
|
|
motd: state => state.config.motd,
|
|
|
|
online: ONLINE,
|
|
|
|
fullpage: IS_FULLPAGE,
|
|
|
|
namespaces(state) {
|
2020-06-17 00:20:37 +02:00
|
|
|
return state.namespaces.namespaces.filter(n => !n.isArchived)
|
2020-05-08 20:43:51 +02:00
|
|
|
},
|
2020-05-08 21:07:33 +02:00
|
|
|
currentList: CURRENT_LIST,
|
2020-05-31 21:17:10 +02:00
|
|
|
background: 'background',
|
2020-07-18 21:39:30 +02:00
|
|
|
imprintUrl: state => state.config.legal.imprintUrl,
|
|
|
|
privacyPolicyUrl: state => state.config.legal.privacyPolicyUrl,
|
2020-08-11 20:18:59 +02:00
|
|
|
canWriteCurrentList: state => state.currentList.maxRight > Rights.READ,
|
2020-05-08 20:43:51 +02:00
|
|
|
}),
|
2019-03-02 11:25:10 +01:00
|
|
|
methods: {
|
|
|
|
logout() {
|
2020-05-08 20:43:51 +02:00
|
|
|
this.$store.dispatch('auth/logout')
|
2020-06-17 22:24:37 +02:00
|
|
|
router.push({name: 'user.login'})
|
2019-03-02 11:25:10 +01:00
|
|
|
},
|
|
|
|
loadNamespaces() {
|
2020-05-08 20:43:51 +02:00
|
|
|
this.$store.dispatch('namespaces/loadNamespaces')
|
2018-09-09 17:23:06 +02:00
|
|
|
},
|
2019-10-16 23:27:21 +02:00
|
|
|
loadNamespacesIfNeeded(e) {
|
2020-05-08 20:43:51 +02:00
|
|
|
if (this.userAuthenticated && (this.userInfo && this.userInfo.type === authTypes.USER) && (e.name === 'home' || this.namespaces.length === 0)) {
|
2019-03-02 11:25:10 +01:00
|
|
|
this.loadNamespaces()
|
|
|
|
}
|
2018-09-09 16:17:56 +02:00
|
|
|
},
|
2018-12-25 16:03:51 +01:00
|
|
|
doStuffAfterRoute(e) {
|
2020-07-07 22:07:13 +02:00
|
|
|
// this.setTitle('') // Reset the title if the page component does not set one itself
|
|
|
|
|
2020-05-09 22:31:33 +02:00
|
|
|
if (this.$store.state[IS_FULLPAGE]) {
|
2020-05-09 22:24:42 +02:00
|
|
|
this.$store.commit(IS_FULLPAGE, false)
|
|
|
|
}
|
2020-05-09 22:31:33 +02:00
|
|
|
|
2018-12-25 16:03:51 +01:00
|
|
|
this.loadNamespacesIfNeeded(e)
|
2020-02-09 13:07:31 +01:00
|
|
|
this.userMenuActive = false
|
2020-05-08 21:07:33 +02:00
|
|
|
|
2020-06-25 23:56:41 +02:00
|
|
|
// If the menu is active on desktop, don't hide it because that would confuse the user
|
|
|
|
if (window.innerWidth < 770) {
|
|
|
|
this.menuActive = false
|
|
|
|
}
|
|
|
|
|
2020-05-08 21:07:33 +02:00
|
|
|
// Reset the current list highlight in menu if the current list is not list related.
|
|
|
|
if (
|
|
|
|
this.$route.name === 'home' ||
|
2020-06-17 22:24:37 +02:00
|
|
|
this.$route.name === 'namespace.edit' ||
|
|
|
|
this.$route.name === 'teams.index' ||
|
|
|
|
this.$route.name === 'teams.edit' ||
|
|
|
|
this.$route.name === 'tasks.range' ||
|
|
|
|
this.$route.name === 'labels.index' ||
|
|
|
|
this.$route.name === 'migrate.start' ||
|
2020-05-08 21:07:33 +02:00
|
|
|
this.$route.name === 'migrate.wunderlist' ||
|
2020-06-17 22:24:37 +02:00
|
|
|
this.$route.name === 'user.settings' ||
|
2020-06-17 00:20:37 +02:00
|
|
|
this.$route.name === 'namespaces.index'
|
2020-05-08 21:07:33 +02:00
|
|
|
) {
|
2020-05-31 21:17:10 +02:00
|
|
|
this.$store.commit(CURRENT_LIST, {})
|
2020-05-08 21:07:33 +02:00
|
|
|
}
|
2018-12-25 16:03:51 +01:00
|
|
|
},
|
2020-05-08 20:43:51 +02:00
|
|
|
showRefreshUI(e) {
|
2019-12-18 22:30:20 +01:00
|
|
|
console.log('recieved refresh event', e)
|
|
|
|
this.registration = e.detail;
|
|
|
|
this.updateAvailable = true;
|
|
|
|
},
|
2020-05-08 20:43:51 +02:00
|
|
|
refreshApp() {
|
2019-12-18 22:30:20 +01:00
|
|
|
this.updateExists = false;
|
2020-05-08 20:43:51 +02:00
|
|
|
if (!this.registration || !this.registration.waiting) {
|
|
|
|
return;
|
|
|
|
}
|
2019-12-18 22:30:20 +01:00
|
|
|
// Notify the service worker to actually do the update
|
|
|
|
this.registration.waiting.postMessage('skipWaiting');
|
|
|
|
},
|
2019-03-02 11:25:10 +01:00
|
|
|
},
|
|
|
|
}
|
2018-08-28 22:50:22 +02:00
|
|
|
</script>
|