diff --git a/lib/guards/new_release_guard/bloc/new_release_cubit.dart b/lib/guards/new_release_guard/bloc/new_release_cubit.dart index 1dad283..2957a14 100644 --- a/lib/guards/new_release_guard/bloc/new_release_cubit.dart +++ b/lib/guards/new_release_guard/bloc/new_release_cubit.dart @@ -1,44 +1,39 @@ import 'package:appcenter_companion/repositories/repositories.dart'; import 'package:bloc/bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:package_info_plus/package_info_plus.dart'; -import 'package:version/version.dart'; part 'new_release_cubit.freezed.dart'; - part 'new_release_state.dart'; class NewReleaseCubit extends Cubit { - NewReleaseCubit(GitHubRepository gitHubRepository) - : _githubRepository = gitHubRepository, + NewReleaseCubit(ReleaseRepository releaseRepository) + : _releaseRepository = releaseRepository, super(NewReleaseState.initial()) { _checkForNewRelease(); } - final GitHubRepository _githubRepository; + final ReleaseRepository _releaseRepository; - void ignoreCurrentRelease({bool ignored = false}) { - emit( - state.maybeMap( - availableVersion: (value) => - value.copyWith(currentReleaseIgnored: ignored), - orElse: () => state, - ), - ); + Future ignoreCurrentRelease({bool ignored = false}) async { + final currentState = state; + if (currentState is NewReleaseStateAvailableVersion) { + await _releaseRepository.ignoreRelease( + version: currentState.version, + ignored: ignored, + ); + emit( + currentState.copyWith(currentReleaseIgnored: ignored), + ); + } } Future _checkForNewRelease() async { - final latestRelease = await _githubRepository.getLatestRelease(); - //final latestVersion = Version.parse(latestRelease.tagName); - final latestVersion = Version.parse('1.8.0'); - final packageInfo = await PackageInfo.fromPlatform(); - final currentVersion = - Version.parse('${packageInfo.version}+${packageInfo.buildNumber}'); - if (currentVersion.compareTo(latestVersion) < 0) { + final newVersion = await _releaseRepository.newVersionAvailable(); + if (newVersion != null) { emit( NewReleaseState.availableVersion( - version: latestRelease.tagName, - url: latestRelease.htmlUrl, + version: newVersion.version, + downloadUrl: newVersion.downloadUrl, ), ); } diff --git a/lib/guards/new_release_guard/bloc/new_release_state.dart b/lib/guards/new_release_guard/bloc/new_release_state.dart index 0cc9cab..60a445b 100644 --- a/lib/guards/new_release_guard/bloc/new_release_state.dart +++ b/lib/guards/new_release_guard/bloc/new_release_state.dart @@ -6,7 +6,7 @@ class NewReleaseState with _$NewReleaseState { factory NewReleaseState.availableVersion({ required String version, - required String url, + required String downloadUrl, @Default(false) bool currentReleaseIgnored, }) = NewReleaseStateAvailableVersion; } diff --git a/lib/guards/new_release_guard/new_release_dialog.dart b/lib/guards/new_release_guard/new_release_dialog.dart index a9dcb67..f01eccb 100644 --- a/lib/guards/new_release_guard/new_release_dialog.dart +++ b/lib/guards/new_release_guard/new_release_dialog.dart @@ -38,7 +38,7 @@ class NewReleaseDialog extends StatelessWidget { FilledButton( child: const Text('Update'), onPressed: () { - launchUrlString(state.url); + launchUrlString(state.downloadUrl); Navigator.of(context).pop(); }, ), diff --git a/lib/guards/new_release_guard/new_release_guard.dart b/lib/guards/new_release_guard/new_release_guard.dart index 1f6fe22..c67de25 100644 --- a/lib/guards/new_release_guard/new_release_guard.dart +++ b/lib/guards/new_release_guard/new_release_guard.dart @@ -1,4 +1,4 @@ -import 'package:appcenter_companion/repositories/github_repository.dart'; +import 'package:appcenter_companion/repositories/repositories.dart'; import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -15,7 +15,7 @@ class NewReleaseGuard extends StatefulWidget { } class _NewReleaseGuardState extends State { - late final cubit = NewReleaseCubit(context.read()); + late final cubit = NewReleaseCubit(context.read()); @override Widget build(BuildContext context) { diff --git a/lib/main.dart b/lib/main.dart index 3cc6d2d..bf32817 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -73,12 +73,16 @@ Future main() async { final GitHubRepository gitHubRepository = GitHubRepository( environment: environment, ); + final ReleaseRepository releaseRepository = ReleaseRepository( + githubRepository: gitHubRepository, + ); runApp( MultiRepositoryProvider( providers: [ RepositoryProvider.value(value: environment), RepositoryProvider.value(value: store), + RepositoryProvider.value(value: releaseRepository), RepositoryProvider.value(value: authenticationRepository), RepositoryProvider.value(value: gitHubRepository), RepositoryProvider.value(value: appcenterHttp), diff --git a/lib/repositories/authentication_repository.dart b/lib/repositories/authentication_repository.dart index 17dc4df..1700b8d 100644 --- a/lib/repositories/authentication_repository.dart +++ b/lib/repositories/authentication_repository.dart @@ -6,7 +6,7 @@ import 'package:fluent_ui/fluent_ui.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:rxdart/rxdart.dart'; -import 'storage_repository.dart'; +import 'secure_storage_repository.dart'; part 'authentication_repository.freezed.dart'; part 'authentication_repository.g.dart'; @@ -45,13 +45,13 @@ extension AuthenticationHelpers on AuthenticationState { } class AuthenticationRepository { - final StorageRepository _storage; + final SecureStorageRepository _storage; final AppcenterHttp _http; AuthenticationRepository({ - StorageRepository? storage, + SecureStorageRepository? storage, required AppcenterHttp http, - }) : _storage = storage ?? StorageRepository(), + }) : _storage = storage ?? SecureStorageRepository(), _http = http { _restoreAuth().then( (value) => diff --git a/lib/repositories/models/models.dart b/lib/repositories/models/models.dart new file mode 100644 index 0000000..681ab16 --- /dev/null +++ b/lib/repositories/models/models.dart @@ -0,0 +1 @@ +export 'new_release.dart'; diff --git a/lib/repositories/models/new_release.dart b/lib/repositories/models/new_release.dart new file mode 100644 index 0000000..87c4dfa --- /dev/null +++ b/lib/repositories/models/new_release.dart @@ -0,0 +1,11 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'new_release.freezed.dart'; + +@freezed +class NewRelease with _$NewRelease { + const factory NewRelease({ + required String version, + required String downloadUrl, + }) = _NewRelease; +} diff --git a/lib/repositories/release_repository.dart b/lib/repositories/release_repository.dart new file mode 100644 index 0000000..c88a868 --- /dev/null +++ b/lib/repositories/release_repository.dart @@ -0,0 +1,62 @@ +import 'package:appcenter_companion/repositories/github_repository.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:version/version.dart'; + +import 'models/new_release.dart'; + +class ReleaseRepository { + ReleaseRepository({ + required GitHubRepository githubRepository, + }) : _githubRepository = githubRepository; + + final GitHubRepository _githubRepository; + + Future get _sharedPreferences => + SharedPreferences.getInstance(); + + Future> get _ignoredVersions => _sharedPreferences + .then((prefs) => prefs.getStringList(_keyIgnoredVersions) ?? []); + + Future _setIgnoredVersions(List ignoredVersions) => + _sharedPreferences.then( + (prefs) => prefs.setStringList(_keyIgnoredVersions, ignoredVersions), + ); + + final _keyIgnoredVersions = 'ignoredVersions'; + + Future newVersionAvailable() async { + final latestRelease = await _githubRepository.getLatestRelease(); + final latestVersion = Version.parse(latestRelease.tagName); + final ignoredVersions = await _ignoredVersions; + + if (ignoredVersions.contains(latestVersion.toString())) { + return null; + } + + final packageInfo = await PackageInfo.fromPlatform(); + final currentVersion = + Version.parse('${packageInfo.version}+${packageInfo.buildNumber}'); + if (currentVersion.compareTo(latestVersion) < 0) { + return NewRelease( + version: latestVersion.toString(), + downloadUrl: latestRelease.htmlUrl, + ); + } + return null; + } + + Future ignoreRelease({ + bool ignored = true, + required String version, + }) async { + final ignoredVersions = await _ignoredVersions; + if (ignored && !ignoredVersions.contains(version)) { + ignoredVersions.add(version); + } + if (!ignored && ignoredVersions.contains(version)) { + ignoredVersions.remove(version); + } + await _setIgnoredVersions(ignoredVersions); + } +} diff --git a/lib/repositories/repositories.dart b/lib/repositories/repositories.dart index 7474b65..386141e 100644 --- a/lib/repositories/repositories.dart +++ b/lib/repositories/repositories.dart @@ -6,3 +6,5 @@ export 'bundled_application_repository.dart'; export 'dto/dto.dart'; export 'entities/entities.dart'; export 'github_repository.dart'; +export 'models/models.dart'; +export 'release_repository.dart'; diff --git a/lib/repositories/storage_repository.dart b/lib/repositories/secure_storage_repository.dart similarity index 95% rename from lib/repositories/storage_repository.dart rename to lib/repositories/secure_storage_repository.dart index 4a3b403..ebc6264 100644 --- a/lib/repositories/storage_repository.dart +++ b/lib/repositories/secure_storage_repository.dart @@ -3,8 +3,8 @@ import 'dart:io'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:shared_preferences/shared_preferences.dart'; -class StorageRepository { - StorageRepository() { +class SecureStorageRepository { + SecureStorageRepository() { // Can't use secure storage on mac os if the app not signed, which will required a developer program account _useSecureStorage = !Platform.isMacOS; }