фиксы
This commit is contained in:
@@ -8,6 +8,10 @@ The Windows executable requests administrator privileges on startup so it can
|
||||
synchronize game clients installed under protected directories such as
|
||||
`Program Files`.
|
||||
|
||||
Launcher version labels are read from the packaged application metadata. Change
|
||||
only the `version: X.Y.Z+N` value in `pubspec.yaml`; login, launcher status,
|
||||
Windows metadata, installer packaging, and AppCast are synchronized from it.
|
||||
|
||||
The launcher is responsible for:
|
||||
|
||||
- logging in to a player's MoonWell account
|
||||
|
||||
+7
-15
@@ -1,22 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0"
|
||||
xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
|
||||
<channel>
|
||||
<title>MoonWell Launcher</title>
|
||||
<description>Обновления MoonWell Launcher</description>
|
||||
<description>MoonWell Launcher updates</description>
|
||||
<language>ru</language>
|
||||
<item>
|
||||
<title>MoonWell Launcher 1.0.1</title>
|
||||
<description>Обновление интерфейса, синхронизации клиента и интеграции с сервером MoonWell.</description>
|
||||
<pubDate>Tue, 28 Jul 2026 20:33:00 +0400</pubDate>
|
||||
<enclosure
|
||||
url="https://storage.yandexcloud.net/warcraft-client/moonwell_launcher_setup.exe"
|
||||
sparkle:version="1.0.1+2"
|
||||
sparkle:shortVersionString="1.0.1"
|
||||
sparkle:os="windows"
|
||||
sparkle:dsaSignature="MD4CHQCdoCW5jNymVlRblQpqy8C+jw4W0rVFQX0Bk25PAh0Av2ZcFYwZFQBQlAstQXna/PsuMNWIzXF4jpDuSA=="
|
||||
length="19989772"
|
||||
type="application/octet-stream" />
|
||||
<title>MoonWell Launcher 1.0.2</title>
|
||||
<description>Исправлено отображение версии и добавлен запуск с правами администратора.</description>
|
||||
<pubDate>Tue, 28 Jul 2026 21:23:41 +04:00</pubDate>
|
||||
<enclosure url="https://storage.yandexcloud.net/warcraft-client/moonwell_launcher_setup.exe" sparkle:version="1.0.2+4" sparkle:shortVersionString="1.0.2" sparkle:os="windows" sparkle:dsaSignature="MD0CHQDFH5GJKhykmXEZJTInFPrsoIkDc9tKroeR2XvJAhxrRjugtgetJlDiAhlyOtvgIYVRmAIb2sVqLelj" length="19994970" type="application/octet-stream" />
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
@@ -84,6 +84,11 @@ so the script writes that same complete value to `sparkle:version` and writes
|
||||
`X.Y.Z` to `sparkle:shortVersionString`. Every production release must have an
|
||||
`X.Y.Z` value greater than the release currently published in AppCast.
|
||||
|
||||
The UI reads `X.Y.Z` from the packaged application metadata through
|
||||
`package_info_plus`. Version labels on the login and launcher screens therefore
|
||||
update automatically after changing `pubspec.yaml`; do not add separate
|
||||
hardcoded version strings.
|
||||
|
||||
Run a dry run first:
|
||||
|
||||
```powershell
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/launcher_version.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_shells.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
|
||||
@@ -47,6 +48,7 @@ class _MwAuthenticationFormState extends State<MwAuthenticationForm> {
|
||||
Widget build(BuildContext context) {
|
||||
final accent = MoonWellDesignTokens.of(context).accent;
|
||||
final registering = widget.mode == MwAuthenticationMode.registration;
|
||||
final launcherVersion = LauncherVersionScope.of(context);
|
||||
return AutofillGroup(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -259,10 +261,10 @@ class _MwAuthenticationFormState extends State<MwAuthenticationForm> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
const Center(
|
||||
Center(
|
||||
child: Text(
|
||||
'MOONWELL · V 1.0.0 · PLAY.MOON-WELL.ONLINE',
|
||||
style: TextStyle(
|
||||
'MOONWELL · V $launcherVersion · PLAY.MOON-WELL.ONLINE',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: Color(0x52ECE4D0),
|
||||
fontSize: 8,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/launcher_version.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_primitives.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
|
||||
@@ -718,7 +719,9 @@ class MwLauncherStatusBar extends StatelessWidget {
|
||||
final String? eta;
|
||||
final String? buildHash;
|
||||
@override
|
||||
Widget build(BuildContext context) => Container(
|
||||
Widget build(BuildContext context) {
|
||||
final launcherVersion = LauncherVersionScope.of(context);
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xD904060E),
|
||||
@@ -744,12 +747,13 @@ class MwLauncherStatusBar extends StatelessWidget {
|
||||
Text(buildHash!),
|
||||
],
|
||||
const SizedBox(width: 24),
|
||||
const Text('Лаунчер v1.0.0'),
|
||||
Text('Лаунчер v$launcherVersion'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MwAccountAffordance extends StatelessWidget {
|
||||
const MwAccountAffordance({super.key, required this.onLogout, this.username});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/launcher_version.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_launcher_components.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_window_chrome.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
@@ -302,21 +303,22 @@ class _LoginStatusBar extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final launcherVersion = LauncherVersionScope.of(context);
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xD904060E),
|
||||
border: Border(top: BorderSide(color: Color(0x247FB8D4))),
|
||||
),
|
||||
child: const Row(
|
||||
child: Row(
|
||||
children: [
|
||||
Text('Версия лаунчера 1.0.0'),
|
||||
SizedBox(width: 24),
|
||||
Text('·'),
|
||||
SizedBox(width: 24),
|
||||
Text('Соединение защищено TLS'),
|
||||
Spacer(),
|
||||
Text('© 2026 MoonWell'),
|
||||
Text('Версия лаунчера $launcherVersion'),
|
||||
const SizedBox(width: 24),
|
||||
const Text('·'),
|
||||
const SizedBox(width: 24),
|
||||
const Text('Соединение защищено TLS'),
|
||||
const Spacer(),
|
||||
const Text('© 2026 MoonWell'),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class LauncherVersionScope extends InheritedWidget {
|
||||
const LauncherVersionScope({
|
||||
super.key,
|
||||
required this.version,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
final String version;
|
||||
|
||||
static String of(BuildContext context) {
|
||||
return context
|
||||
.dependOnInheritedWidgetOfExactType<LauncherVersionScope>()
|
||||
?.version ??
|
||||
'dev';
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(LauncherVersionScope oldWidget) {
|
||||
return version != oldWidget.version;
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_shells.dart';
|
||||
import 'package:moonwell_launcher/app/home_screen/home_screen.dart';
|
||||
import 'package:moonwell_launcher/app/launcher_version.dart';
|
||||
import 'package:moonwell_launcher/app/login_screen/login_screen.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/application/restore_launcher_session_use_case.dart';
|
||||
@@ -9,7 +10,9 @@ import 'package:moonwell_launcher/features/preferences/domain/repositories/prefe
|
||||
import 'package:moonwell_launcher/service_container.dart';
|
||||
|
||||
class MoonWellApp extends StatefulWidget {
|
||||
const MoonWellApp({super.key});
|
||||
const MoonWellApp({super.key, required this.launcherVersion});
|
||||
|
||||
final String launcherVersion;
|
||||
|
||||
@override
|
||||
State<MoonWellApp> createState() => _MoonWellAppState();
|
||||
@@ -35,7 +38,9 @@ class _MoonWellAppState extends State<MoonWellApp> {
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _themeController,
|
||||
builder: (context, _) => LauncherThemeScope(
|
||||
builder: (context, _) => LauncherVersionScope(
|
||||
version: widget.launcherVersion,
|
||||
child: LauncherThemeScope(
|
||||
controller: _themeController,
|
||||
child: MaterialApp(
|
||||
title: 'MoonWell',
|
||||
@@ -44,6 +49,7 @@ class _MoonWellAppState extends State<MoonWellApp> {
|
||||
theme: MoonWellTheme.create(_themeController.variant),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-1
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/mw_app.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/data/launcher_update_service.dart';
|
||||
import 'package:moonwell_launcher/service_container.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
@@ -13,6 +14,7 @@ Future<void> main() async {
|
||||
|
||||
await configureDependencies(env: 'flutter');
|
||||
final launcherUpdateService = getIt<LauncherUpdateService>();
|
||||
final launcherVersion = await _readLauncherVersion();
|
||||
|
||||
const windowOptions = WindowOptions(
|
||||
size: Size(1280, 720),
|
||||
@@ -31,5 +33,14 @@ Future<void> main() async {
|
||||
}),
|
||||
);
|
||||
|
||||
runApp(const MoonWellApp());
|
||||
runApp(MoonWellApp(launcherVersion: launcherVersion));
|
||||
}
|
||||
|
||||
Future<String> _readLauncherVersion() async {
|
||||
try {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
return packageInfo.version;
|
||||
} catch (_) {
|
||||
return 'dev';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
C:/Users/sindo/AppData/Local/Pub/Cache/hosted/pub.dev/package_info_plus-9.0.1/
|
||||
@@ -8,6 +8,7 @@ import Foundation
|
||||
import auto_updater_macos
|
||||
import file_picker
|
||||
import flutter_secure_storage_darwin
|
||||
import package_info_plus
|
||||
import screen_retriever_macos
|
||||
import shared_preferences_foundation
|
||||
import url_launcher_macos
|
||||
@@ -17,6 +18,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AutoUpdaterMacosPlugin.register(with: registry.registrar(forPlugin: "AutoUpdaterMacosPlugin"))
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
|
||||
@@ -424,6 +424,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -592,6 +600,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
package_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.0.1"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
path:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
+2
-1
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.1+2
|
||||
version: 1.0.2+4
|
||||
|
||||
environment:
|
||||
sdk: ">=3.10.0"
|
||||
@@ -47,6 +47,7 @@ dependencies:
|
||||
window_manager: ^0.5.1
|
||||
auto_updater: 1.0.0
|
||||
url_launcher: ^6.3.2
|
||||
package_info_plus: ^9.0.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_authentication.dart';
|
||||
import 'package:moonwell_launcher/app/launcher_version.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
|
||||
void main() {
|
||||
@@ -117,4 +118,26 @@ void main() {
|
||||
hasLength(1),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('authentication footer uses the packaged launcher version', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
LauncherVersionScope(
|
||||
version: '9.8.7',
|
||||
child: MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: Scaffold(
|
||||
body: MwAuthenticationForm(
|
||||
usernameController: TextEditingController(),
|
||||
passwordController: TextEditingController(),
|
||||
onSubmit: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.textContaining('V 9.8.7'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_launcher_components.dart';
|
||||
import 'package:moonwell_launcher/app/launcher_version.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
|
||||
void main() {
|
||||
@@ -137,6 +138,25 @@ void main() {
|
||||
expect(find.text('Проверить файлы'), findsOneWidget);
|
||||
expect(find.text('Выйти'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('status bar uses the packaged launcher version', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
LauncherVersionScope(
|
||||
version: '9.8.7',
|
||||
child: MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: const Scaffold(
|
||||
body: SizedBox(
|
||||
height: 26,
|
||||
child: MwLauncherStatusBar(status: 'Ready'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.textContaining('v9.8.7'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
void _noop() {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_shells.dart';
|
||||
import 'package:moonwell_launcher/app/launcher_version.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
|
||||
void main() {
|
||||
@@ -24,7 +25,9 @@ void main() {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: MwLoginShell(
|
||||
home: LauncherVersionScope(
|
||||
version: '9.8.7',
|
||||
child: MwLoginShell(
|
||||
form: const SizedBox(),
|
||||
onDrag: _noop,
|
||||
onDoubleTap: _noop,
|
||||
@@ -33,11 +36,13 @@ void main() {
|
||||
onClose: _noop,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final image = tester.widget<Image>(find.byType(Image));
|
||||
expect((image.image as AssetImage).assetName, 'assets/login_key_art.png');
|
||||
expect(find.textContaining('9.8.7'), findsOneWidget);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user