Changelog and What's new: CHANGELOG.md cut into each release, shown in the update band, a What's new card and once after updating

CHANGELOG.md keeps an Unreleased section (user-facing changes add a line in
the same commit); tool/release.sh cuts it into the version, the manifest
gets a structured "changes" field next to the one-line "notes" older
apps read, and the Gitea release text is that section. The app's update
band shows the top items, What's new opens a card (Added / Changed /
Fixed), and the first start after an update shows it once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-18 02:11:37 +03:00
1 parent ec0849eb01
commit 638a30e16c
22 files changed
+1637 -58

No files matched your search

+24
View File
@@ -44,6 +44,8 @@ const keyAutoSitOut = 'autoSitOut';
const _keyChatPopups = 'chatPopups';
const _keyNotifyPeople = 'notifyPeople';
const _keyShareDiagnostics = 'shareDiagnostics';
const _keyWhatsNewSeen = 'whatsNewSeen';
const _keyWhatsNewNotes = 'whatsNewNotes';
enum HwdecMode { auto, off, custom }
@@ -107,6 +109,13 @@ class AppSettings {
/// `SfxKind` name -> `SoundPreset` name, per-sound overrides.
final soundPresetFor = signal<Map<String, String>>(const {});
/// What's new (`update/whats_new.dart`): the version whose card was
/// shown, and the notes last kept (a version's `ReleaseNotes` as JSON).
String? whatsNewSeen;
Map<String, Object?>? whatsNewNotes;
/// The box was empty when this run opened it: a new install.
bool freshInstall = false;
/// Video shaders: one choice, used on join and changed both here and
/// in the session (Ctrl+0..6, the shader button).
final shaderPreset = signal(ShaderPreset.off);
@@ -131,6 +140,7 @@ class AppSettings {
Future<void> _loadNow() async {
try {
final box = _box = await Hive.openBox(settingsBoxName);
freshInstall = box.isEmpty;
for (final line in await migrateSettings(box)) {
debugPrint(line);
}
@@ -194,6 +204,10 @@ class AppSettings {
shaderQuality.value =
ShaderQuality.values.where((q) => q.name == quality).firstOrNull ??
ShaderQuality.hq;
final seen = box.get(_keyWhatsNewSeen);
if (seen is String) whatsNewSeen = seen;
final notes = box.get(_keyWhatsNewNotes);
if (notes is Map) whatsNewNotes = {for (final e in notes.entries) '${e.key}': e.value};
final files = box.get(_keyCustomShaders);
if (files is List) {
customShaders.value = List.unmodifiable(files.whereType<String>());
@@ -328,6 +342,16 @@ class AppSettings {
_put(_keyCustomShaders, [...paths]);
}
Future<void> setWhatsNewSeen(String version) {
whatsNewSeen = version;
return _put(_keyWhatsNewSeen, version);
}
Future<void> setWhatsNewNotes(Map<String, Object?> notes) {
whatsNewNotes = notes;
return _put(_keyWhatsNewNotes, notes);
}
Future<void> _put(String key, Object value) async {
try {
final box = _box ??