From 881778b31364b346d003d5747356f96bcd7e1b30 Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Wed, 22 May 2024 22:46:46 +0200 Subject: [PATCH] Show release notes after update (#178) * replace generated release notes with pre-built assets * implemented release notes dialog * store release notes for version * show release notes dialog after update * added release notes dialog to settings * allow blank values in settings * updated release notes --- .github/workflows/create_release.yml | 40 ++----------- .../release_notes/release_notes_en_0.19.0.md | 4 ++ .../release_notes/release_notes_ru_0.19.0.md | 4 ++ lib/data/shared_prefs_service.dart | 5 ++ lib/l10n/intl_en.arb | 12 +++- .../version/widget_list_tile_version.dart | 21 ++++--- .../bloc_release_notes.dart | 27 +++++++++ .../flow_dialog_release_notes.dart | 30 ++++++++++ .../state_release_notes.dart | 16 +++++ .../widget_dialog_release_notes.dart | 58 +++++++++++++++++++ pubspec.yaml | 1 + 11 files changed, 174 insertions(+), 44 deletions(-) create mode 100644 assets/release_notes/release_notes_en_0.19.0.md create mode 100644 assets/release_notes/release_notes_ru_0.19.0.md create mode 100644 lib/screens/shared/release_notes_dialog/bloc_release_notes.dart create mode 100644 lib/screens/shared/release_notes_dialog/flow_dialog_release_notes.dart create mode 100644 lib/screens/shared/release_notes_dialog/state_release_notes.dart create mode 100644 lib/screens/shared/release_notes_dialog/widget_dialog_release_notes.dart diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index 035ab0a..e784717 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -18,10 +18,6 @@ on: description: "Version" required: true type: string - release-notes: - description: "Release notes" - required: true - type: string run-integration-tests: description: "Run integration tests" required: true @@ -60,25 +56,9 @@ jobs: stage-backend: false version: ${{ inputs.version }} - generate-release-notes: - name: Generate release notes - needs: [build-android, build-ios] - runs-on: ubuntu-latest - steps: - - name: Generate release notes - run: | - echo ${{ inputs.release-notes }} > whatsnew-en-US.md - perl -i -pe 's/\s{1}(-{1})/\n$1/g' whatsnew-en-US.md - - - name: Upload merged_native_libs.zip to artifacts - uses: actions/upload-artifact@v3 - with: - name: whatsnew-en-US - path: whatsnew-en-US.md - create-github-release: name: Create Github release - needs: [generate-release-notes] + needs: [build-android, build-ios] runs-on: ubuntu-latest permissions: contents: write @@ -112,21 +92,16 @@ jobs: - name: Rename apk run: mv app-prod-release.apk m3_lightmeter.apk - - name: Download release notes - uses: actions/download-artifact@v3 - with: - name: whatsnew-en-US - - uses: ncipollo/release-action@v1.12.0 with: artifacts: "m3_lightmeter.apk" skipIfReleaseExists: true tag: "v${{ github.event.inputs.version }}" - bodyFile: "whatsnew-en-US.md" + bodyFile: "assets/release_notes/release_notes_en_${{ inputs.version }}.md" create-google-play-release: name: Create Google Play release - needs: [generate-release-notes] + needs: [build-android, build-ios] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -143,14 +118,9 @@ jobs: unzip app-prod-release.aab (cd base/lib && zip -r "$OLDPWD/merged_native_libs.zip" .) - - name: Download release notes - uses: actions/download-artifact@v3 - with: - name: whatsnew-en-US - - name: Move release notes to a folder run: | - mv whatsnew-en-US.md whatsnew-en-US + mv assets/release_notes/release_notes_en_${{ inputs.version }}.md whatsnew-en-US mkdir whatsnew mv whatsnew-en-US whatsnew @@ -177,7 +147,7 @@ jobs: upload-to-app-store: name: Upload to App Store - needs: [generate-release-notes] + needs: [build-android, build-ios] runs-on: macos-13 steps: - uses: actions/checkout@v3 diff --git a/assets/release_notes/release_notes_en_0.19.0.md b/assets/release_notes/release_notes_en_0.19.0.md new file mode 100644 index 0000000..56290e0 --- /dev/null +++ b/assets/release_notes/release_notes_en_0.19.0.md @@ -0,0 +1,4 @@ +- [Pro] Added a timer for shooting long exposures without leaving the app, just tap the necessary shutter speed in the list. +- Long exposure values are no longer limited by 1" and are generated to match the list of aperture values. +- Refined the app's color palette & unified icons across the app. +- Added release notes dialog. diff --git a/assets/release_notes/release_notes_ru_0.19.0.md b/assets/release_notes/release_notes_ru_0.19.0.md new file mode 100644 index 0000000..f30d037 --- /dev/null +++ b/assets/release_notes/release_notes_ru_0.19.0.md @@ -0,0 +1,4 @@ +- [Pro] Добавлен таймер для удобства съёмки при длительных выдержках. Достаточно просто нажать на нужное значение в списке экспозиционных пар. +- Длинные выдержки больше не ограничены 1" и генерируются в соответствии со значениями диафрагмы. +- Переработана цветовая палитра приложения и унифицированы иконки. +- Добавлен список изменений. diff --git a/lib/data/shared_prefs_service.dart b/lib/data/shared_prefs_service.dart index 57f947d..2662d7f 100644 --- a/lib/data/shared_prefs_service.dart +++ b/lib/data/shared_prefs_service.dart @@ -32,6 +32,8 @@ class UserPreferencesService { static const primaryColorKey = "primaryColor"; static const dynamicColorKey = "dynamicColor"; + static const seenChangelogVersionKey = "seenChangelogVersion"; + final SharedPreferences _sharedPreferences; UserPreferencesService(this._sharedPreferences) { @@ -157,4 +159,7 @@ class UserPreferencesService { bool get dynamicColor => _sharedPreferences.getBool(dynamicColorKey) ?? false; set dynamicColor(bool value) => _sharedPreferences.setBool(dynamicColorKey, value); + + String get seenChangelogVersion => _sharedPreferences.getString(seenChangelogVersionKey) ?? ''; + set seenChangelogVersion(String value) => _sharedPreferences.setString(seenChangelogVersionKey, value); } diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 34247a6..0b973c9 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -119,5 +119,15 @@ "tooltipUseLightSensor": "Use lightsensor", "tooltipUseCamera": "Use camera", "tooltipOpenSettings": "Open settings", - "exposurePair": "Exposure pair" + "exposurePair": "Exposure pair", + "whatsnew": "What's new?", + "changesInVersion": "Changes in version {version}:", + "@changesInVersion": { + "placeholders": { + "version": { + "type": "String" + } + } + }, + "close": "Close" } \ No newline at end of file diff --git a/lib/screens/settings/components/about/components/version/widget_list_tile_version.dart b/lib/screens/settings/components/about/components/version/widget_list_tile_version.dart index 347af56..c37d80f 100644 --- a/lib/screens/settings/components/about/components/version/widget_list_tile_version.dart +++ b/lib/screens/settings/components/about/components/version/widget_list_tile_version.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/shared/release_notes_dialog/widget_dialog_release_notes.dart'; import 'package:package_info_plus/package_info_plus.dart'; class VersionListTile extends StatelessWidget { @@ -7,14 +8,18 @@ class VersionListTile extends StatelessWidget { @override Widget build(BuildContext context) { - return ListTile( - leading: const Icon(Icons.info_outline), - title: Text(S.of(context).version), - trailing: FutureBuilder( - future: PackageInfo.fromPlatform(), - builder: (context, snapshot) => snapshot.data != null - ? Text(S.of(context).versionNumber(snapshot.data!.version, snapshot.data!.buildNumber)) - : const SizedBox.shrink(), + return FutureBuilder( + future: PackageInfo.fromPlatform(), + builder: (context, snapshot) => ListTile( + leading: const Icon(Icons.info_outline), + title: Text(S.of(context).version), + onTap: snapshot.data != null + ? () => showDialog( + context: context, + builder: (_) => ReleaseNotesDialog(version: snapshot.data!.version), + ) + : null, + trailing: Text(S.of(context).versionNumber(snapshot.data?.version ?? '', snapshot.data?.buildNumber ?? '')), ), ); } diff --git a/lib/screens/shared/release_notes_dialog/bloc_release_notes.dart b/lib/screens/shared/release_notes_dialog/bloc_release_notes.dart new file mode 100644 index 0000000..c7c2bc9 --- /dev/null +++ b/lib/screens/shared/release_notes_dialog/bloc_release_notes.dart @@ -0,0 +1,27 @@ +import 'dart:async'; + +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/data/shared_prefs_service.dart'; +import 'package:lightmeter/screens/shared/release_notes_dialog/state_release_notes.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +class ReleaseNotesBloc extends Cubit { + final UserPreferencesService _userPreferencesService; + + ReleaseNotesBloc(this._userPreferencesService) : super(const HiddenReleaseNotesDialogState()) { + _showDialogIfNeeded(); + } + + Future _showDialogIfNeeded() async { + PackageInfo.fromPlatform().then((value) { + emit(ShowReleaseNotesDialogState(value.version)); + if (value.version != _userPreferencesService.seenChangelogVersion) { + emit(ShowReleaseNotesDialogState(value.version)); + } + }); + } + + void setChangelogVersion() { + _userPreferencesService.seenChangelogVersion = (state as ShowReleaseNotesDialogState).version; + } +} diff --git a/lib/screens/shared/release_notes_dialog/flow_dialog_release_notes.dart b/lib/screens/shared/release_notes_dialog/flow_dialog_release_notes.dart new file mode 100644 index 0000000..b52710e --- /dev/null +++ b/lib/screens/shared/release_notes_dialog/flow_dialog_release_notes.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/providers/services_provider.dart'; +import 'package:lightmeter/screens/shared/release_notes_dialog/bloc_release_notes.dart'; +import 'package:lightmeter/screens/shared/release_notes_dialog/state_release_notes.dart'; +import 'package:lightmeter/screens/shared/release_notes_dialog/widget_dialog_release_notes.dart'; + +class ReleaseNotesFlow extends StatelessWidget { + final Widget child; + + const ReleaseNotesFlow({required this.child, super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => ReleaseNotesBloc(ServicesProvider.of(context).userPreferencesService), + child: BlocListener( + listener: (context, state) { + if (state is ShowReleaseNotesDialogState) { + showDialog( + context: context, + builder: (_) => ReleaseNotesDialog(version: state.version), + ).then((_) => context.read().setChangelogVersion()); + } + }, + child: child, + ), + ); + } +} diff --git a/lib/screens/shared/release_notes_dialog/state_release_notes.dart b/lib/screens/shared/release_notes_dialog/state_release_notes.dart new file mode 100644 index 0000000..73404ec --- /dev/null +++ b/lib/screens/shared/release_notes_dialog/state_release_notes.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +@immutable +sealed class ReleaseNotesState { + const ReleaseNotesState(); +} + +class HiddenReleaseNotesDialogState extends ReleaseNotesState { + const HiddenReleaseNotesDialogState(); +} + +class ShowReleaseNotesDialogState extends ReleaseNotesState { + final String version; + + const ShowReleaseNotesDialogState(this.version); +} diff --git a/lib/screens/shared/release_notes_dialog/widget_dialog_release_notes.dart b/lib/screens/shared/release_notes_dialog/widget_dialog_release_notes.dart new file mode 100644 index 0000000..390399d --- /dev/null +++ b/lib/screens/shared/release_notes_dialog/widget_dialog_release_notes.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; +import 'package:lightmeter/data/models/supported_locale.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/providers/user_preferences_provider.dart'; +import 'package:lightmeter/res/dimens.dart'; + +class ReleaseNotesDialog extends StatelessWidget { + final String version; + + const ReleaseNotesDialog({required this.version, super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(S.of(context).whatsnew), + content: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + S.of(context).changesInVersion(version), + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox(height: Dimens.grid8), + FutureBuilder( + future: loadReleaseNotes(context), + builder: (context, snapshot) => Text(snapshot.data ?? ''), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: Navigator.of(context).pop, + child: Text(S.of(context).close), + ), + ], + ); + } + + Future loadReleaseNotes(BuildContext context) async { + late final String localeName; + + switch (UserPreferencesProvider.localeOf(context)) { + case SupportedLocale.ru: + localeName = SupportedLocale.ru.name; + default: + localeName = SupportedLocale.en.name; + } + + try { + return rootBundle.loadString('assets/release_notes/release_notes_${localeName}_$version.md'); + } catch (e) { + return ''; + } + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 391e525..7b14005 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -68,6 +68,7 @@ flutter: uses-material-design: true assets: - assets/camera_stub_image.jpg + - assets/release_notes/ flutter_intl: enabled: true