Skip to content

Commit 38ac7fd

Browse files
authored
Merge pull request #2631 from RedisInsight/build/feature/RI-4843_New_version_is_available
#RI-4843 - Inform users that a new version is available
2 parents d4d7519 + 738feca commit 38ac7fd

File tree

14 files changed

+119
-9
lines changed

14 files changed

+119
-9
lines changed

redisinsight/desktop/app.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ const init = async () => {
3737

3838
nativeTheme.themeSource = electronStore?.get(ElectronStorageItem.themeSource) || config.themeSource
3939

40-
checkForUpdate(process.env.MANUAL_UPGRADES_LINK || process.env.UPGRADES_LINK)
41-
4240
app.setName(config.name)
4341
app.setAppUserModelId(config.name)
4442
if (process.platform !== 'darwin') {
@@ -71,6 +69,8 @@ const init = async () => {
7169
}
7270

7371
await windowFactory(WindowType.Main, splashWindow, { parsedDeepLink })
72+
73+
checkForUpdate(process.env.MANUAL_UPGRADES_LINK || process.env.UPGRADES_LINK)
7474
} catch (_err) {
7575
const error = _err as Error
7676
console.log(wrapErrorMessageSensitiveData(error))

redisinsight/desktop/preload.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ contextBridge.exposeInMainWorld('app', {
2525
deepLinkAction: ((parsedDeepLink: any) => {
2626
ipcRenderer.on(IpcOnEvent.deepLinkAction, parsedDeepLink)
2727
}),
28+
updateAvailable: ((updateInfo: any) => {
29+
ipcRenderer.on(IpcOnEvent.appUpdateAvailable, updateInfo)
30+
}),
2831
ipc: ipcHandler,
2932
config: {
3033
apiPort: config.apiPort

redisinsight/desktop/src/lib/updater/updater.handlers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { app } from 'electron'
22
import { autoUpdater, UpdateDownloadedEvent } from 'electron-updater'
33
import log from 'electron-log'
44

5-
import { electronStore } from 'desktopSrc/lib'
5+
import { electronStore, updateDownloaded } from 'desktopSrc/lib'
66
import { wrapErrorMessageSensitiveData } from 'desktopSrc/utils'
77
import { ElectronStorageItem } from 'uiSrc/electron/constants'
88

@@ -40,5 +40,7 @@ export const initAutoUpdaterHandlers = () => {
4040
electronStore?.set(ElectronStorageItem.updateDownloadedForTelemetry, true)
4141
electronStore?.set(ElectronStorageItem.updateDownloadedVersion, info.version)
4242
electronStore?.set(ElectronStorageItem.updatePreviousVersion, app.getVersion())
43+
44+
updateDownloaded(info)
4345
})
4446
}

redisinsight/desktop/src/lib/updater/updater.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
import log from 'electron-log'
2-
import { autoUpdater } from 'electron-updater'
2+
import { UpdateDownloadedEvent, autoUpdater } from 'electron-updater'
33
import { wrapErrorMessageSensitiveData } from 'desktopSrc/utils'
4+
import { getWindows } from 'desktopSrc/lib/window'
5+
import { IpcOnEvent } from 'uiSrc/electron/constants'
46

5-
export const checkForUpdate = (url: string = '') => {
7+
export const updateDownloaded = (updateInfo: UpdateDownloadedEvent) => {
8+
setTimeout(() => {
9+
const [currentWindow] = getWindows().values()
10+
11+
currentWindow?.webContents.send(IpcOnEvent.appUpdateAvailable, updateInfo)
12+
}, 60 * 1_000) // 1 min
13+
}
14+
15+
export const checkForUpdate = async (url: string = '') => {
616
if (!url || process.mas) {
717
return
818
}
@@ -20,7 +30,12 @@ export const checkForUpdate = (url: string = '') => {
2030
log.error(wrapErrorMessageSensitiveData(error))
2131
}
2232

23-
autoUpdater.checkForUpdatesAndNotify()
2433
autoUpdater.autoDownload = true
2534
autoUpdater.autoInstallOnAppQuit = true
35+
36+
await autoUpdater.checkForUpdates()
37+
}
38+
39+
export const quitAndInstallUpdate = () => {
40+
autoUpdater.quitAndInstall(true, true)
2641
}

redisinsight/desktop/src/lib/window/window.handlers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
electronStore,
99
windowFactory,
1010
WindowType,
11+
quitAndInstallUpdate,
1112
} from 'desktopSrc/lib'
1213
import { IpcInvokeEvent, ElectronStorageItem, IpcOnEvent } from 'uiSrc/electron/constants'
1314

@@ -99,4 +100,7 @@ export const initWindowIPCHandlers = () => {
99100
ipcMain.handle(IpcInvokeEvent.windowOpen, async (_event, { location }) => {
100101
await windowFactory(WindowType.Main, null, { parsedDeepLink: { initialPage: location } })
101102
})
103+
ipcMain.handle(IpcInvokeEvent.appRestart, async () => {
104+
quitAndInstallUpdate()
105+
})
102106
}

redisinsight/ui/src/components/notifications/Notifications.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ const Notifications = () => {
130130
event: TelemetryEvent.CLOUD_CREATE_DATABASE_IN_SUBSCRIPTION_FORM_CLOSED,
131131
})
132132
break
133+
case InfiniteMessagesIds.appUpdateAvailable:
134+
sendEventTelemetry({
135+
event: TelemetryEvent.UPDATE_NOTIFICATION_CLOSED,
136+
})
137+
break
133138

134139
default:
135140
break

redisinsight/ui/src/components/notifications/components/infinite-messages/InfiniteMessages.spec.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,23 @@ describe('INFINITE_MESSAGES', () => {
9797
expect(render(<>{Inner}</>)).toBeTruthy()
9898
})
9999
})
100+
101+
describe('APP_UPDATE_AVAILABLE', () => {
102+
it('should render message', () => {
103+
const { Inner } = INFINITE_MESSAGES.APP_UPDATE_AVAILABLE('1', jest.fn())
104+
expect(render(<>{Inner}</>)).toBeTruthy()
105+
})
106+
107+
it('should call onSuccess', () => {
108+
const onSuccess = jest.fn()
109+
const { Inner } = INFINITE_MESSAGES.APP_UPDATE_AVAILABLE('1', onSuccess)
110+
render(<>{Inner}</>)
111+
112+
fireEvent.click(screen.getByTestId('app-restart-btn'))
113+
fireEvent.mouseUp(screen.getByTestId('app-update-available-notification'))
114+
fireEvent.mouseDown(screen.getByTestId('app-update-available-notification'))
115+
116+
expect(onSuccess).toBeCalled()
117+
})
118+
})
100119
})

