Pārlūkot izejas kodu

feat: улучшение системы OTA обновлений

- Создание store для управления OTA обновлениями

- Добавление предупреждения для dev-версий

- Добавление кнопки проверки обновлений на странице about
horanchikk 3 mēneši atpakaļ
vecāks
revīzija
44d9c0f8ca
5 mainītis faili ar 102 papildinājumiem un 31 dzēšanām
  1. 13 22
      components/Form/OTA.vue
  2. 7 5
      composables/useOTA.ts
  3. 32 4
      pages/about.vue
  4. 7 0
      plugins/ota-check.client.ts
  5. 43 0
      store/useOTAStore.ts

+ 13 - 22
components/Form/OTA.vue

@@ -25,6 +25,10 @@
           <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">
@@ -58,7 +62,7 @@
         <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-primary hover:bg-primary hover:text-black rounded-xl flex gap-3 justify-center items-center duration-150"
+              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
@@ -131,30 +135,15 @@
 import { ref } from 'vue'
 import { onClickOutside } from '@vueuse/core'
 import { Browser } from '@capacitor/browser'
-import { useOTA } from '../../composables/useOTA'
+import { useOTAStore } from '../../store/useOTAStore'
 import { useLogger } from '../../composables/useLogger'
+import { storeToRefs } from 'pinia'
 
-type UpdateInfo = [boolean, string, string, string] | undefined;
-
-interface Description {
-  features: string[];
-  bugfixes: string[];
-}
-
-const { needsUpdate, getDescription } = await useOTA()
 const log = useLogger('OTAComponent')
-const update = ref<UpdateInfo>(needsUpdate() as UpdateInfo)
-
-const description = ref<Description | undefined>()
+const otaStore = useOTAStore()
 const target = ref(null)
-const showForm = ref(false)
-
-if (update.value) {
-  showForm.value = true
-  description.value = getDescription() as Description
-}
 
-log.log(update.value);
+const { showForm, update, description } = storeToRefs(otaStore)
 
 async function installUpdate() {
   if (!update.value?.[3]) {
@@ -163,14 +152,16 @@ async function installUpdate() {
   }
   
   try {
-    await Browser.open({ url: String(update.value[3]) })
+    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, () => {
-  showForm.value = false
+  otaStore.closeForm()
 })
 </script>
 

+ 7 - 5
composables/useOTA.ts

@@ -1,20 +1,22 @@
 import { useSettings } from '~/store/useSettings';
 import type { Tota } from '~/types/ota';
+import { useApi } from './useApi';
 
 export async function useOTA() {
   const { 
     $config: { public: { APP_VERSION } },
-    $api
   } = useNuxtApp()
   const { $state: { settings } } = useSettings()
+  const { checkModule } = useApi()
   const log = useLogger('OTA')
 
   const latestUpdate = ref<Tota>({} as Tota)
 
   try {
+    const otaModule = checkModule('ota')
     log.info('Получение обновлений...')
     
-    latestUpdate.value = await $api.ota.getVersion(settings.dev)
+    latestUpdate.value = await otaModule.getVersion(settings.dev)
     
     log.info(`Последняя актуальная версия: ${latestUpdate.value.version}`)
   } catch (e) {
@@ -33,12 +35,12 @@ export async function useOTA() {
   }
 
   function parseUpdateLog(text: string) {
-    const result = { features: [], bugfixes: [] };
+    const result = { features: [] as string[], bugfixes: [] as string[] };
   
-    const getSection = (title) =>
+    const getSection = (title: string): string =>
       (text.match(new RegExp(`###\\s*${title}([\\s\\S]*?)(?=\\n###\\s*|$)`, 'i')) || [])[1] || '';
   
-    const normalize = (line) =>
+    const normalize = (line: string): string =>
       line
         .replace(/^\s*[*-]\s*/, '')                     
         .replace(/\*\*/g, '')                           

+ 32 - 4
pages/about.vue

@@ -24,22 +24,50 @@
             <p class="text-center text-2xl text-semibold">с 💖</p>
         </div>
         <div class="flex flex-col justify-center gap-1">
-            <p class="opacity-50" v-text="`Версия приложения - ${APP_VERSION}`" />
+            <p class="opacity-50 text-center" v-text="`Версия приложения - ${APP_VERSION}`" />
+            <button 
+                @click="checkUpdates" 
+                :class="[
+                    'h-[42px] w-[262px] flex items-center justify-center gap-2 px-4 py-2 rounded-lg border border-foreground hover:bg-foreground hover:text-black transition-colors',
+                    { 'loading': isLoading }
+                ]"
+                :disabled="isLoading"
+            >
+                <svg v-if="!isLoading" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                    <path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
+                    <path d="M3 3v5h5"/>
+                    <path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/>
+                    <path d="M16 21h5v-5"/>
+                </svg>
+                {{ isLoading ? '' : 'Проверить обновления' }}
+            </button>
         </div>
     </div>
 </template>
 
 <script setup lang="ts">
+import { useOTAStore } from '~/store/useOTAStore'
+import { ref } from 'vue'
+
 const { $config: { public: { APP_VERSION } } } = useNuxtApp()
+const otaStore = useOTAStore()
+const isLoading = ref(false)
+
+async function checkUpdates() {
+    isLoading.value = true
+    await otaStore.checkForUpdates()
+    isLoading.value = false
+}
 
 definePageMeta({
   name: 'О приложении',
   middleware: ['user-only'],
 })
-
-// test
 </script>
 
 <style scoped>
-
+.loading {
+    cursor: wait;
+    opacity: 0.7;
+}
 </style>

+ 7 - 0
plugins/ota-check.client.ts

@@ -0,0 +1,7 @@
+import { useOTAStore } from '~/store/useOTAStore'
+
+export default defineNuxtPlugin(async () => {
+  const otaStore = useOTAStore()
+  
+  await otaStore.checkForUpdates()
+}) 

+ 43 - 0
store/useOTAStore.ts

@@ -0,0 +1,43 @@
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+import { useOTA } from '../composables/useOTA'
+import { useLogger } from '../composables/useLogger'
+
+export const useOTAStore = defineStore('ota', () => {
+  const showForm = ref(false)
+  const update = ref<[boolean, string, string, string] | undefined>()
+  const description = ref<{ features: string[], bugfixes: string[] }>()
+  const log = useLogger('OTAStore')
+
+  async function checkForUpdates() {
+    try {
+      const { needsUpdate, getDescription } = await useOTA()
+      const updateInfo = needsUpdate()
+      log.info('Получена информация об обновлении:', updateInfo)
+      
+      if (updateInfo) {
+        update.value = updateInfo as [boolean, string, string, string]
+        description.value = getDescription()
+        showForm.value = true
+        log.info('Форма обновления открыта')
+      } else {
+        log.info('Обновлений не найдено')
+      }
+    } catch (error) {
+      log.error('Ошибка проверки обновлений:', error)
+    }
+  }
+
+  function closeForm() {
+    showForm.value = false
+    log.info('Форма обновления закрыта')
+  }
+
+  return {
+    showForm,
+    update,
+    description,
+    checkForUpdates,
+    closeForm
+  }
+})