Версия 0.5.2
CI / Линт и типы (push) Canceled after 0s
CI / Сборка (Windows) (push) Canceled after 0s

Иконки: пересобраны из мастера 1024×1024. В .icns было одно представление
512×512 — macOS домасштабировала остальные сама; теперь все десять, от 16 до
1024. В .ico добавлен размер 24 и своя картинка вместо заглушки Electron.

Релиз: differentialPackage и publishAutoUpdate выключены — .blockmap и
latest.yml нужны electron-updater, которого в приложении нет, и лежали в
ассетах мёртвым грузом. Вместо них CI кладёт рядом с установщиком .md5.

Версия в интерфейсе больше не константа: renderer спрашивает её у main через
app.getVersion(). Держать константу «в синхроне с package.json» вручную не
вышло ни разу — она осталась на 0.4.0, и релизы 0.5.0 и 0.5.1 показали
пользователю чужую версию.
This commit is contained in:
2026-09-04 08:31:52 +03:00
parent 88f8819570
commit 0842c4d4a5
14 changed files with 82 additions and 12 deletions
+5 -1
View File
@@ -1,4 +1,4 @@
import { BrowserWindow, ipcMain, nativeTheme, type IpcMainInvokeEvent } from 'electron'
import { app, BrowserWindow, ipcMain, nativeTheme, type IpcMainInvokeEvent } from 'electron'
import type { IpcChannel, IpcHandlers, ThemeState } from '../shared/ipc-contract'
import { uninstallApp } from './uninstall.win32'
import {
@@ -46,6 +46,10 @@ function themeState(): ThemeState {
export function registerIpcHandlers(): void {
handle('theme:get', () => themeState())
// Ровно то же значение, что electron-builder кладёт в имя установщика и в
// «Установленные приложения» — оба берут его из "version" в package.json.
handle('app:version', () => app.getVersion())
// Кнопка «Удалить приложение» есть в интерфейсе только на Windows (SettingsView.tsx),
// но канал регистрируем всегда — так и должно быть по контракту, просто на других
// платформах он никогда не вызывается.
+3
View File
@@ -43,6 +43,9 @@ const api = {
* службы только на Windows (см. SettingsView.tsx). */
platform: process.platform,
/** Версия запущенной сборки — из package.json через `app.getVersion()` в main. */
getAppVersion: (): Promise<string> => invoke('app:version'),
getTheme: (): Promise<ThemeState> => invoke('theme:get'),
onThemeChanged: (listener: (state: ThemeState) => void): (() => void) =>
subscribe('theme:changed', listener),
+9 -6
View File
@@ -1,6 +1,7 @@
import { useState } from 'react'
import type { ZapretListId, ZapretStatus } from '@shared/ipc-contract'
import { GITHUB_URL } from '@shared/links'
import { useAppVersion } from '../hooks/useAppVersion'
import { useZapretLists } from '../hooks/useZapretLists'
import { LIST_TITLES } from '../lib/zapretListMeta'
import ListEditor from './ListEditor'
@@ -12,9 +13,6 @@ import ListEditor from './ListEditor'
*/
const isWindows = window.api.platform === 'win32'
const APP_CHANNEL = 'beta'
// Держать в синхроне с "version" в package.json — оттуда её берёт electron-builder
// для имени установщика и для «Установленные приложения» в Windows.
const APP_VERSION = '0.4.0'
export default function SettingsView({
onBack,
@@ -27,6 +25,7 @@ export default function SettingsView({
}): React.JSX.Element {
const { lists, autoHostlist, save, reset, clearAuto } = useZapretLists()
const [openListId, setOpenListId] = useState<ZapretListId | null>(null)
const appVersion = useAppVersion()
const openList = lists.find((list) => list.id === openListId)
if (openList) {
@@ -119,9 +118,13 @@ export default function SettingsView({
<p className="settings__support-hint">Поддержите звёздочкой</p>
</section>
<p className="settings__version">
{APP_CHANNEL}-{APP_VERSION}-{window.api.platform}
</p>
{/* Пока версия не пришла из main, строки нет вовсе: иначе на первом кадре
мелькнуло бы «beta--win32» с пустотой на месте номера. */}
{appVersion && (
<p className="settings__version">
{APP_CHANNEL}-{appVersion}-{window.api.platform}
</p>
)}
{isWindows && (
<button
+1 -1
View File
@@ -52,7 +52,7 @@ export default function ZapretCard(): React.JSX.Element {
)}
<div className="settings__field">
<span className="settings__label">Стратегия</span>
<span className="settings__label">Способ</span>
{/* На время переключения список заблокирован: смена стратегии на работающем
обходе останавливает и перенастраивает службу, и второй выбор посреди этого
наложился бы на первый. */}
+29
View File
@@ -0,0 +1,29 @@
import { useEffect, useState } from 'react'
/**
* Версия запущенной сборки — та же, что в имени установщика и в «Установленных
* приложениях»: main отдаёт её через `app.getVersion()`, то есть прямо из package.json.
*
* Раньше renderer держал версию своей константой с пометкой «держать в синхроне с
* package.json». Синхронизировать вручную забыли: константа осталась на 0.4.0, и релизы
* 0.5.0 и 0.5.1 показывали пользователю чужую версию. Спрашивать у main дороже на один
* IPC-вызов, зато разъехаться уже нечему.
*
* `null` — версия ещё не пришла; показывать её в этот момент нечего.
*/
export function useAppVersion(): string | null {
const [version, setVersion] = useState<string | null>(null)
useEffect(() => {
// Окно могло закрыться, пока шёл ответ — тогда setState уже некому применять.
let cancelled = false
void window.api.getAppVersion().then((value) => {
if (!cancelled) setVersion(value)
})
return () => {
cancelled = true
}
}, [])
return version
}
+8
View File
@@ -101,6 +101,14 @@ export interface ZapretDiagnosticResult extends ZapretDiagnosticTarget {
export interface IpcHandlers {
'theme:get': () => ThemeState
/**
* Версия приложения — `app.getVersion()`, то есть `"version"` из package.json той сборки,
* которая сейчас запущена. Раньше renderer держал её отдельной константой «в синхроне
* с package.json» вручную: константа осталась на 0.4.0 и релизы 0.5.0 и 0.5.1 показали
* пользователю чужую версию. Спрашивать у main — единственный способ не разъехаться.
*/
'app:version': () => string
/** Windows: подтверждает диалогом ОС, запускает деинсталлятор и закрывает приложение. */
'app:uninstall': () => void