PWA: install banner copy
This commit is contained in:
129
src/App.vue
129
src/App.vue
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { usePuzzleStore } from './stores/puzzle';
|
||||
import { useI18n } from './composables/useI18n';
|
||||
import GameBoard from './components/GameBoard.vue';
|
||||
@@ -16,11 +16,77 @@ const store = usePuzzleStore();
|
||||
const { t, locale, setLocale } = useI18n();
|
||||
const showCustomModal = ref(false);
|
||||
const showGuide = ref(false);
|
||||
const deferredPrompt = ref(null);
|
||||
const canInstall = ref(false);
|
||||
const installDismissed = ref(false);
|
||||
const isCoarsePointer = ref(false);
|
||||
const isStandalone = ref(false);
|
||||
let displayModeMedia = null;
|
||||
|
||||
const installLabel = computed(() => {
|
||||
return isCoarsePointer.value ? t('pwa.installMobile') : t('pwa.installDesktop');
|
||||
});
|
||||
|
||||
const updateStandalone = () => {
|
||||
isStandalone.value = window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
|
||||
if (isStandalone.value) {
|
||||
canInstall.value = false;
|
||||
installDismissed.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const handleBeforeInstallPrompt = (e) => {
|
||||
e.preventDefault();
|
||||
deferredPrompt.value = e;
|
||||
if (!isStandalone.value) {
|
||||
canInstall.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const handleAppInstalled = () => {
|
||||
deferredPrompt.value = null;
|
||||
canInstall.value = false;
|
||||
installDismissed.value = true;
|
||||
};
|
||||
|
||||
const handleInstall = async () => {
|
||||
if (!deferredPrompt.value) return;
|
||||
deferredPrompt.value.prompt();
|
||||
const choice = await deferredPrompt.value.userChoice;
|
||||
deferredPrompt.value = null;
|
||||
canInstall.value = false;
|
||||
if (!choice || choice.outcome !== 'accepted') {
|
||||
installDismissed.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (!store.loadState()) {
|
||||
store.initGame(); // Inicjalizacja domyślnej gry jeśli brak zapisu
|
||||
}
|
||||
if (typeof window !== 'undefined') {
|
||||
isCoarsePointer.value = window.matchMedia('(pointer: coarse)').matches;
|
||||
updateStandalone();
|
||||
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
|
||||
window.addEventListener('appinstalled', handleAppInstalled);
|
||||
displayModeMedia = window.matchMedia('(display-mode: standalone)');
|
||||
if (displayModeMedia?.addEventListener) {
|
||||
displayModeMedia.addEventListener('change', updateStandalone);
|
||||
} else if (displayModeMedia?.addListener) {
|
||||
displayModeMedia.addListener(updateStandalone);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
|
||||
window.removeEventListener('appinstalled', handleAppInstalled);
|
||||
if (displayModeMedia?.removeEventListener) {
|
||||
displayModeMedia.removeEventListener('change', updateStandalone);
|
||||
} else if (displayModeMedia?.removeListener) {
|
||||
displayModeMedia.removeListener(updateStandalone);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -37,6 +103,16 @@ onMounted(() => {
|
||||
<div class="underline"></div>
|
||||
</header>
|
||||
|
||||
<div v-if="canInstall && !installDismissed" class="install-banner">
|
||||
<div class="install-text">{{ t('pwa.installTitle') }}</div>
|
||||
<div class="install-actions">
|
||||
<button class="btn-neon secondary install-btn" @click="handleInstall">
|
||||
{{ installLabel }}
|
||||
</button>
|
||||
<button class="install-close" @click="installDismissed = true">×</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="game-layout">
|
||||
<!-- Level Selection -->
|
||||
<LevelSelector
|
||||
@@ -143,6 +219,57 @@ h1 {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.install-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 16px;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
border: 1px solid rgba(0, 242, 254, 0.35);
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25);
|
||||
width: min(680px, 92vw);
|
||||
margin: -10px 0 20px;
|
||||
}
|
||||
|
||||
.install-text {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 0.95rem;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.install-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.install-btn {
|
||||
padding: 8px 16px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.install-close {
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.1rem;
|
||||
line-height: 1;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.install-close:hover {
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
/* Remove old glass panel style from game-layout since we split it */
|
||||
.board-section {
|
||||
display: flex;
|
||||
|
||||
@@ -45,7 +45,10 @@ const messages = {
|
||||
'win.shareX': 'X',
|
||||
'win.shareFacebook': 'Facebook',
|
||||
'win.shareWhatsapp': 'WhatsApp',
|
||||
'win.shareDownload': 'Pobierz zrzut'
|
||||
'win.shareDownload': 'Pobierz zrzut',
|
||||
'pwa.installTitle': 'Zainstaluj aplikację i graj offline',
|
||||
'pwa.installMobile': 'Dodaj do ekranu głównego',
|
||||
'pwa.installDesktop': 'Zainstaluj na komputerze'
|
||||
},
|
||||
en: {
|
||||
'app.title': 'Nonograms',
|
||||
@@ -84,7 +87,10 @@ const messages = {
|
||||
'win.shareX': 'X',
|
||||
'win.shareFacebook': 'Facebook',
|
||||
'win.shareWhatsapp': 'WhatsApp',
|
||||
'win.shareDownload': 'Download screenshot'
|
||||
'win.shareDownload': 'Download screenshot',
|
||||
'pwa.installTitle': 'Install the app and play offline',
|
||||
'pwa.installMobile': 'Add to home screen',
|
||||
'pwa.installDesktop': 'Install on desktop'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user