OTA.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. <template>
  2. <transition
  3. name="fade"
  4. mode="out-in"
  5. >
  6. <div
  7. v-if="showForm && update"
  8. class="fixed z-[1000] top-0 left-0 w-screen h-screen bg-black bg-opacity-70 flex justify-center items-end"
  9. >
  10. <div
  11. ref="target"
  12. :class="showForm ? 'show-up' : 'show-down'"
  13. class="w-full bg-background-200 flex flex-col gap-1 rounded-t-xl text-foreground p-5 justify-center items-center"
  14. >
  15. <div @click="showForm = false" class="absolute top-3 right-1">
  16. <IClose
  17. class="h-[48px] w-[48px] fill-red-400 stroke-red-400"
  18. />
  19. </div>
  20. <h1 class="text-2xl font-semibold">
  21. Обновление
  22. </h1>
  23. <div class="flex text-md opacity-50 gap-2">
  24. <p>{{ update[2] || 'Текущая версия' }}</p>
  25. <img :src="`/icons/right-arrow.svg`">
  26. <p>{{ update[1] || 'Новая версия' }}</p>
  27. </div>
  28. <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">
  29. <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>
  30. <p class="text-sm w-fit ml-3">Данная версия нестабильна, возможны ошибки. Используйте на свой страх и риск.</p>
  31. </div>
  32. <div class="w-full border-b-[1px] border-foreground border-opacity-30 my-1" />
  33. <div class="w-full max-h-[30vh] overflow-y-scroll transition-all">
  34. <template v-if="description && description.features && description.features.length > 0">
  35. <p class="font-bold">
  36. Нововведения
  37. </p>
  38. <ul class="list-disc list-inside text-sm">
  39. <li
  40. v-for="(feat, idx) in description.features"
  41. :key="idx"
  42. >
  43. {{ feat }}
  44. </li>
  45. </ul>
  46. </template>
  47. <template v-if="description && description.bugfixes && description.bugfixes.length > 0">
  48. <p class="font-bold">
  49. Исправления
  50. </p>
  51. <ul class="list-disc list-inside text-sm">
  52. <li
  53. v-for="(fix, idx) in description.bugfixes"
  54. :key="idx"
  55. >
  56. {{ fix }}
  57. </li>
  58. </ul>
  59. </template>
  60. </div>
  61. <div class="w-full border-b-[1px] border-foreground border-opacity-30 my-1" />
  62. <div class="w-full flex justify-center items-center mt-2">
  63. <NuxtLink v-if="update[3]">
  64. <button
  65. 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"
  66. @click="installUpdate"
  67. >
  68. <svg
  69. xmlns="http://www.w3.org/2000/svg"
  70. width="24"
  71. height="24"
  72. viewBox="0 0 24 24"
  73. ><g
  74. fill="none"
  75. stroke="currentColor"
  76. stroke-linecap="round"
  77. stroke-linejoin="round"
  78. stroke-width="2"
  79. ><path
  80. stroke-dasharray="2 4"
  81. stroke-dashoffset="6"
  82. d="M12 3c4.97 0 9 4.03 9 9c0 4.97 -4.03 9 -9 9"
  83. ><animate
  84. attributeName="stroke-dashoffset"
  85. dur="0.6s"
  86. repeatCount="indefinite"
  87. values="6;0"
  88. /></path><path
  89. stroke-dasharray="32"
  90. stroke-dashoffset="32"
  91. d="M12 21c-4.97 0 -9 -4.03 -9 -9c0 -4.97 4.03 -9 9 -9"
  92. ><animate
  93. fill="freeze"
  94. attributeName="stroke-dashoffset"
  95. begin="0.1s"
  96. dur="0.4s"
  97. values="32;0"
  98. /></path><path
  99. stroke-dasharray="10"
  100. stroke-dashoffset="10"
  101. d="M12 8v7.5"
  102. ><animate
  103. fill="freeze"
  104. attributeName="stroke-dashoffset"
  105. begin="0.5s"
  106. dur="0.2s"
  107. values="10;0"
  108. /></path><path
  109. stroke-dasharray="6"
  110. stroke-dashoffset="6"
  111. d="M12 15.5l3.5 -3.5M12 15.5l-3.5 -3.5"
  112. ><animate
  113. fill="freeze"
  114. attributeName="stroke-dashoffset"
  115. begin="0.7s"
  116. dur="0.2s"
  117. values="6;0"
  118. /></path></g></svg>
  119. Скачать обновление
  120. </button>
  121. </NuxtLink>
  122. <button
  123. v-else
  124. 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"
  125. >
  126. Ссылка временно недоступна
  127. </button>
  128. </div>
  129. </div>
  130. </div>
  131. </transition>
  132. </template>
  133. <script setup lang="ts">
  134. import { ref } from 'vue'
  135. import { onClickOutside } from '@vueuse/core'
  136. import { Browser } from '@capacitor/browser'
  137. import { useOTAStore } from '../../store/useOTAStore'
  138. import { useLogger } from '../../composables/useLogger'
  139. import { storeToRefs } from 'pinia'
  140. const log = useLogger('OTAComponent')
  141. const otaStore = useOTAStore()
  142. const target = ref(null)
  143. const { showForm, update, description } = storeToRefs(otaStore)
  144. async function installUpdate() {
  145. if (!update.value?.[3]) {
  146. log.error('Не удалось получить ссылку на обновление')
  147. return
  148. }
  149. try {
  150. const downloadUrl = String(update.value[3])
  151. const releaseUrl = downloadUrl.replace('/releases/download/', '/releases/tag/').replace(/\/[^/]+\.apk$/, '')
  152. await Browser.open({ url: releaseUrl })
  153. } catch (error) {
  154. log.error('Не удалось открыть ссылку на обновление:', error)
  155. }
  156. }
  157. onClickOutside(target, () => {
  158. otaStore.closeForm()
  159. })
  160. </script>
  161. <style scoped>
  162. @keyframes showUp {
  163. 0% { transform: translateY(30vh); }
  164. 100% { transform: translateY(0px) }
  165. }
  166. @keyframes showDown {
  167. 0% { transform: translateY(0px); }
  168. 100% { transform: translateY(30vh) }
  169. }
  170. .show-up {
  171. animation: showUp 300ms ease-in-out;
  172. animation-fill-mode: forwards;
  173. }
  174. .show-down {
  175. animation: showDown 300ms ease-in-out;
  176. animation-fill-mode: forwards;
  177. }
  178. .fade-enter-active,
  179. .fade-leave-active {
  180. transition: opacity 300ms ease-in-out;
  181. }
  182. .fade-enter {
  183. opacity: 0;
  184. }
  185. .fade-leave-to {
  186. opacity: 0;
  187. }
  188. </style>