feat: add message to add to home screen on mobile
This commit is contained in:
parent
169feaaf0f
commit
3c9083b90d
@ -12,6 +12,7 @@
|
||||
<keyboard-shortcuts v-if="keyboardShortcutsActive"/>
|
||||
|
||||
<Teleport to="body">
|
||||
<AddToHomeScreen/>
|
||||
<UpdateNotification/>
|
||||
<Notification/>
|
||||
</Teleport>
|
||||
@ -43,6 +44,7 @@ import {useBaseStore} from '@/stores/base'
|
||||
|
||||
import {useColorScheme} from '@/composables/useColorScheme'
|
||||
import {useBodyClass} from '@/composables/useBodyClass'
|
||||
import AddToHomeScreen from '@/components/home/AddToHomeScreen.vue'
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
const authStore = useAuthStore()
|
||||
|
80
src/components/home/AddToHomeScreen.vue
Normal file
80
src/components/home/AddToHomeScreen.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="shouldShowMessage"
|
||||
class="add-to-home-screen"
|
||||
:class="{'has-update-available': hasUpdateAvailable}"
|
||||
>
|
||||
<icon icon="arrow-up-from-bracket" class="add-icon"/>
|
||||
<p>
|
||||
{{ $t('home.addToHomeScreen') }}
|
||||
</p>
|
||||
<BaseButton @click="() => hideMessage = true" class="hide-button">
|
||||
<icon icon="x"/>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import {useLocalStorage} from '@vueuse/core'
|
||||
import {computed} from 'vue'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
|
||||
const hideMessage = useLocalStorage('hideAddToHomeScreenMessage', false)
|
||||
const hasUpdateAvailable = computed(() => baseStore.updateAvailable)
|
||||
|
||||
const shouldShowMessage = computed(() => {
|
||||
if (hideMessage.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && window.matchMedia('(display-mode: standalone)').matches) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.add-to-home-screen {
|
||||
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;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: .5rem 1rem;
|
||||
background: var(--grey-900);
|
||||
border-radius: $radius;
|
||||
font-size: .9rem;
|
||||
color: var(--grey-200);
|
||||
|
||||
@media screen and (min-width: $tablet) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.has-update-available {
|
||||
bottom: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
color: var(--primary-light);
|
||||
}
|
||||
|
||||
.hide-button {
|
||||
padding: .25rem .5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
@ -12,9 +12,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref} from 'vue'
|
||||
import {computed, ref} from 'vue'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
|
||||
const updateAvailable = ref(false)
|
||||
const baseStore = useBaseStore()
|
||||
|
||||
const updateAvailable = computed(() => baseStore.updateAvailable)
|
||||
const registration = ref(null)
|
||||
const refreshing = ref(false)
|
||||
|
||||
@ -31,11 +34,11 @@ navigator?.serviceWorker?.addEventListener(
|
||||
function showRefreshUI(e: Event) {
|
||||
console.log('recieved refresh event', e)
|
||||
registration.value = e.detail
|
||||
updateAvailable.value = true
|
||||
baseStore.setUpdateAvailable(true)
|
||||
}
|
||||
|
||||
function refreshApp() {
|
||||
updateAvailable.value = false
|
||||
baseStore.setUpdateAvailable(false)
|
||||
if (!registration.value || !registration.value.waiting) {
|
||||
return
|
||||
}
|
||||
@ -65,7 +68,6 @@ function refreshApp() {
|
||||
border-radius: $radius;
|
||||
font-size: .9rem;
|
||||
color: var(--grey-900);
|
||||
|
||||
}
|
||||
|
||||
.update-notification__message {
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
faAngleRight,
|
||||
faArchive,
|
||||
faArrowLeft,
|
||||
faArrowUpFromBracket,
|
||||
faBars,
|
||||
faBell,
|
||||
faCalendar,
|
||||
@ -56,7 +57,7 @@ import {
|
||||
faTimes,
|
||||
faTrashAlt,
|
||||
faUser,
|
||||
faUsers,
|
||||
faUsers, faX,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import {
|
||||
faBellSlash,
|
||||
@ -71,7 +72,7 @@ import {
|
||||
} from '@fortawesome/free-regular-svg-icons'
|
||||
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
|
||||
|
||||
import type { FontAwesomeIcon as FontAwesomeIconFixedTypes } from '@/types/vue-fontawesome'
|
||||
import type {FontAwesomeIcon as FontAwesomeIconFixedTypes} from '@/types/vue-fontawesome'
|
||||
|
||||
library.add(faAlignLeft)
|
||||
library.add(faAngleRight)
|
||||
@ -139,6 +140,8 @@ library.add(faTimesCircle)
|
||||
library.add(faTrashAlt)
|
||||
library.add(faUser)
|
||||
library.add(faUsers)
|
||||
library.add(faArrowUpFromBracket)
|
||||
library.add(faX)
|
||||
|
||||
// overwriting the wrong types
|
||||
export default FontAwesomeIcon as unknown as FontAwesomeIconFixedTypes
|
@ -5,6 +5,7 @@
|
||||
"welcomeDay": "Hi {username}!",
|
||||
"welcomeEvening": "Good Evening {username}!",
|
||||
"lastViewed": "Last viewed",
|
||||
"addToHomeScreen": "Add this app to your home screen for faster access and improved experience.",
|
||||
"project": {
|
||||
"importText": "Import your projects and tasks from other services into Vikunja:",
|
||||
"import": "Import your data into Vikunja"
|
||||
|
@ -28,6 +28,7 @@ export const useBaseStore = defineStore('base', () => {
|
||||
const keyboardShortcutsActive = ref(false)
|
||||
const quickActionsActive = ref(false)
|
||||
const logoVisible = ref(true)
|
||||
const updateAvailable = ref(false)
|
||||
|
||||
function setLoading(newLoading: boolean) {
|
||||
loading.value = newLoading
|
||||
@ -77,6 +78,10 @@ export const useBaseStore = defineStore('base', () => {
|
||||
function setReady(value: boolean) {
|
||||
ready.value = value
|
||||
}
|
||||
|
||||
function setUpdateAvailable(value: boolean) {
|
||||
updateAvailable.value = value
|
||||
}
|
||||
|
||||
async function handleSetCurrentProject(
|
||||
{project, forceUpdate = false}: {project: IProject | null, forceUpdate?: boolean},
|
||||
@ -135,6 +140,7 @@ export const useBaseStore = defineStore('base', () => {
|
||||
keyboardShortcutsActive: readonly(keyboardShortcutsActive),
|
||||
quickActionsActive: readonly(quickActionsActive),
|
||||
logoVisible: readonly(logoVisible),
|
||||
updateAvailable: readonly(updateAvailable),
|
||||
|
||||
setLoading,
|
||||
setReady,
|
||||
@ -145,6 +151,7 @@ export const useBaseStore = defineStore('base', () => {
|
||||
setBackground,
|
||||
setBlurHash,
|
||||
setLogoVisible,
|
||||
setUpdateAvailable,
|
||||
|
||||
handleSetCurrentProject,
|
||||
loadApp,
|
||||
|
Loading…
x
Reference in New Issue
Block a user