cordova-plugin-hot-updates 2.1.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +361 -192
- package/package.json +5 -6
- package/plugin.xml +9 -10
- package/src/ios/HotUpdates+Helpers.h +23 -0
- package/src/ios/HotUpdates+Helpers.m +24 -0
- package/src/ios/HotUpdates.h +4 -3
- package/src/ios/HotUpdates.m +45 -211
- package/src/ios/HotUpdatesConstants.h +45 -0
- package/src/ios/HotUpdatesConstants.m +46 -0
- package/www/HotUpdates.js +162 -142
- package/CHANGELOG.md +0 -239
- package/docs/API.md +0 -352
- package/docs/README.md +0 -187
- package/docs/hot-updates-admin.html +0 -467
package/docs/README.md
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
# Hot Updates Plugin v2.1.0 - JavaScript Integration
|
|
2
|
-
|
|
3
|
-
**4 метода API | JavaScript контролирует ВСЁ | Manual updates only**
|
|
4
|
-
|
|
5
|
-
**Последнее обновление:** 2025-11-04 (Bugfix Update)
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 🐛 Важные исправления (2025-11-04)
|
|
10
|
-
|
|
11
|
-
✅ **Автоустановка** - теперь работает при каждом запуске
|
|
12
|
-
✅ **Callback формат** - возвращает `null` при успехе (не пустую строку)
|
|
13
|
-
✅ **WebView cache** - очищается перед reload (версия всегда актуальна)
|
|
14
|
-
✅ **Динамическая версия** - `window.APP_VERSION` загружается из native автоматически
|
|
15
|
-
✅ **Проверка установленной версии** - `getUpdate()` не скачивает уже установленную версию
|
|
16
|
-
✅ **Чистый код** - убраны отладочные логи и смайлики из native
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## 📋 API методы
|
|
21
|
-
|
|
22
|
-
```javascript
|
|
23
|
-
// 1. Скачать обновление
|
|
24
|
-
window.HotUpdates.getUpdate({url: string, version?: string}, callback)
|
|
25
|
-
|
|
26
|
-
// 2. Установить обновление
|
|
27
|
-
window.HotUpdates.forceUpdate(callback)
|
|
28
|
-
|
|
29
|
-
// 3. Подтвердить успешную загрузку
|
|
30
|
-
window.HotUpdates.canary(version: string, callback)
|
|
31
|
-
|
|
32
|
-
// 4. Получить черный список
|
|
33
|
-
window.HotUpdates.getIgnoreList(callback)
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
**Callback формат:**
|
|
37
|
-
- Успех: `callback(null)` или `callback()`
|
|
38
|
-
- Ошибка: `callback({error: {message: "..."}})`
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## 🔑 Получение версии приложения
|
|
43
|
-
|
|
44
|
-
### ⚡ Рекомендуемый подход (НОВОЕ в v2.1.0)
|
|
45
|
-
|
|
46
|
-
Версия загружается **автоматически из native** через `getVersionInfo()`:
|
|
47
|
-
|
|
48
|
-
```javascript
|
|
49
|
-
document.addEventListener('deviceready', function() {
|
|
50
|
-
// Получаем версию из native (installedVersion или appBundleVersion)
|
|
51
|
-
cordova.exec(
|
|
52
|
-
function(info) {
|
|
53
|
-
window.APP_VERSION = info.installedVersion || info.appBundleVersion;
|
|
54
|
-
console.log('Current version:', window.APP_VERSION);
|
|
55
|
-
|
|
56
|
-
// Теперь можно вызвать canary
|
|
57
|
-
window.HotUpdates.canary(window.APP_VERSION, function() {
|
|
58
|
-
console.log('Canary confirmed');
|
|
59
|
-
});
|
|
60
|
-
},
|
|
61
|
-
function(error) {
|
|
62
|
-
console.error('Failed to get version:', error);
|
|
63
|
-
},
|
|
64
|
-
'HotUpdates',
|
|
65
|
-
'getVersionInfo',
|
|
66
|
-
[]
|
|
67
|
-
);
|
|
68
|
-
});
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
**Преимущества:**
|
|
72
|
-
- ✅ Версия всегда актуальна после hot update
|
|
73
|
-
- ✅ Не нужно синхронизировать версию между кодом и config.xml
|
|
74
|
-
- ✅ Автоматически обновляется после каждого обновления
|
|
75
|
-
|
|
76
|
-
### 📌 Альтернатива: Статическая версия
|
|
77
|
-
|
|
78
|
-
Если нужна статическая версия (не рекомендуется):
|
|
79
|
-
|
|
80
|
-
```html
|
|
81
|
-
<script src="cordova.js"></script>
|
|
82
|
-
<script>
|
|
83
|
-
window.APP_VERSION = "2.7.7"; // Нужно обновлять вручную
|
|
84
|
-
</script>
|
|
85
|
-
<script src="js/app.js"></script>
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## ⚡ Минимальная интеграция
|
|
91
|
-
|
|
92
|
-
```javascript
|
|
93
|
-
document.addEventListener('deviceready', function() {
|
|
94
|
-
// 1. Canary обязателен при каждом старте
|
|
95
|
-
window.HotUpdates.canary(window.APP_VERSION, function() {
|
|
96
|
-
console.log('Canary confirmed');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// 2. Проверить ignoreList
|
|
100
|
-
window.HotUpdates.getIgnoreList(function(result) {
|
|
101
|
-
const ignoredVersions = result.versions; // ["2.7.5", "2.7.6"]
|
|
102
|
-
|
|
103
|
-
// 3. Проверить обновления на сервере
|
|
104
|
-
fetch('https://your-api.com/updates/check?version=' + window.APP_VERSION)
|
|
105
|
-
.then(r => r.json())
|
|
106
|
-
.then(data => {
|
|
107
|
-
if (data.hasUpdate && !ignoredVersions.includes(data.newVersion)) {
|
|
108
|
-
// 4. Скачать обновление
|
|
109
|
-
window.HotUpdates.getUpdate({
|
|
110
|
-
url: data.downloadURL,
|
|
111
|
-
version: data.newVersion
|
|
112
|
-
}, function(error) {
|
|
113
|
-
if (error) {
|
|
114
|
-
console.error('Download failed:', error.error.message);
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 5. Показать popup
|
|
119
|
-
if (confirm('Обновить до ' + data.newVersion + '?')) {
|
|
120
|
-
// 6. Установить
|
|
121
|
-
window.HotUpdates.forceUpdate(function(error) {
|
|
122
|
-
if (error) {
|
|
123
|
-
console.error('Install failed:', error.error.message);
|
|
124
|
-
} else {
|
|
125
|
-
console.log('Update installed, reloading...');
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
// Если пользователь отказался - обновление установится при следующем запуске
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
.catch(err => console.error('Check failed:', err));
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## 🎯 Ключевые правила
|
|
141
|
-
|
|
142
|
-
### 1. JavaScript контролирует ВСЁ
|
|
143
|
-
- Native НЕ проверяет ignoreList
|
|
144
|
-
- Вы читаете список через `getIgnoreList()` и принимаете решения
|
|
145
|
-
- Native только выполняет команды
|
|
146
|
-
|
|
147
|
-
### 2. Canary обязателен
|
|
148
|
-
```javascript
|
|
149
|
-
// При КАЖДОМ старте приложения, иначе rollback через 20 секунд
|
|
150
|
-
window.HotUpdates.canary(window.APP_VERSION, function() {});
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### 3. Автоустановка при следующем запуске
|
|
154
|
-
Если пользователь проигнорировал popup (закрыл приложение), обновление автоматически установится при следующем запуске.
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## 📚 Полная документация
|
|
159
|
-
|
|
160
|
-
- **API.md** - полное описание всех методов, параметров и ошибок
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## 🔥 Тестирование
|
|
165
|
-
|
|
166
|
-
Откройте `hot-updates-admin.html` в приложении для тестирования всех методов.
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
## ⚠️ Важно
|
|
171
|
-
|
|
172
|
-
1. **window.APP_VERSION**
|
|
173
|
-
- ✅ Рекомендуется загружать через `getVersionInfo()` (автоматически актуальна)
|
|
174
|
-
- ⚠️ Статическая установка требует ручного обновления при каждом релизе
|
|
175
|
-
- Должна быть установлена ДО первого вызова `canary()`
|
|
176
|
-
|
|
177
|
-
2. **canary()** должен вызываться при КАЖДОМ старте приложения (иначе rollback через 20 сек)
|
|
178
|
-
|
|
179
|
-
3. **getIgnoreList()** нужно проверять ПЕРЕД скачиванием обновления
|
|
180
|
-
|
|
181
|
-
4. **Автоустановка**
|
|
182
|
-
- ✅ Работает автоматически при следующем запуске (если пользователь проигнорировал popup)
|
|
183
|
-
- ✅ Срабатывает всегда (исправлено в v2.1.0 Bugfix Update)
|
|
184
|
-
|
|
185
|
-
5. **Callback формат**
|
|
186
|
-
- Успех: `callback()` или `callback(null)` - возвращает `null` или `undefined`
|
|
187
|
-
- Ошибка: `callback({error: {message: "текст ошибки"}})`
|
|
@@ -1,467 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="ru">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
-
<title>Hot Updates Admin v2.1.0</title>
|
|
7
|
-
<style>
|
|
8
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
-
body {
|
|
10
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
|
|
11
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
12
|
-
padding: 20px;
|
|
13
|
-
min-height: 100vh;
|
|
14
|
-
}
|
|
15
|
-
.container {
|
|
16
|
-
max-width: 600px;
|
|
17
|
-
margin: 0 auto;
|
|
18
|
-
background: white;
|
|
19
|
-
border-radius: 15px;
|
|
20
|
-
padding: 20px;
|
|
21
|
-
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
|
22
|
-
}
|
|
23
|
-
h1 {
|
|
24
|
-
color: #667eea;
|
|
25
|
-
margin-bottom: 10px;
|
|
26
|
-
font-size: 24px;
|
|
27
|
-
text-align: center;
|
|
28
|
-
}
|
|
29
|
-
.back-link {
|
|
30
|
-
display: block;
|
|
31
|
-
text-align: center;
|
|
32
|
-
margin-bottom: 20px;
|
|
33
|
-
color: #667eea;
|
|
34
|
-
text-decoration: none;
|
|
35
|
-
font-weight: 600;
|
|
36
|
-
}
|
|
37
|
-
.back-link:hover {
|
|
38
|
-
text-decoration: underline;
|
|
39
|
-
}
|
|
40
|
-
.status {
|
|
41
|
-
background: #f0f4ff;
|
|
42
|
-
padding: 15px;
|
|
43
|
-
border-radius: 10px;
|
|
44
|
-
margin-bottom: 20px;
|
|
45
|
-
font-size: 13px;
|
|
46
|
-
line-height: 1.6;
|
|
47
|
-
}
|
|
48
|
-
.status-title {
|
|
49
|
-
font-weight: bold;
|
|
50
|
-
color: #667eea;
|
|
51
|
-
margin-bottom: 10px;
|
|
52
|
-
}
|
|
53
|
-
.section {
|
|
54
|
-
margin-bottom: 20px;
|
|
55
|
-
}
|
|
56
|
-
.section-title {
|
|
57
|
-
font-weight: bold;
|
|
58
|
-
margin-bottom: 10px;
|
|
59
|
-
color: #333;
|
|
60
|
-
font-size: 16px;
|
|
61
|
-
padding-bottom: 5px;
|
|
62
|
-
border-bottom: 2px solid #667eea;
|
|
63
|
-
}
|
|
64
|
-
button {
|
|
65
|
-
width: 100%;
|
|
66
|
-
padding: 12px;
|
|
67
|
-
margin: 5px 0;
|
|
68
|
-
border: none;
|
|
69
|
-
border-radius: 8px;
|
|
70
|
-
background: #667eea;
|
|
71
|
-
color: white;
|
|
72
|
-
font-size: 14px;
|
|
73
|
-
font-weight: 600;
|
|
74
|
-
cursor: pointer;
|
|
75
|
-
transition: all 0.3s;
|
|
76
|
-
}
|
|
77
|
-
button:active {
|
|
78
|
-
transform: scale(0.98);
|
|
79
|
-
background: #5568d3;
|
|
80
|
-
}
|
|
81
|
-
button.danger { background: #f56565; }
|
|
82
|
-
button.success { background: #48bb78; }
|
|
83
|
-
button.warning { background: #ed8936; }
|
|
84
|
-
.log {
|
|
85
|
-
background: #1a1a1a;
|
|
86
|
-
color: #00ff00;
|
|
87
|
-
padding: 15px;
|
|
88
|
-
border-radius: 8px;
|
|
89
|
-
font-family: 'Courier New', monospace;
|
|
90
|
-
font-size: 11px;
|
|
91
|
-
max-height: 300px;
|
|
92
|
-
overflow-y: auto;
|
|
93
|
-
margin-top: 15px;
|
|
94
|
-
line-height: 1.4;
|
|
95
|
-
}
|
|
96
|
-
.log-entry {
|
|
97
|
-
margin-bottom: 5px;
|
|
98
|
-
padding: 2px 0;
|
|
99
|
-
}
|
|
100
|
-
.log-time {
|
|
101
|
-
color: #888;
|
|
102
|
-
margin-right: 5px;
|
|
103
|
-
}
|
|
104
|
-
.log-success { color: #48bb78; }
|
|
105
|
-
.log-error { color: #f56565; }
|
|
106
|
-
.log-info { color: #4299e1; }
|
|
107
|
-
.progress {
|
|
108
|
-
background: #e2e8f0;
|
|
109
|
-
height: 6px;
|
|
110
|
-
border-radius: 3px;
|
|
111
|
-
overflow: hidden;
|
|
112
|
-
margin: 10px 0;
|
|
113
|
-
display: none;
|
|
114
|
-
}
|
|
115
|
-
.progress-bar {
|
|
116
|
-
height: 100%;
|
|
117
|
-
background: linear-gradient(90deg, #667eea, #764ba2);
|
|
118
|
-
width: 0%;
|
|
119
|
-
transition: width 0.3s;
|
|
120
|
-
}
|
|
121
|
-
.version-badge {
|
|
122
|
-
display: inline-block;
|
|
123
|
-
background: #667eea;
|
|
124
|
-
color: white;
|
|
125
|
-
padding: 3px 10px;
|
|
126
|
-
border-radius: 12px;
|
|
127
|
-
font-size: 11px;
|
|
128
|
-
font-weight: bold;
|
|
129
|
-
margin-right: 5px;
|
|
130
|
-
}
|
|
131
|
-
.input-group {
|
|
132
|
-
margin: 10px 0;
|
|
133
|
-
}
|
|
134
|
-
input {
|
|
135
|
-
width: 100%;
|
|
136
|
-
padding: 10px;
|
|
137
|
-
border: 1px solid #ddd;
|
|
138
|
-
border-radius: 6px;
|
|
139
|
-
font-size: 14px;
|
|
140
|
-
margin-top: 5px;
|
|
141
|
-
}
|
|
142
|
-
label {
|
|
143
|
-
font-size: 13px;
|
|
144
|
-
color: #555;
|
|
145
|
-
font-weight: 600;
|
|
146
|
-
}
|
|
147
|
-
</style>
|
|
148
|
-
</head>
|
|
149
|
-
<body>
|
|
150
|
-
<div class="container">
|
|
151
|
-
<h1>🔥 Hot Updates Admin v2.1.0</h1>
|
|
152
|
-
|
|
153
|
-
<a href="index.html" class="back-link">← Вернуться к приложению</a>
|
|
154
|
-
|
|
155
|
-
<div id="status" class="status">
|
|
156
|
-
<div class="status-title">📱 Статус</div>
|
|
157
|
-
<div id="statusContent">Загрузка...</div>
|
|
158
|
-
</div>
|
|
159
|
-
|
|
160
|
-
<div class="progress" id="progress">
|
|
161
|
-
<div class="progress-bar" id="progressBar"></div>
|
|
162
|
-
</div>
|
|
163
|
-
|
|
164
|
-
<div class="section">
|
|
165
|
-
<div class="section-title">📋 Информация (DEBUG)</div>
|
|
166
|
-
<button onclick="getVersionInfo()">🔍 Получить версии</button>
|
|
167
|
-
<div id="versionInfo" style="margin-top: 10px; font-size: 12px; color: #666; line-height: 1.6; background: #f7fafc; padding: 10px; border-radius: 6px;"></div>
|
|
168
|
-
</div>
|
|
169
|
-
|
|
170
|
-
<div class="section">
|
|
171
|
-
<div class="section-title">⚡ Update (NEW API v2.1.0)</div>
|
|
172
|
-
<div class="input-group">
|
|
173
|
-
<label>URL:</label>
|
|
174
|
-
<input type="text" id="updateUrl" value="http://localhost:3000/api/updates/download/2.7.8/ios">
|
|
175
|
-
</div>
|
|
176
|
-
<button class="warning" onclick="performGetUpdate()">📥 Download</button>
|
|
177
|
-
<button class="success" onclick="performInstallUpdate()">🚀 Install</button>
|
|
178
|
-
<button class="danger" onclick="performFullUpdate()">⚡ FULL UPDATE</button>
|
|
179
|
-
</div>
|
|
180
|
-
|
|
181
|
-
<div class="section">
|
|
182
|
-
<div class="section-title">🐦 Canary (ТЗ п.4)</div>
|
|
183
|
-
<p style="font-size: 11px; color: #666; margin-bottom: 8px;">
|
|
184
|
-
Вызывается автоматически при старте JS. Timeout: 20 сек → auto rollback.
|
|
185
|
-
</p>
|
|
186
|
-
<button class="success" onclick="confirmCanary()">✅ Confirm Canary</button>
|
|
187
|
-
</div>
|
|
188
|
-
|
|
189
|
-
<div class="section">
|
|
190
|
-
<div class="section-title">🚫 Ignore List (Read-Only)</div>
|
|
191
|
-
<p style="font-size: 11px; color: #666; margin-bottom: 8px;">
|
|
192
|
-
Управляется native (добавляет при auto-rollback). JS только читает.
|
|
193
|
-
</p>
|
|
194
|
-
<button class="warning" onclick="getIgnoreList()">📋 Show Ignore List</button>
|
|
195
|
-
</div>
|
|
196
|
-
|
|
197
|
-
<div class="section">
|
|
198
|
-
<div class="section-title">📝 Логи</div>
|
|
199
|
-
<div id="log" class="log"></div>
|
|
200
|
-
<button class="danger" onclick="clearLog()" style="margin-top: 10px;">Очистить</button>
|
|
201
|
-
</div>
|
|
202
|
-
</div>
|
|
203
|
-
|
|
204
|
-
<script src="cordova.js"></script>
|
|
205
|
-
<script>
|
|
206
|
-
// APP VERSION - берется динамически из native (installedVersion или appBundleVersion)
|
|
207
|
-
window.APP_VERSION = null; // Заполняется асинхронно в deviceready
|
|
208
|
-
</script>
|
|
209
|
-
<script>
|
|
210
|
-
const logContainer = document.getElementById('log');
|
|
211
|
-
const statusContent = document.getElementById('statusContent');
|
|
212
|
-
const progressBar = document.getElementById('progressBar');
|
|
213
|
-
const progress = document.getElementById('progress');
|
|
214
|
-
|
|
215
|
-
function log(message, type = 'info') {
|
|
216
|
-
const time = new Date().toLocaleTimeString('ru-RU');
|
|
217
|
-
const entry = document.createElement('div');
|
|
218
|
-
entry.className = `log-entry log-${type}`;
|
|
219
|
-
entry.innerHTML = `<span class="log-time">[${time}]</span>${message}`;
|
|
220
|
-
logContainer.insertBefore(entry, logContainer.firstChild);
|
|
221
|
-
console.log(message);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function clearLog() {
|
|
225
|
-
logContainer.innerHTML = '';
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function updateStatus(info) {
|
|
229
|
-
statusContent.innerHTML = `
|
|
230
|
-
<div><span class="version-badge">Bundle</span>${info.appBundleVersion || 'N/A'}</div>
|
|
231
|
-
<div><span class="version-badge">Installed</span>${info.installedVersion || 'N/A'}</div>
|
|
232
|
-
<div><span class="version-badge">Previous</span>${info.previousVersion || 'N/A'}</div>
|
|
233
|
-
<div style="margin-top: 10px;">
|
|
234
|
-
Auto: <strong>${info.autoUpdateEnabled ? '✅' : '❌'}</strong> |
|
|
235
|
-
Rollback: <strong>${info.canRollback ? '✅' : '❌'}</strong>
|
|
236
|
-
</div>
|
|
237
|
-
`;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function showProgress(percent) {
|
|
241
|
-
progress.style.display = 'block';
|
|
242
|
-
progressBar.style.width = percent + '%';
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function hideProgress() {
|
|
246
|
-
progress.style.display = 'none';
|
|
247
|
-
progressBar.style.width = '0%';
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
document.addEventListener('deviceready', function() {
|
|
251
|
-
log('✅ Admin panel ready', 'success');
|
|
252
|
-
log('ℹ️ v2.1.0 - 4 API methods only', 'info');
|
|
253
|
-
|
|
254
|
-
if (navigator.splashscreen) {
|
|
255
|
-
navigator.splashscreen.hide();
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
statusContent.innerHTML = `
|
|
259
|
-
<div>🔥 Hot Updates v2.1.0 Final</div>
|
|
260
|
-
<div style="margin-top: 5px; font-size: 11px;">
|
|
261
|
-
Loading info...
|
|
262
|
-
</div>
|
|
263
|
-
`;
|
|
264
|
-
|
|
265
|
-
// ВАЖНО: Загружаем версию динамически из native
|
|
266
|
-
cordova.exec(
|
|
267
|
-
function(info) {
|
|
268
|
-
// Устанавливаем текущую версию (installedVersion или appBundleVersion)
|
|
269
|
-
window.APP_VERSION = info.installedVersion || info.appBundleVersion;
|
|
270
|
-
log(`📱 Current version: ${window.APP_VERSION}`, 'info');
|
|
271
|
-
|
|
272
|
-
// Автоматически загружаем полную информацию
|
|
273
|
-
setTimeout(() => getVersionInfo(), 100);
|
|
274
|
-
},
|
|
275
|
-
function(error) {
|
|
276
|
-
log(`❌ Failed to load version: ${JSON.stringify(error)}`, 'error');
|
|
277
|
-
window.APP_VERSION = '2.7.7'; // Fallback
|
|
278
|
-
setTimeout(() => getVersionInfo(), 500);
|
|
279
|
-
},
|
|
280
|
-
'HotUpdates',
|
|
281
|
-
'getVersionInfo',
|
|
282
|
-
[]
|
|
283
|
-
);
|
|
284
|
-
}, false);
|
|
285
|
-
|
|
286
|
-
// DEBUG: getVersionInfo для отладки
|
|
287
|
-
function getVersionInfo() {
|
|
288
|
-
log('🔍 Getting version info...', 'info');
|
|
289
|
-
cordova.exec(
|
|
290
|
-
function(info) {
|
|
291
|
-
log('✅ Version info received', 'success');
|
|
292
|
-
|
|
293
|
-
const versionInfoDiv = document.getElementById('versionInfo');
|
|
294
|
-
versionInfoDiv.innerHTML = `
|
|
295
|
-
<strong>Bundle:</strong> ${info.appBundleVersion}<br>
|
|
296
|
-
<strong>Installed:</strong> ${info.installedVersion || 'none'}<br>
|
|
297
|
-
<strong>Previous:</strong> ${info.previousVersion || 'none'}<br>
|
|
298
|
-
<strong>Canary:</strong> ${info.canaryVersion || 'none'}<br>
|
|
299
|
-
<strong>Pending:</strong> ${info.pendingVersion || 'none'} ${info.hasPendingUpdate ? '✅' : ''}<br>
|
|
300
|
-
<strong>Ignore List:</strong> ${info.ignoreList.length ? info.ignoreList.join(', ') : 'empty'}
|
|
301
|
-
`;
|
|
302
|
-
|
|
303
|
-
statusContent.innerHTML = `
|
|
304
|
-
<div>Current: ${info.installedVersion || info.appBundleVersion}</div>
|
|
305
|
-
<div style="margin-top: 5px; font-size: 11px;">
|
|
306
|
-
Previous: ${info.previousVersion || 'none'}
|
|
307
|
-
</div>
|
|
308
|
-
`;
|
|
309
|
-
},
|
|
310
|
-
function(error) {
|
|
311
|
-
log(`❌ Error: ${JSON.stringify(error)}`, 'error');
|
|
312
|
-
},
|
|
313
|
-
'HotUpdates',
|
|
314
|
-
'getVersionInfo',
|
|
315
|
-
[]
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// NEW API v2.1.0: getUpdate - только загрузка
|
|
320
|
-
function performGetUpdate() {
|
|
321
|
-
const url = document.getElementById('updateUrl').value;
|
|
322
|
-
if (!url) {
|
|
323
|
-
log('❌ URL required!', 'error');
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Извлекаем версию из URL (например: /2.7.8/ → 2.7.8)
|
|
328
|
-
const versionMatch = url.match(/\/(\d+\.\d+\.\d+)\//);
|
|
329
|
-
const version = versionMatch ? versionMatch[1] : 'pending';
|
|
330
|
-
|
|
331
|
-
log(`📥 Downloading version ${version}...`, 'info');
|
|
332
|
-
showProgress(0);
|
|
333
|
-
|
|
334
|
-
cordova.exec(
|
|
335
|
-
function(result) {
|
|
336
|
-
log(`✅ Downloaded!`, 'success');
|
|
337
|
-
log(`ℹ️ If you ignore popup, update installs on next launch`, 'info');
|
|
338
|
-
showProgress(100);
|
|
339
|
-
setTimeout(() => hideProgress(), 1000);
|
|
340
|
-
},
|
|
341
|
-
function(error) {
|
|
342
|
-
log(`❌ ${error.error?.message || JSON.stringify(error)}`, 'error');
|
|
343
|
-
hideProgress();
|
|
344
|
-
},
|
|
345
|
-
'HotUpdates',
|
|
346
|
-
'getUpdate',
|
|
347
|
-
[{url: url, version: version}]
|
|
348
|
-
);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// NEW API v2.1.0: forceUpdate - только установка (БЕЗ параметров!)
|
|
352
|
-
function performInstallUpdate() {
|
|
353
|
-
log(`🚀 Installing...`, 'info');
|
|
354
|
-
showProgress(50);
|
|
355
|
-
|
|
356
|
-
cordova.exec(
|
|
357
|
-
function(result) {
|
|
358
|
-
log(`✅ Installed!`, 'success');
|
|
359
|
-
showProgress(100);
|
|
360
|
-
setTimeout(() => {
|
|
361
|
-
hideProgress();
|
|
362
|
-
log('ℹ️ WebView reloaded with new version', 'info');
|
|
363
|
-
}, 1000);
|
|
364
|
-
},
|
|
365
|
-
function(error) {
|
|
366
|
-
log(`❌ ${error.error?.message || JSON.stringify(error)}`, 'error');
|
|
367
|
-
hideProgress();
|
|
368
|
-
},
|
|
369
|
-
'HotUpdates',
|
|
370
|
-
'forceUpdate',
|
|
371
|
-
[]
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Full update: getUpdate + forceUpdate
|
|
376
|
-
function performFullUpdate() {
|
|
377
|
-
const url = document.getElementById('updateUrl').value;
|
|
378
|
-
if (!url) {
|
|
379
|
-
log('❌ URL required!', 'error');
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Извлекаем версию из URL
|
|
384
|
-
const versionMatch = url.match(/\/(\d+\.\d+\.\d+)\//);
|
|
385
|
-
const version = versionMatch ? versionMatch[1] : 'pending';
|
|
386
|
-
|
|
387
|
-
log(`⚡ Full update to ${version}...`, 'info');
|
|
388
|
-
showProgress(0);
|
|
389
|
-
|
|
390
|
-
cordova.exec(
|
|
391
|
-
function(result) {
|
|
392
|
-
log(`✅ Downloaded`, 'success');
|
|
393
|
-
showProgress(50);
|
|
394
|
-
|
|
395
|
-
cordova.exec(
|
|
396
|
-
function(installResult) {
|
|
397
|
-
log(`✅ Installed!`, 'success');
|
|
398
|
-
showProgress(100);
|
|
399
|
-
setTimeout(() => {
|
|
400
|
-
hideProgress();
|
|
401
|
-
log('ℹ️ WebView reloaded with new version', 'info');
|
|
402
|
-
}, 1000);
|
|
403
|
-
},
|
|
404
|
-
function(installError) {
|
|
405
|
-
log(`❌ ${installError.error?.message || JSON.stringify(installError)}`, 'error');
|
|
406
|
-
hideProgress();
|
|
407
|
-
},
|
|
408
|
-
'HotUpdates',
|
|
409
|
-
'forceUpdate',
|
|
410
|
-
[]
|
|
411
|
-
);
|
|
412
|
-
},
|
|
413
|
-
function(downloadError) {
|
|
414
|
-
log(`❌ ${downloadError.error?.message || JSON.stringify(downloadError)}`, 'error');
|
|
415
|
-
hideProgress();
|
|
416
|
-
},
|
|
417
|
-
'HotUpdates',
|
|
418
|
-
'getUpdate',
|
|
419
|
-
[{url: url, version: version}]
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function confirmCanary() {
|
|
424
|
-
// Версия берется из глобальной переменной window.APP_VERSION
|
|
425
|
-
// В production она устанавливается в app.js при сборке
|
|
426
|
-
const version = window.APP_VERSION || '2.7.7';
|
|
427
|
-
|
|
428
|
-
log(`🐦 Confirming canary for version ${version}...`, 'info');
|
|
429
|
-
|
|
430
|
-
cordova.exec(
|
|
431
|
-
function(result) {
|
|
432
|
-
log(`✅ Canary confirmed for ${version}!`, 'success');
|
|
433
|
-
},
|
|
434
|
-
function(error) {
|
|
435
|
-
log(`❌ ${JSON.stringify(error)}`, 'error');
|
|
436
|
-
},
|
|
437
|
-
'HotUpdates',
|
|
438
|
-
'canary',
|
|
439
|
-
[version]
|
|
440
|
-
);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// ❌ performRollback() REMOVED - rollback is automatic only (canary timeout 20 sec)
|
|
444
|
-
|
|
445
|
-
function getIgnoreList() {
|
|
446
|
-
log('📋 Getting list...', 'info');
|
|
447
|
-
cordova.exec(
|
|
448
|
-
function(result) {
|
|
449
|
-
// NEW API v2.1.0: {versions: []}
|
|
450
|
-
const versions = result.versions || [];
|
|
451
|
-
if (versions.length > 0) {
|
|
452
|
-
log(`✅ (${versions.length}): ${versions.join(', ')}`, 'success');
|
|
453
|
-
} else {
|
|
454
|
-
log(`ℹ️ Empty`, 'info');
|
|
455
|
-
}
|
|
456
|
-
},
|
|
457
|
-
function(error) {
|
|
458
|
-
log(`❌ ${JSON.stringify(error)}`, 'error');
|
|
459
|
-
},
|
|
460
|
-
'HotUpdates',
|
|
461
|
-
'getIgnoreList',
|
|
462
|
-
[]
|
|
463
|
-
);
|
|
464
|
-
}
|
|
465
|
-
</script>
|
|
466
|
-
</body>
|
|
467
|
-
</html>
|