redisinsight/ui/src/components/notifications/components/infinite-messages/InfiniteMessages.tsx

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export enum InfiniteMessagesIds {
88
autoCreateDb = 'autoCreateDb',
99
databaseExists = 'databaseExists',
1010
subscriptionExists = 'subscriptionExists',
11+
appUpdateAvailable = 'appUpdateAvailable',
1112
}
1213

1314
export const INFINITE_MESSAGES = {
@@ -180,5 +181,41 @@ export const INFINITE_MESSAGES = {
180181
</EuiFlexGroup>
181182
</div>
182183
)
183-
})
184+
}),
185+
APP_UPDATE_AVAILABLE: (version: string, onSuccess?: () => void) => ({
186+
id: InfiniteMessagesIds.appUpdateAvailable,
187+
Inner: (
188+
<div
189+
role="presentation"
190+
onMouseDown={(e) => { e.preventDefault() }}
191+
onMouseUp={(e) => { e.preventDefault() }}
192+
data-testid="app-update-available-notification"
193+
>
194+
<EuiTitle className="infiniteMessage__title">
195+
<span>
196+
New version is now available
197+
</span>
198+
</EuiTitle>
199+
<EuiText size="s">
200+
<>
201+
With RedisInsight
202+
{` ${version} `}
203+
you have access to new useful features and optimizations.
204+
<br />
205+
Restart RedisInsight to install updates.
206+
</>
207+
</EuiText>
208+
<br />
209+
<EuiButton
210+
fill
211+
size="s"
212+
color="secondary"
213+
onClick={() => onSuccess?.()}
214+
data-testid="app-restart-btn"
215+
>
216+
Restart
217+
</EuiButton>
218+
</div>
219+
)
220+
}),
184221
}

redisinsight/ui/src/electron/components/ConfigElectron/ConfigElectron.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { useEffect } from 'react'
22
import { useDispatch, useSelector } from 'react-redux'
33
import { useHistory } from 'react-router-dom'
4+
import { UpdateInfo } from 'electron-updater'
45
import { IParsedDeepLink } from 'desktopSrc/lib/app/deep-link.handlers'
56
import {
67
appServerInfoSelector,
7-
appElectronInfoSelector
8+
appElectronInfoSelector,
89
} from 'uiSrc/slices/app/info'
9-
import { ipcCheckUpdates, ipcSendEvents } from 'uiSrc/electron/utils'
10+
import { ipcAppRestart, ipcCheckUpdates, ipcSendEvents } from 'uiSrc/electron/utils'
1011
import { ipcDeleteDownloadedVersion } from 'uiSrc/electron/utils/ipcDeleteStoreValues'
12+
import { addInfiniteNotification } from 'uiSrc/slices/app/notifications'
13+
import { INFINITE_MESSAGES } from 'uiSrc/components/notifications/components'
14+
import { TelemetryEvent, sendEventTelemetry } from 'uiSrc/telemetry'
1115

1216
const ConfigElectron = () => {
1317
let isCheckedUpdates = false
@@ -19,6 +23,7 @@ const ConfigElectron = () => {
1923

2024
useEffect(() => {
2125
window.app?.deepLinkAction?.(deepLinkAction)
26+
window.app?.updateAvailable?.(updateAvailableAction)
2227
}, [])
2328

2429
useEffect(() => {
@@ -48,6 +53,14 @@ const ConfigElectron = () => {
4853
}
4954
}
5055

56+
const updateAvailableAction = (_e: any, { version }: UpdateInfo) => {
57+
sendEventTelemetry({ event: TelemetryEvent.UPDATE_NOTIFICATION_DISPLAYED })
58+
dispatch(addInfiniteNotification(INFINITE_MESSAGES.APP_UPDATE_AVAILABLE(version, () => {
59+
sendEventTelemetry({ event: TelemetryEvent.UPDATE_NOTIFICATION_RESTART_CLICKED })
60+
ipcAppRestart()
61+
})))
62+
}
63+
5164
return null
5265
}
5366

redisinsight/ui/src/electron/constants/ipcEvent.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ enum IpcInvokeEvent {
55
cloudOauth = 'cloud:oauth',
66
windowOpen = 'window:open',
77
themeChange = 'theme:change',
8+
appRestart = 'app:restart',
89
}
910

1011
enum IpcOnEvent {
1112
sendWindowId = 'window:send:id',
1213
cloudOauthCallback = 'cloud:oauth:callback',
1314
deepLinkAction = 'deep-link:action',
15+
appUpdateAvailable = 'app:update:available',
1416
}
1517

1618
export {

0 commit comments

Comments
 (0)