Skip to content

Commit 942707f

Browse files
committed
feat: improve update logic and error messages
1 parent 924b111 commit 942707f

File tree

2 files changed

+175
-15
lines changed

2 files changed

+175
-15
lines changed

public/locales/en/translation.json

+19-2
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,25 @@
8888
"title": "Ubisoft Connect"
8989
},
9090
"update": {
91-
"body": "Something went wrong with the update after multiple attempts! Please manually uninstall and reinstall HyperPlay. error: {{error}}",
92-
"title": "Update Error"
91+
"addressUnreachable": "Address unreachable. Please check your internet connection.",
92+
"body": "Something went wrong with the update after multiple attempts! Please check the error message below or reinstall HyperPlay. error: {{error}}",
93+
"certAuthorityInvalid": "Certificate authority invalid. Please check your system time and date or open a ticket with HyperPlay support.",
94+
"certDateInvalid": "Certificate date invalid. Please check your system time and date or open a ticket with HyperPlay support.",
95+
"connectionAborted": "Connection aborted. Please check your internet connection.",
96+
"connectionClosed": "Connection closed. Please check your internet connection.",
97+
"connectionRefused": "Connection refused. Please check your internet connection.",
98+
"connectionReset": "Connection reset. Please check your internet connection.",
99+
"connectionTimedOut": "Connection timed out. Please check your internet connection.",
100+
"emptyResponse": "Empty response. Please check your internet connection.",
101+
"failed": "Download Failed. Please check your internet connection.",
102+
"http2ServerRefusedStream": "HTTP2 server refused stream. Please check your internet connection.",
103+
"internetDisconnected": "Internet disconnected. Please check your internet connection.",
104+
"message": "Error Updating",
105+
"nameNotResolved": "Name not resolved. Please check your internet connection.",
106+
"networkAccessDenied": "Network access denied. Please check your internet connection.",
107+
"networkChanged": "Network changed. Please check your internet connection.",
108+
"proxyConnectionFailed": "Proxy connection failed. Please check your proxy settings.",
109+
"sslProtocolError": "SSL protocol error. Please check your system time and date or open a ticket with HyperPlay support."
93110
},
94111
"winetricks": {
95112
"message": "Winetricks returned the following error during execution:{{newLine}}{{error}}",

src/backend/updater.ts

+156-13
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ import { dialog, shell } from 'electron'
22
import { autoUpdater } from 'electron-updater'
33
import { t } from 'i18next'
44

5-
import { configStore, icon, isLinux } from './constants'
5+
import { configStore, icon, isLinux, isMac } from './constants'
66
import { logError, logInfo, LogPrefix } from './logger/logger'
77
import { captureException } from '@sentry/electron'
88
import { getFileSize } from './utils'
99
import { ClientUpdateStatuses } from '@hyperplay/utils'
1010
import { trackEvent } from './metrics/metrics'
11+
import { homedir } from 'os'
12+
import { join } from 'path'
13+
import { rm } from 'fs/promises'
1114
// to test auto update on windows locally make sure you added the option "verifyUpdateCodeSignature": false
1215
// under build.win in package.json and also change the app version to an old one there
1316

@@ -21,18 +24,32 @@ autoUpdater.autoInstallOnAppQuit = true
2124
let isAppUpdating = false
2225
let hasUpdated = false
2326
let updateAttempts = 0
24-
const MAX_UPDATE_ATTEMPTS = 3
27+
const MAX_UPDATE_ATTEMPTS = 5
2528

2629
// check for updates every hour
27-
const checkUpdateInterval = 1 * 60 * 60 * 1000
28-
setInterval(() => {
29-
if (shouldCheckForUpdates) {
30-
autoUpdater.checkForUpdates()
30+
const checkUpdateInterval = 1 * 1000 * 60 * 60
31+
setInterval(async () => {
32+
if (shouldCheckForUpdates && !hasUpdated && !isAppUpdating) {
33+
logInfo('Checking for client updates...', LogPrefix.AutoUpdater)
34+
await autoUpdater.checkForUpdates()
3135
}
3236
}, checkUpdateInterval)
3337

3438
autoUpdater.on('update-available', async (info) => {
39+
if (isAppUpdating && hasUpdated) {
40+
logInfo(
41+
'New update available, but user has already updated the app',
42+
LogPrefix.AutoUpdater
43+
)
44+
return
45+
}
46+
3547
if (!shouldCheckForUpdates) {
48+
logInfo(
49+
'New update available, but user has disabled auto updates',
50+
LogPrefix.AutoUpdater
51+
)
52+
3653
return
3754
}
3855
newVersion = info.version
@@ -70,6 +87,7 @@ autoUpdater.on('download-progress', (progress) => {
7087

7188
autoUpdater.on('update-downloaded', async () => {
7289
logInfo('App update is downloaded')
90+
hasUpdated = true
7391

7492
trackEvent({
7593
event: 'Client Update Downloaded',
@@ -92,12 +110,22 @@ autoUpdater.on('update-downloaded', async () => {
92110
if (response === 1) {
93111
return autoUpdater.quitAndInstall()
94112
}
95-
hasUpdated = true
113+
logInfo('User chose not to update the app for now.', LogPrefix.AutoUpdater)
96114
})
97115

98116
autoUpdater.on('error', async (error) => {
99117
isAppUpdating = false
100-
logError(`Error updating HyperPlay: ${error.message}`, LogPrefix.AutoUpdater)
118+
119+
// To avoid false positives, we should not show the error dialog if the app has already updated successfully
120+
if (hasUpdated) {
121+
return
122+
}
123+
124+
const errorMessage = getErrorMessage(error.message)
125+
logError(`Error updating HyperPlay: ${errorMessage}`, LogPrefix.AutoUpdater)
126+
127+
// will remove cached updates when it fails to avoid corrupted updates
128+
await removeCachedUpdatesFolder()
101129

102130
updateAttempts++
103131

@@ -133,12 +161,11 @@ autoUpdater.on('error', async (error) => {
133161
updateAttempts = 0
134162

135163
const { response } = await dialog.showMessageBox({
136-
title: t('box.error.update.title', 'Error Updating'),
164+
title: t('box.error.update.message', 'Error Updating'),
137165
message: t(
138-
'box.error.update.message',
139-
`Something went wrong with the update after multiple attempts! Please manually uninstall and reinstall HyperPlay. error: ${JSON.stringify(
140-
error
141-
)}`
166+
'box.error.update.body',
167+
`Something went wrong with the update after multiple attempts! Please check the error message below or reinstall HyperPlay. error: {{error}}`,
168+
{ error: errorMessage }
142169
),
143170
type: 'error',
144171
buttons: [t('button.cancel', 'Cancel'), t('button.download', 'Download')]
@@ -155,3 +182,119 @@ export function isClientUpdating(): ClientUpdateStatuses {
155182
}
156183
return isAppUpdating ? 'updating' : 'idle'
157184
}
185+
186+
async function removeCachedUpdatesFolder() {
187+
// remove hyperplay-updates folder from cache directory
188+
// on macOS: /Users/<username>/Library/Caches/hyperplay-updates
189+
// on Windows: C:\Users\<username>\AppData\Local\hyperplay-updates
190+
const macOSPath = join(homedir(), 'Library', 'Caches', 'hyperplay-updates')
191+
const windowsPath = join(homedir(), 'AppData', 'Local', 'hyperplay-updates')
192+
193+
try {
194+
await rm(isMac ? macOSPath : windowsPath, { recursive: true })
195+
} catch (error) {
196+
logError(
197+
`Error removing cached updates folder: ${error}`,
198+
LogPrefix.AutoUpdater
199+
)
200+
}
201+
}
202+
203+
const commonDownloadErrors: Record<string, () => string> = {
204+
ERR_NETWORK_CHANGED: () =>
205+
t(
206+
'box.error.update.networkChanged',
207+
'Network changed. Please check your internet connection.'
208+
),
209+
ERR_INTERNET_DISCONNECTED: () =>
210+
t(
211+
'box.error.update.internetDisconnected',
212+
'Internet disconnected. Please check your internet connection.'
213+
),
214+
ERR_CONNECTION_RESET: () =>
215+
t(
216+
'box.error.update.connectionReset',
217+
'Connection reset. Please check your internet connection.'
218+
),
219+
ERR_CONNECTION_CLOSED: () =>
220+
t(
221+
'box.error.update.connectionClosed',
222+
'Connection closed. Please check your internet connection.'
223+
),
224+
ERR_CONNECTION_TIMED_OUT: () =>
225+
t(
226+
'box.error.update.connectionTimedOut',
227+
'Connection timed out. Please check your internet connection.'
228+
),
229+
ERR_NAME_NOT_RESOLVED: () =>
230+
t(
231+
'box.error.update.nameNotResolved',
232+
'Name not resolved. Please check your internet connection.'
233+
),
234+
ERR_CONNECTION_REFUSED: () =>
235+
t(
236+
'box.error.update.connectionRefused',
237+
'Connection refused. Please check your internet connection.'
238+
),
239+
ERR_SSL_PROTOCOL_ERROR: () =>
240+
t(
241+
'box.error.update.sslProtocolError',
242+
'SSL protocol error. Please check your system time and date or open a ticket with HyperPlay support.'
243+
),
244+
ERR_CERT_AUTHORITY_INVALID: () =>
245+
t(
246+
'box.error.update.certAuthorityInvalid',
247+
'Certificate authority invalid. Please check your system time and date or open a ticket with HyperPlay support.'
248+
),
249+
ERR_NETWORK_ACCESS_DENIED: () =>
250+
t(
251+
'box.error.update.networkAccessDenied',
252+
'Network access denied. Please check your internet connection.'
253+
),
254+
ERR_PROXY_CONNECTION_FAILED: () =>
255+
t(
256+
'box.error.update.proxyConnectionFailed',
257+
'Proxy connection failed. Please check your proxy settings.'
258+
),
259+
ERR_CONNECTION_ABORTED: () =>
260+
t(
261+
'box.error.update.connectionAborted',
262+
'Connection aborted. Please check your internet connection.'
263+
),
264+
ERR_ADDRESS_UNREACHABLE: () =>
265+
t(
266+
'box.error.update.addressUnreachable',
267+
'Address unreachable. Please check your internet connection.'
268+
),
269+
ERR_CERT_DATE_INVALID: () =>
270+
t(
271+
'box.error.update.certDateInvalid',
272+
'Certificate date invalid. Please check your system time and date or open a ticket with HyperPlay support.'
273+
),
274+
ERR_HTTP2_SERVER_REFUSED_STREAM: () =>
275+
t(
276+
'box.error.update.http2ServerRefusedStream',
277+
'HTTP2 server refused stream. Please check your internet connection.'
278+
),
279+
ERR_EMPTY_RESPONSE: () =>
280+
t(
281+
'box.error.update.emptyResponse',
282+
'Empty response. Please check your internet connection.'
283+
),
284+
ERR_FAILED: () =>
285+
t(
286+
'box.error.update.failed',
287+
'Download Failed. Please check your internet connection.'
288+
)
289+
}
290+
291+
function getErrorMessage(error: string): string {
292+
const trimmedError = error.replace('net::', '').trim()
293+
if (
294+
Object.prototype.hasOwnProperty.call(commonDownloadErrors, trimmedError)
295+
) {
296+
return commonDownloadErrors[trimmedError]()
297+
} else {
298+
return error
299+
}
300+
}

0 commit comments

Comments
 (0)