feat: move update from navigation to app #2997
21
src/App.vue
21
src/App.vue
|
@ -8,9 +8,13 @@
|
||||||
<no-auth-wrapper v-else>
|
<no-auth-wrapper v-else>
|
||||||
<router-view/>
|
<router-view/>
|
||||||
</no-auth-wrapper>
|
</no-auth-wrapper>
|
||||||
<Notification/>
|
|
||||||
|
|
||||||
<keyboard-shortcuts v-if="keyboardShortcutsActive"/>
|
<keyboard-shortcuts v-if="keyboardShortcutsActive"/>
|
||||||
|
|
||||||
|
<Teleport to="body">
|
||||||
|
<UpdateNotification/>
|
||||||
|
<Notification/>
|
||||||
|
</Teleport>
|
||||||
</ready>
|
</ready>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -19,23 +23,26 @@ import {computed, watch} from 'vue'
|
||||||
import {useRoute, useRouter} from 'vue-router'
|
import {useRoute, useRouter} from 'vue-router'
|
||||||
import {useI18n} from 'vue-i18n'
|
import {useI18n} from 'vue-i18n'
|
||||||
import isTouchDevice from 'is-touch-device'
|
import isTouchDevice from 'is-touch-device'
|
||||||
import {success} from '@/message'
|
|
||||||
|
|
||||||
import Notification from '@/components/misc/notification.vue'
|
import Notification from '@/components/misc/notification.vue'
|
||||||
import KeyboardShortcuts from './components/misc/keyboard-shortcuts/index.vue'
|
import UpdateNotification from '@/components/home/UpdateNotification.vue'
|
||||||
|
import KeyboardShortcuts from '@/components/misc/keyboard-shortcuts/index.vue'
|
||||||
|
|
||||||
import TheNavigation from '@/components/home/TheNavigation.vue'
|
import TheNavigation from '@/components/home/TheNavigation.vue'
|
||||||
import ContentAuth from './components/home/contentAuth.vue'
|
import ContentAuth from '@/components/home/contentAuth.vue'
|
||||||
import ContentLinkShare from './components/home/contentLinkShare.vue'
|
import ContentLinkShare from '@/components/home/contentLinkShare.vue'
|
||||||
import NoAuthWrapper from '@/components/misc/no-auth-wrapper.vue'
|
import NoAuthWrapper from '@/components/misc/no-auth-wrapper.vue'
|
||||||
import Ready from '@/components/misc/ready.vue'
|
import Ready from '@/components/misc/ready.vue'
|
||||||
|
|
||||||
import {setLanguage} from './i18n'
|
import {setLanguage} from '@/i18n'
|
||||||
import AccountDeleteService from '@/services/accountDelete'
|
import AccountDeleteService from '@/services/accountDelete'
|
||||||
|
import {success} from '@/message'
|
||||||
|
|
||||||
|
import {useAuthStore} from '@/stores/auth'
|
||||||
import {useBaseStore} from '@/stores/base'
|
import {useBaseStore} from '@/stores/base'
|
||||||
|
|
||||||
import {useColorScheme} from '@/composables/useColorScheme'
|
import {useColorScheme} from '@/composables/useColorScheme'
|
||||||
import {useBodyClass} from '@/composables/useBodyClass'
|
import {useBodyClass} from '@/composables/useBodyClass'
|
||||||
import {useAuthStore} from './stores/auth'
|
|
||||||
|
|
||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
<update/>
|
|
||||||
<BaseButton
|
<BaseButton
|
||||||
@click="openQuickActions"
|
@click="openQuickActions"
|
||||||
class="trigger-button pr-0"
|
class="trigger-button pr-0"
|
||||||
|
@ -95,7 +94,6 @@ import {ref, computed, onMounted, nextTick} from 'vue'
|
||||||
|
|
||||||
import {RIGHTS as Rights} from '@/constants/rights'
|
import {RIGHTS as Rights} from '@/constants/rights'
|
||||||
|
|
||||||
import Update from '@/components/home/update.vue'
|
|
||||||
import ListSettingsDropdown from '@/components/list/list-settings-dropdown.vue'
|
import ListSettingsDropdown from '@/components/list/list-settings-dropdown.vue'
|
||||||
import Dropdown from '@/components/misc/dropdown.vue'
|
import Dropdown from '@/components/misc/dropdown.vue'
|
||||||
import DropdownItem from '@/components/misc/dropdown-item.vue'
|
import DropdownItem from '@/components/misc/dropdown-item.vue'
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="update-notification" v-if="updateAvailable">
|
<div class="update-notification" v-if="updateAvailable">
|
||||||
<p>{{ $t('update.available') }}</p>
|
<p class="update-notification__message">{{ $t('update.available') }}</p>
|
||||||
<x-button @click="refreshApp()" :shadow="false" class="has-no-text-wrap">
|
<x-button
|
||||||
|
@click="refreshApp()"
|
||||||
|
:shadow="false"
|
||||||
|
:wrap="false"
|
||||||
|
>
|
||||||
{{ $t('update.do') }}
|
{{ $t('update.do') }}
|
||||||
</x-button>
|
</x-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,15 +20,13 @@ const refreshing = ref(false)
|
||||||
|
|
||||||
document.addEventListener('swUpdated', showRefreshUI, {once: true})
|
document.addEventListener('swUpdated', showRefreshUI, {once: true})
|
||||||
|
|
||||||
if (navigator && navigator.serviceWorker) {
|
navigator?.serviceWorker?.addEventListener(
|
||||||
navigator.serviceWorker.addEventListener(
|
|
||||||
'controllerchange', () => {
|
'controllerchange', () => {
|
||||||
if (refreshing.value) return
|
if (refreshing.value) return
|
||||||
refreshing.value = true
|
refreshing.value = true
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
function showRefreshUI(e: Event) {
|
function showRefreshUI(e: Event) {
|
||||||
console.log('recieved refresh event', e)
|
console.log('recieved refresh event', e)
|
||||||
|
@ -33,6 +35,7 @@ function showRefreshUI(e: Event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshApp() {
|
function refreshApp() {
|
||||||
|
updateAvailable.value = false
|
||||||
if (!registration.value || !registration.value.waiting) {
|
if (!registration.value || !registration.value.waiting) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -43,39 +46,30 @@ function refreshApp() {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.update-notification {
|
.update-notification {
|
||||||
|
position: fixed;
|
||||||
|
// FIXME: We should prevent usage of z-index or
|
||||||
|
// at least define it centrally
|
||||||
|
// the highest z-index of a modal is .hint-modal with 4500
|
||||||
|
z-index: 5000;
|
||||||
|
bottom: 1rem;
|
||||||
|
inset-inline: 1rem;
|
||||||
|
max-width: max-content;
|
||||||
|
margin-inline: auto;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: $warning;
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
|
background: $warning;
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
font-size: .9rem;
|
font-size: .9rem;
|
||||||
color: var(--grey-900);
|
color: var(--grey-900);
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
position: fixed;
|
|
||||||
bottom: 1rem;
|
|
||||||
width: 450px;
|
|
||||||
left: calc(50vw - 225px);
|
|
||||||
|
|
||||||
@media screen and (max-width: $tablet) {
|
|
||||||
position: fixed;
|
|
||||||
left: 1rem;
|
|
||||||
right: 1rem;
|
|
||||||
bottom: 1rem;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
> * + * {
|
|
||||||
margin-left: .5rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .update-notification {
|
.update-notification__message {
|
||||||
color: var(--grey-200);
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -224,9 +224,4 @@ labelStore.loadAllLabels()
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-touch .content-auth,
|
|
||||||
.content-auth.z-unset {
|
|
||||||
z-index: unset;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
|
@ -8,17 +8,20 @@
|
||||||
'has-no-shadow': !shadow || variant === 'tertiary',
|
'has-no-shadow': !shadow || variant === 'tertiary',
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
|
:style="{
|
||||||
|
'--button-white-space': wrap ? 'break-spaces' : 'nowrap',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<template v-if="icon">
|
<template v-if="icon">
|
||||||
<icon
|
<icon
|
||||||
v-if="showIconOnly"
|
v-if="showIconOnly"
|
||||||
:icon="icon"
|
:icon="icon"
|
||||||
:style="{'color': iconColor !== '' ? iconColor : false}"
|
:style="{'color': iconColor !== '' ? iconColor : undefined}"
|
||||||
/>
|
/>
|
||||||
<span class="icon is-small" v-else>
|
<span class="icon is-small" v-else>
|
||||||
<icon
|
<icon
|
||||||
:icon="icon"
|
:icon="icon"
|
||||||
:style="{'color': iconColor !== '' ? iconColor : false}"
|
:style="{'color': iconColor !== '' ? iconColor : undefined}"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -50,6 +53,7 @@ export interface ButtonProps extends BaseButtonProps {
|
||||||
iconColor?: string
|
iconColor?: string
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
shadow?: boolean
|
shadow?: boolean
|
||||||
|
wrap?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -58,6 +62,7 @@ const {
|
||||||
iconColor = '',
|
iconColor = '',
|
||||||
loading = false,
|
loading = false,
|
||||||
shadow = true,
|
shadow = true,
|
||||||
|
wrap = true,
|
||||||
} = defineProps<ButtonProps>()
|
} = defineProps<ButtonProps>()
|
||||||
|
|
||||||
const variantClass = computed(() => BUTTON_TYPES_MAP[variant])
|
const variantClass = computed(() => BUTTON_TYPES_MAP[variant])
|
||||||
|
@ -67,7 +72,7 @@ const showIconOnly = computed(() => icon !== '' && typeof slots.default === 'und
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.button {
|
:where(.button) {
|
||||||
transition: all $transition;
|
transition: all $transition;
|
||||||
border: 0;
|
border: 0;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
@ -77,7 +82,7 @@ const showIconOnly = computed(() => icon !== '' && typeof slots.default === 'und
|
||||||
min-height: $button-height;
|
min-height: $button-height;
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
white-space: break-spaces;
|
white-space: var(--button-white-space);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: var(--shadow-md);
|
box-shadow: var(--shadow-md);
|
||||||
|
@ -99,7 +104,6 @@ const showIconOnly = computed(() => icon !== '' && typeof slots.default === 'und
|
||||||
&.is-primary.is-outlined:hover {
|
&.is-primary.is-outlined:hover {
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-small {
|
.is-small {
|
||||||
|
|
|
@ -915,7 +915,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"update": {
|
"update": {
|
||||||
"available": "There is an update for Vikunja available!",
|
"available": "There is an update available!",
|
||||||
"do": "Update Now"
|
"do": "Update Now"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
|
|
|
@ -3,7 +3,3 @@
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.has-no-text-wrap {
|
|
||||||
white-space: nowrap !important;
|
|
||||||
}
|
|
||||||
|
|
Reference in New Issue
Block a user