Skip to content

Commit 2d26bd6

Browse files
feat: check for updates (#567)
1 parent 19760ae commit 2d26bd6

File tree

6 files changed

+114
-4
lines changed

6 files changed

+114
-4
lines changed

src/main/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { migrateJsonToSqlite } from './db/migrate'
77
import { registerIPC } from './ipc'
88
import { mainMenu } from './menu/main'
99
import { store } from './store'
10+
import { checkForUpdates } from './updates'
1011

1112
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true' // Отключаем security warnings
1213

@@ -73,8 +74,8 @@ else {
7374
app.whenReady().then(() => {
7475
createWindow()
7576
registerIPC()
76-
7777
initApi()
78+
checkForUpdates()
7879

7980
if (store.app.get('isAutoMigratedFromJson')) {
8081
return

src/main/menu/main.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import {
66
BrowserWindow,
77
dialog,
88
type MenuItemConstructorOptions,
9+
shell,
910
} from 'electron'
11+
import { repository } from '../../../package.json'
1012
import i18n from '../i18n'
1113
import { send } from '../ipc'
14+
import { fethUpdates } from '../updates'
1215
import { createMenu, createPlatformMenuItems } from './utils'
1316

1417
const year = new Date().getFullYear()
@@ -40,6 +43,40 @@ const appMenuItems: MenuConfig[] = [
4043
platforms: ['darwin'],
4144
click: () => aboutApp(),
4245
},
46+
{
47+
id: 'update',
48+
label: i18n.t('menu:app.update'),
49+
click: async () => {
50+
const latestVersion = await fethUpdates()
51+
52+
if (latestVersion) {
53+
const buttonId = dialog.showMessageBoxSync(
54+
BrowserWindow.getFocusedWindow()!,
55+
{
56+
message: i18n.t('messages:update.available', {
57+
newVersion: latestVersion,
58+
oldVersion: version,
59+
}),
60+
buttons: [i18n.t('button.update.0'), i18n.t('button.update.1')],
61+
defaultId: 0,
62+
cancelId: 1,
63+
},
64+
)
65+
66+
if (buttonId === 0) {
67+
shell.openExternal(`${repository}/releases`)
68+
}
69+
}
70+
else {
71+
dialog.showMessageBoxSync(BrowserWindow.getFocusedWindow()!, {
72+
message: i18n.t('messages:update.noAvailable'),
73+
})
74+
}
75+
},
76+
},
77+
{
78+
type: 'separator',
79+
},
4380
{
4481
id: 'preferences',
4582
label: i18n.t('menu:app.preferences'),
@@ -229,7 +266,6 @@ const menuItems: MenuItemConstructorOptions[] = [
229266
submenu: createPlatformMenuItems(appMenuItems),
230267
},
231268
{
232-
// role: 'fileMenu',
233269
label: 'File',
234270
submenu: createPlatformMenuItems(fileMenuItems),
235271
},

src/main/types/ipc.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ type MainMenuAction =
2222
| 'presentation-mode'
2323

2424
type DBAction = 'relaod' | 'move' | 'migrate' | 'clear'
25-
type SystemAction = 'reload' | 'open-external' | 'deep-link'
25+
type SystemAction =
26+
| 'reload'
27+
| 'open-external'
28+
| 'deep-link'
29+
| 'update-available'
2630
type PrettierAction = 'format'
2731
type FsAction = 'assets'
2832

src/main/updates/index.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* eslint-disable node/prefer-global/process */
2+
import { repository, version } from '../../../package.json'
3+
import { send } from '../ipc'
4+
5+
interface GitHubRelease {
6+
tag_name: string
7+
}
8+
9+
const INTERVAL = 1000 * 60 * 60 * 3 // 3 часа
10+
const isDev = process.env.NODE_ENV === 'development'
11+
12+
export async function fethUpdates() {
13+
if (isDev) {
14+
return
15+
}
16+
17+
try {
18+
const url = `${repository.replace('github.com', 'api.github.com/repos')}/releases`
19+
20+
const response = await fetch(url)
21+
const data = (await response.json()) as GitHubRelease[]
22+
23+
if (!data) {
24+
return
25+
}
26+
27+
const releases = data.filter((release) => {
28+
const tagName = release.tag_name.replace('v', '')
29+
return tagName.startsWith('4.')
30+
})
31+
32+
if (releases.length > 0) {
33+
const latestRelease = releases[0]
34+
const latestVersion = latestRelease.tag_name.replace('v', '')
35+
36+
if (latestVersion !== version) {
37+
send('system:update-available')
38+
return latestVersion as string
39+
}
40+
}
41+
}
42+
catch (err) {
43+
console.error('Error checking for updates:', err)
44+
}
45+
}
46+
47+
export function checkForUpdates() {
48+
fethUpdates()
49+
50+
setInterval(() => {
51+
fethUpdates()
52+
}, INTERVAL)
53+
}

src/renderer/ipc/listeners/system.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { useApp, useFolders, useSnippets } from '@/composables'
1+
import { useApp, useFolders, useSnippets, useSonner } from '@/composables'
22
import { ipc } from '@/electron'
3+
import { repository } from '../../../../package.json'
34

45
const {
56
highlightedFolderId,
@@ -9,6 +10,7 @@ const {
910
} = useApp()
1011
const { selectFolder } = useFolders()
1112
const { selectSnippet, getSnippets } = useSnippets()
13+
const { sonner } = useSonner()
1214

1315
export function registerSystemListeners() {
1416
ipc.on('system:deep-link', async (_, url: string) => {
@@ -33,4 +35,17 @@ export function registerSystemListeners() {
3335
console.error(error)
3436
}
3537
})
38+
39+
ipc.on('system:update-available', () => {
40+
sonner({
41+
message: 'Update available',
42+
type: 'success',
43+
action: {
44+
label: 'Go to GitHub',
45+
onClick: () => {
46+
ipc.invoke('system:open-external', `${repository}/releases`)
47+
},
48+
},
49+
})
50+
})
3651
}

tsconfig.main.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"target": "ES2023",
44
"rootDir": "src/main",
55
"module": "CommonJS",
6+
"resolveJsonModule": true,
67
"strict": true,
78
"inlineSources": true,
89
"outDir": "build/main",

0 commit comments

Comments
 (0)