123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- <template>
- <transition
- name="fade"
- mode="out-in"
- >
- <div
- v-if="showForm && update"
- class="fixed z-[1000] top-0 left-0 w-screen h-screen bg-black bg-opacity-70 flex justify-center items-end"
- >
- <div
- ref="target"
- :class="showForm ? 'show-up' : 'show-down'"
- class="w-full bg-background-200 flex flex-col gap-1 rounded-t-xl text-foreground p-5 justify-center items-center"
- >
- <div @click="showForm = false" class="absolute top-3 right-1">
- <IClose
- class="h-[48px] w-[48px] fill-red-400 stroke-red-400"
- />
- </div>
- <h1 class="text-2xl font-semibold">
- Обновление
- </h1>
- <div class="flex text-md opacity-50 gap-2">
- <p>{{ update[2] || 'Текущая версия' }}</p>
- <img :src="`/icons/right-arrow.svg`">
- <p>{{ update[1] || 'Новая версия' }}</p>
- </div>
- <div v-if="update[1].includes('dev')" class="my-2 flex justify-center items-center border-[1px] border-yellow-700 bg-yellow-700 bg-opacity-50 py-2 px-4 rounded-lg transition-all">
- <svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2"><rect width="14" height="14" x="5" y="5" rx="4"/><path stroke-linecap="round" d="M12 9v3m0 3.02v-.01"/></g></svg>
- <p class="text-sm w-fit ml-3">Данная версия нестабильна, возможны ошибки. Используйте на свой страх и риск.</p>
- </div>
- <div class="w-full border-b-[1px] border-foreground border-opacity-30 my-1" />
- <div class="w-full max-h-[30vh] overflow-y-scroll transition-all">
- <template v-if="description && description.features && description.features.length > 0">
- <p class="font-bold">
- Нововведения
- </p>
- <ul class="list-disc list-inside text-sm">
- <li
- v-for="(feat, idx) in description.features"
- :key="idx"
- >
- {{ feat }}
- </li>
- </ul>
- </template>
- <template v-if="description && description.bugfixes && description.bugfixes.length > 0">
- <p class="font-bold">
- Исправления
- </p>
- <ul class="list-disc list-inside text-sm">
- <li
- v-for="(fix, idx) in description.bugfixes"
- :key="idx"
- >
- {{ fix }}
- </li>
- </ul>
- </template>
- </div>
- <div class="w-full border-b-[1px] border-foreground border-opacity-30 my-1" />
- <div class="w-full flex justify-center items-center mt-2">
- <NuxtLink v-if="update[3]">
- <button
- class="border-[1px] text-xl px-12 py-1 border-green-400 hover:bg-green-400 hover:text-black rounded-xl flex gap-3 justify-center items-center duration-150"
- @click="installUpdate"
- >
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="24"
- height="24"
- viewBox="0 0 24 24"
- ><g
- fill="none"
- stroke="currentColor"
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="2"
- ><path
- stroke-dasharray="2 4"
- stroke-dashoffset="6"
- d="M12 3c4.97 0 9 4.03 9 9c0 4.97 -4.03 9 -9 9"
- ><animate
- attributeName="stroke-dashoffset"
- dur="0.6s"
- repeatCount="indefinite"
- values="6;0"
- /></path><path
- stroke-dasharray="32"
- stroke-dashoffset="32"
- d="M12 21c-4.97 0 -9 -4.03 -9 -9c0 -4.97 4.03 -9 9 -9"
- ><animate
- fill="freeze"
- attributeName="stroke-dashoffset"
- begin="0.1s"
- dur="0.4s"
- values="32;0"
- /></path><path
- stroke-dasharray="10"
- stroke-dashoffset="10"
- d="M12 8v7.5"
- ><animate
- fill="freeze"
- attributeName="stroke-dashoffset"
- begin="0.5s"
- dur="0.2s"
- values="10;0"
- /></path><path
- stroke-dasharray="6"
- stroke-dashoffset="6"
- d="M12 15.5l3.5 -3.5M12 15.5l-3.5 -3.5"
- ><animate
- fill="freeze"
- attributeName="stroke-dashoffset"
- begin="0.7s"
- dur="0.2s"
- values="6;0"
- /></path></g></svg>
- Скачать обновление
- </button>
- </NuxtLink>
- <button
- v-else
- class="border-[1px] text-xl px-12 py-1 border-neutral-600 text-neutral-600 rounded-xl flex gap-3 justify-center items-center duration-150"
- >
- Ссылка временно недоступна
- </button>
- </div>
- </div>
- </div>
- </transition>
- </template>
- <script setup lang="ts">
- import { ref } from 'vue'
- import { onClickOutside } from '@vueuse/core'
- import { Browser } from '@capacitor/browser'
- import { useOTAStore } from '../../store/useOTAStore'
- import { useLogger } from '../../composables/useLogger'
- import { storeToRefs } from 'pinia'
- const log = useLogger('OTAComponent')
- const otaStore = useOTAStore()
- const target = ref(null)
- const { showForm, update, description } = storeToRefs(otaStore)
- async function installUpdate() {
- if (!update.value?.[3]) {
- log.error('Не удалось получить ссылку на обновление')
- return
- }
-
- try {
- const downloadUrl = String(update.value[3])
- const releaseUrl = downloadUrl.replace('/releases/download/', '/releases/tag/').replace(/\/[^/]+\.apk$/, '')
- await Browser.open({ url: releaseUrl })
- } catch (error) {
- log.error('Не удалось открыть ссылку на обновление:', error)
- }
- }
- onClickOutside(target, () => {
- otaStore.closeForm()
- })
- </script>
- <style scoped>
- @keyframes showUp {
- 0% { transform: translateY(30vh); }
- 100% { transform: translateY(0px) }
- }
- @keyframes showDown {
- 0% { transform: translateY(0px); }
- 100% { transform: translateY(30vh) }
- }
- .show-up {
- animation: showUp 300ms ease-in-out;
- animation-fill-mode: forwards;
- }
- .show-down {
- animation: showDown 300ms ease-in-out;
- animation-fill-mode: forwards;
- }
- .fade-enter-active,
- .fade-leave-active {
- transition: opacity 300ms ease-in-out;
- }
- .fade-enter {
- opacity: 0;
- }
- .fade-leave-to {
- opacity: 0;
- }
- </style>
|