nc-photos/app/lib/widget/changelog.dart
Ming Ming 8d75ab7967 66.3
2024-06-14 00:12:25 +08:00

618 lines
18 KiB
Dart

import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/theme.dart';
import 'package:np_codegen/np_codegen.dart';
part 'changelog.g.dart';
part 'changelog/changelog_550.dart';
part 'changelog/changelog_560.dart';
part 'changelog/changelog_570.dart';
part 'changelog/changelog_580.dart';
part 'changelog/changelog_590.dart';
part 'changelog/changelog_600.dart';
part 'changelog/changelog_610.dart';
part 'changelog/changelog_630.dart';
part 'changelog/changelog_640.dart';
part 'changelog/changelog_650.dart';
part 'changelog/changelog_660.dart';
part 'changelog/changelog_662.dart';
part 'changelog/changelog_663.dart';
class ChangelogArguments {
const ChangelogArguments(this.fromVersion);
final int fromVersion;
}
@npLog
class Changelog extends StatelessWidget {
static const routeName = "/changelog";
static Route buildRoute(ChangelogArguments args) => MaterialPageRoute(
builder: (context) => Changelog.fromArgs(args),
);
static bool hasContent(int fromVersion) =>
_changelogs.keys.first > fromVersion;
const Changelog({
super.key,
this.fromVersion,
});
Changelog.fromArgs(ChangelogArguments args, {Key? key})
: this(
key: key,
fromVersion: args.fromVersion,
);
@override
build(BuildContext context) => Scaffold(
appBar: _buildAppBar(),
body: Builder(builder: (context) => _buildContent(context)),
);
AppBar _buildAppBar() => AppBar(
title: Text(L10n.global().changelogTitle),
elevation: 0,
);
Widget _buildContent(BuildContext context) {
return Column(
children: [
Expanded(
child: ListView.builder(
itemCount: _changelogs.length,
itemBuilder: _buildItem,
),
),
],
);
}
Widget _buildItem(BuildContext context, int i) {
try {
final version = _changelogs.keys.elementAt(i);
return ExpansionTile(
key: PageStorageKey(i),
title: Text((version / 10).toStringAsFixed(1)),
initiallyExpanded:
fromVersion == null ? (i == 0) : (version > fromVersion!),
expandedCrossAxisAlignment: CrossAxisAlignment.start,
expandedAlignment: Alignment.topLeft,
childrenPadding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
children: _changelogs[version]!(context),
);
} catch (e, stackTrace) {
_log.severe("[_buildItem] Uncaught exception", e, stackTrace);
return const SizedBox();
}
}
final int? fromVersion;
}
List<Widget> _buildChangelog460(BuildContext context) {
return [
_subSectionHighlight("IMPORTANT"),
_bulletGroup(const Text(
"Completely reworked the cache database, the app will perform a full resync with server from scratch")),
_bulletGroup(const Text(
"(LDAP user only) Previously the username is used in place of user ID incorrectly, this is now fixed but albums created before may become broken. Please recreate them if that's the case. Sorry for your inconvenience")),
_sectionPadding(),
_subSection("Improvements"),
_bulletGroup(const Text(
"Improved performance working with a large collection of photos")),
_bulletGroup(
const Text("New image editor"),
[
const Text(
"Adjust image brightness, contrast, white point, black point, saturation, warmth and tint")
],
),
_bulletGroup(
const Text("Double tap to exit"),
[
const Text("Settings > Miscellaneous > Double tap to exit"),
],
),
_bulletGroup(
const Text("Replace account info in the app bar with customized label"),
[
const Text("Settings > Account > Label"),
],
),
_sectionPadding(),
_subSection("Localization"),
_bulletGroup(const Text("Updated Finnish (by pHamala)")),
_bulletGroup(const Text("Updated Spanish (by luckkmaxx)")),
_sectionPadding(),
_subSection("Known issues"),
_bulletGroup(const Text(
"Google Maps is temporarily disabled due to a bug in the Maps SDK. The app will use OSM as the only map provider until the bug is fixed")),
];
}
List<Widget> _buildChangelog470(BuildContext context) {
return [
_subSection("Localization"),
_bulletGroup(const Text("Updated Spanish (by luckkmaxx)")),
_sectionPadding(),
_subSection("Known issues"),
_bulletGroup(const Text(
"Google Maps is temporarily disabled due to a bug in the Maps SDK. The app will use OSM as the only map provider until the bug is fixed")),
];
}
List<Widget> _buildChangelog480(BuildContext context) {
return [
_subSectionHighlight("IMPORTANT"),
_bulletGroup(
const Text("Favorites and People are relocated to the Search tab")),
_sectionPadding(),
_subSection("Improvements"),
_bulletGroup(const Text("Search")),
_sectionPadding(),
_subSection("Localization"),
_bulletGroup(const Text("Updated Finnish (by pHamala)")),
_bulletGroup(const Text("Updated Spanish (by luckkmaxx)")),
_sectionPadding(),
_subSection("Known issues"),
_bulletGroup(const Text(
"Google Maps is temporarily disabled due to a bug in the Maps SDK. The app will use OSM as the only map provider until the bug is fixed")),
];
}
List<Widget> _buildChangelog500(BuildContext context) {
return [
_subSection("Changes"),
_bulletGroup(
const Text(
"Search and show places converted from GPS coordinates embedded in EXIF"),
[
const Text("The app will process your photos in the background"),
],
),
_bulletGroup(const Text("Google Maps is now re-enabled on some devices")),
_sectionPadding(),
_subSection("Localization"),
_bulletGroup(const Text("Updated Finnish (by pHamala)")),
_sectionPadding(),
_subSection("Known issues"),
_bulletGroup(const Text(
"Google Maps is temporarily disabled on some devices due to a bug in the Maps SDK")),
];
}
List<Widget> _buildChangelog510(BuildContext context) {
return [
_subSection("Changes"),
_bulletGroup(
const Text("New image editing tools"),
[
const Text("Crop"),
const Text("Change the orientation (90°, 180°, 270°) of an image"),
],
),
_bulletGroup(const Text("Search now returns more relavant results")),
_bulletGroup(
const Text("Tweak how many days should be included in Memories"),
[
const Text("Settings > Photos > Memories range"),
],
),
];
}
List<Widget> _buildChangelog520(BuildContext context) {
return [
_subSection("Changes"),
_bulletGroup(const Text(
"New option to share a reduced quality preview instead of the original file")),
_bulletGroup(const Text(
"Support saving enhanced/edited photos directly on the server")),
_bulletGroup(
const Text("Image enhancements"),
[
const Text("Added color pop"),
const Text("Improved portrait blur in scenes with multiple objects"),
],
),
_bulletGroup(
const Text("Image editor"),
[
const Text("Fixed changing image orientation may flip the image"),
],
),
];
}
List<Widget> _buildChangelog530(BuildContext context) {
return [
_subSection("Changes"),
_bulletGroup(
const Text("New enhancement: auto retouch"),
[
const Text(
"Automatically retouch your photos, improve color and vibrance"),
],
),
_bulletGroup(const Text(
"A redesigned enhancement page that is more informative and beautiful")),
_sectionPadding(),
_subSection("Localization"),
_bulletGroup(const Text("Updated Spanish (by luckkmaxx)")),
];
}
List<Widget> _buildChangelog540(BuildContext context) {
return [
_subSection("Changes"),
_bulletGroup(const Text("Performance tweaks")),
_sectionPadding(),
_subSection("Localization"),
_bulletGroup(const Text("Updated Spanish (by luckkmaxx)")),
];
}
// ignore: unused_element
class _ChangelogBanner extends StatelessWidget {
const _ChangelogBanner({
required this.title,
// ignore: unused_element
this.action,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
color:
Theme.of(context).elevate(Theme.of(context).colorScheme.surface, 2),
child: DefaultTextStyle(
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
child: TextButtonTheme(
data: TextButtonThemeData(
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.primary),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: title,
),
if (action != null) action!,
],
),
),
),
);
}
final Widget title;
final Widget? action;
}
List<Widget> _buildChangelogCompat(BuildContext context, int majorVersion) {
var change = _oldChangelogs[majorVersion - 1];
if (change != null) {
try {
// remove the 1st line showing the version number repeatedly
change = change.substring(change.indexOf("\n")).trim();
} catch (_) {
change = _oldChangelogs[majorVersion - 1];
}
}
return [Text(change ?? "n/a")];
}
Widget _sectionPadding() => const SizedBox(height: 16);
Widget _subSection(String label) => Text(
label,
style: const TextStyle(fontSize: 16),
);
Widget _subSectionHighlight(String label) => Text(
label,
style: const TextStyle(fontSize: 16, color: Colors.red),
);
Widget _bulletGroup(Widget main, [List<Widget> children = const []]) => Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Column(
children: [
_bulletPoint(main),
...children.map((s) => _subBulletPoint(s)),
],
),
);
Widget _bulletPoint(Widget child) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 4),
const Text(_bullet),
const SizedBox(width: 4),
Expanded(child: child),
],
);
Widget _subBulletPoint(Widget child) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 24),
const Text(_bullet),
const SizedBox(width: 4),
Expanded(child: child),
],
);
final _changelogs = <int, List<Widget> Function(BuildContext)>{
663: (_) => const [_Changelog663()],
662: (_) => const [_Changelog662()],
660: (_) => const [_Changelog660()],
650: (_) => const [_Changelog650()],
640: (_) => const [_Changelog640()],
630: (_) => const [_Changelog630()],
610: (_) => const [_Changelog610()],
600: (_) => const [_Changelog600()],
590: (_) => const [_Changelog590()],
580: (_) => const [_Changelog580()],
570: (_) => const [_Changelog570()],
560: (_) => const [_Changelog560()],
550: (_) => const [_Changelog550()],
540: _buildChangelog540,
530: _buildChangelog530,
520: _buildChangelog520,
510: _buildChangelog510,
500: _buildChangelog500,
480: _buildChangelog480,
470: _buildChangelog470,
460: _buildChangelog460,
450: (context) => _buildChangelogCompat(context, 45),
440: (context) => _buildChangelogCompat(context, 44),
430: (context) => _buildChangelogCompat(context, 43),
420: (context) => _buildChangelogCompat(context, 42),
410: (context) => _buildChangelogCompat(context, 41),
400: (context) => _buildChangelogCompat(context, 40),
380: (context) => _buildChangelogCompat(context, 38),
370: (context) => _buildChangelogCompat(context, 37),
360: (context) => _buildChangelogCompat(context, 36),
350: (context) => _buildChangelogCompat(context, 35),
340: (context) => _buildChangelogCompat(context, 34),
320: (context) => _buildChangelogCompat(context, 32),
310: (context) => _buildChangelogCompat(context, 31),
300: (context) => _buildChangelogCompat(context, 30),
290: (context) => _buildChangelogCompat(context, 29),
280: (context) => _buildChangelogCompat(context, 28),
270: (context) => _buildChangelogCompat(context, 27),
260: (context) => _buildChangelogCompat(context, 26),
240: (context) => _buildChangelogCompat(context, 24),
230: (context) => _buildChangelogCompat(context, 23),
200: (context) => _buildChangelogCompat(context, 20),
190: (context) => _buildChangelogCompat(context, 19),
180: (context) => _buildChangelogCompat(context, 18),
170: (context) => _buildChangelogCompat(context, 17),
150: (context) => _buildChangelogCompat(context, 15),
130: (context) => _buildChangelogCompat(context, 13),
80: (context) => _buildChangelogCompat(context, 8),
70: (context) => _buildChangelogCompat(context, 7),
};
const _bullet = "\u2022";
const _oldChangelogs = [
// v1
null,
// v2
null,
// v3
null,
// v4
null,
// v5
null,
// v6
null,
// v7
"""1.7.0
Added HEIC support
Fixed a bug that corrupted the albums. Please re-add the photos after upgrading. Sorry for your inconvenience
""",
// v8
"""1.8.0
Dark theme
""",
// v9
null,
// v10
null,
// v11
null,
// v12
null,
// v13
"""13.0
Added MP4 support (Android only)
""",
// v14
null,
// v15
"""15.0
This version includes changes that are not compatible with older versions. Please also update your other devices if applicable
""",
// v16
null,
// v17
"""17.0
Archive photos to only show them in albums
Link to report issues in Settings
""",
// v18
"""18.0
Modify date/time of photos
Support GIF
""",
// v19
"""19.0
- Folder based album to browse photos in an existing folder (read only)
- Batch import folder based albums
This version includes changes that are not compatible with older versions. Please also update your other devices if applicable
""",
// v20
"""20.0
- Improved albums: sorting, text labels
- Simplify sharing to other apps
- Added WebM support (Android only)
""",
// v21
null,
// v22
null,
// v23
"""23.0
- Paid version is now published on Play Store. Head to Settings to learn more if you are interested
""",
// v24
"""24.0
- Show and manage deleted files in trash bin
""",
// v25
null,
// v26
"""26.0
- Pick album cover (open a photo in an album -> details -> use as cover)
""",
// v27
"""27.0
- New settings to customize photo viewer
""",
// v28
"""28.0
- New settings:
- Follow system dark theme settings (Android 10+)
""",
// v29
"""29.0
Features:
- (Experimental) Support the Nextcloud Face Recognition app
- Slideshow
- Performance & cache tweaks
- Due to an overhaul to the cache management, the old cache can't be used and will be cleared. First run after update will thus be slower
Localization (new/update):
- German (by PhilProg)
- Spanish (by luckkmaxx)
""",
// v30
"""30.0
Features:
- Share a single item using a link
- Optimize albums: the JSON files are now much smaller
- Download album/selected items
Localization (new/update):
- Czech (by Skyhawk)
- Spanish (by luckkmaxx)
""",
// v31
"""31.0
Features:
- Share multiple items using a link
- Manage shares in Collections > Sharing
- (Web) Now support share links like Android
- Group photos by date in albums (enable in Settings > Album)
""",
// v32
"""32.0
Features:
- Enable/disable server app integrations in Settings > Account
""",
// v33
null,
// v34
"""34.0
- Add OSM as an alternative map provider (Settings > Viewer)
- (Experimental) Add shared album (Settings > Experimental)
- (UI) Swipe up to show photo details
- (Localization) Update Spanish (by luckkmaxx)
""",
// v35
"""35.0
- Optimize start up performance
- Photos should appear more quickly on start up
- (UI) Swipe down to close the photo viewer
- (Localization) Add Finnish (by pHamala)
* The app needs to resync with the server due to changes in the database
""",
// v36
"""36.0
- Memories
- Show photos taken in the past
""",
// v37
"""37.0
- Favorites
- Browse favorites (Collections > Favorites)
- Add to or remove from favorites in photo viewer
- Tag
- Browse photos by specific tags (Collections > New collection > Tag)
- (Localization) Add Polish (by szymok)
- (Localization) Update Finnish (by pHamala)
""",
// v38
"""38.0
- (Android) Image metadata are now processed in a background service
- (Localization) Update Finnish (by pHamala)
- (Localization) Update Spanish (by luckkmaxx)
""",
// v39
null,
// v40
"""40.0
- (Android) Fixed a race condition causing the app to deadlock
- (Localization) Add Portuguese (by fernosan)
- (Localization) Update Finnish (by pHamala)
- (Localization) Update Russian (by kvasenok)
""",
// v41
"""41.0
- (Android) Enhance your photo with the new Enhance button in viewer
- (Android) New photo enhancement algorithms:
- Low-light enhancement
- Portrait blur
- (Localization) Add Chinese (by zerolin)
- (Localization) Update French (by mgreil)
""",
// v42
"""42.0
- Add tweakable parameters to low-light enhancement and portrait blur
""",
// v43
"""43.0
- (Android) Photo enhancements now implemented in C++:
- Better performance
- Less restrictions on RAM usage
- (Android) New photo enhancement algorithms:
- Super-resolution (upscale image to 4x)
- (Localization) Update Finnish (by pHamala)
""",
// v44
"""44.0
- (Android) New photo enhancement algorithms:
- Style transfer
""",
// v45
"""45.0
- Toggle between processing EXIF over Wi-Fi only or any network in Settings
- (Localization) Update Greek (by Chris Karasoulis)
""",
];