Files
ghatreh_user/src/modules/version/VersionUpdateModal.vue
T
2026-06-29 15:27:35 +03:30

224 lines
5.3 KiB
Vue

<template>
<Teleport to="body">
<Transition name="version-update-fade">
<div v-if="isUpdating || hasError" class="version-update-overlay" dir="rtl">
<div class="version-update-card">
<div v-if="!hasError" class="version-update-spinner" />
<div v-else class="version-update-error-icon">!</div>
<h2 class="version-update-title">
{{ hasError ? 'خطا در به‌روزرسانی' : 'در حال به‌روزرسانی برنامه' }}
</h2>
<p class="version-update-text">
{{ statusText || 'لطفاً چند لحظه صبر کنید...' }}
</p>
<button v-if="hasError" type="button" class="version-update-button" @click="retry">
تلاش مجدد
</button>
</div>
</div>
</Transition>
</Teleport>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { setAppConfig } from '@/config/app-config'
import { fetchFreshAppConfig } from '@/config/load-config'
import { loadRemoteAppVersion, resetVersionCheckCache } from '@/config/load-version'
import {
shouldUpdateForVersion,
canStartVersionUpdate,
refreshConfigCacheForVersion,
cancelVersionUpdateLock,
hardReloadApp
} from '@/config/config-cache'
const isUpdating = ref(false)
const hasError = ref(false)
const statusText = ref('')
const pendingVersion = ref('')
onMounted(async () => {
await checkRemoteVersion()
})
async function checkRemoteVersion() {
try {
const version = await loadRemoteAppVersion()
/**
* خیلی مهم:
* اگر get_version جواب نداد، هیچ کاری نکن.
* نه cache پاک کن، نه reload بزن.
* چون ممکنه کاربر فقط اینترنت ضعیف داشته باشه یا endpoint موقتاً 404/timeout بده.
*/
if (!version) {
console.warn('Remote version is empty. Version check skipped.')
return
}
if (!shouldUpdateForVersion(version)) return
if (!canStartVersionUpdate(version)) return
pendingVersion.value = version
await startUpdate(version)
} catch (error) {
console.warn('Version check failed. Version update skipped:', error)
}
}
async function startUpdate(version: string) {
try {
isUpdating.value = true
hasError.value = false
statusText.value = 'در حال آماده‌سازی نسخه جدید...'
await wait(500)
statusText.value = 'دریافت تنظیمات جدید برنامه...'
const freshConfig = await refreshConfigCacheForVersion(version, fetchFreshAppConfig)
setAppConfig(freshConfig)
await wait(500)
statusText.value = 'بارگذاری مجدد برنامه...'
await wait(300)
hardReloadApp()
} catch (error) {
console.error('Version update failed:', error)
cancelVersionUpdateLock()
isUpdating.value = false
hasError.value = true
statusText.value = 'به‌روزرسانی انجام نشد. لطفاً دوباره تلاش کنید.'
}
}
function retry() {
hasError.value = false
if (pendingVersion.value) {
startUpdate(pendingVersion.value)
return
}
resetVersionCheckCache()
void checkRemoteVersion()
}
function wait(ms: number) {
return new Promise((resolve) => window.setTimeout(resolve, ms))
}
</script>
<style scoped>
.version-update-overlay {
position: fixed;
inset: 0;
z-index: 999999;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
background: radial-gradient(
circle at top,
rgba(var(--colorPrimaryRGB, 37 99 235) / 0.16),
transparent 38%
),
rgba(15, 23, 42, 0.38);
backdrop-filter: blur(8px);
}
.version-update-card {
width: min(390px, 100%);
padding: 30px 22px 26px;
border-radius: 24px;
text-align: center;
color: var(--colorText, #111827);
background: var(--bg, #ffffff);
border: 1px solid rgba(var(--colorPrimaryRGB, 37 99 235) / 0.18);
box-shadow: 0 22px 70px rgba(15, 23, 42, 0.22);
}
.version-update-spinner {
width: 58px;
height: 58px;
margin: 0 auto 20px;
border-radius: 999px;
border: 4px solid rgba(var(--colorPrimaryRGB, 37 99 235) / 0.14);
border-top-color: var(--colorPrimary, #2563eb);
animation: version-update-spin 0.9s linear infinite;
}
.version-update-error-icon {
width: 58px;
height: 58px;
margin: 0 auto 20px;
border-radius: 999px;
display: flex;
align-items: center;
justify-content: center;
color: var(--color6, #ffffff);
background: var(--color5, #f44336);
font-size: 34px;
font-weight: 900;
line-height: 1;
}
.version-update-title {
margin: 0;
color: var(--colorText, #111827);
font-size: 19px;
font-weight: 900;
}
.version-update-text {
margin: 12px 0 0;
color: var(--colorText, #111827);
opacity: 0.72;
font-size: 14px;
line-height: 2;
}
.version-update-button {
width: 100%;
margin-top: 22px;
padding: 12px 18px;
border: 0;
border-radius: 14px;
color: var(--color6, #ffffff);
background: var(--colorPrimary, #2563eb);
font-weight: 800;
cursor: pointer;
}
.version-update-button:active {
transform: translateY(1px);
}
.version-update-fade-enter-active,
.version-update-fade-leave-active {
transition: opacity 0.2s ease;
}
.version-update-fade-enter-from,
.version-update-fade-leave-to {
opacity: 0;
}
@keyframes version-update-spin {
to {
transform: rotate(360deg);
}
}
</style>