diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 4b8cafc..8989cea 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -38,17 +38,17 @@ jobs: # with: # api-level: 29 # script: flutter drive --driver=test_driver/main_test.dart --target integration_test/app_test.dart - - name: create Fat APK - run: flutter build apk --release + # - name: create Fat APK + # run: flutter build apk --release - name: create Split APK run: flutter build apk --split-per-abi - - name: Upload Fat APK - uses: actions/upload-artifact@v3 - with: - name: release-fat-apk - path: build/app/outputs/apk/release/app-release.apk + # - name: Upload Fat APK + # uses: actions/upload-artifact@v3 + # with: + # name: release-fat-apk + # path: build/app/outputs/apk/release/app-release.apk - name: Upload Split APK uses: actions/upload-artifact@v3 @@ -64,7 +64,6 @@ jobs: uses: ncipollo/release-action@v1 with: artifacts: "build/app/outputs/apk/release/*.apk, build/app/outputs/flutter-apk/*.apk" - tag: v${{ steps.read-version.outputs.version-number }} token: ${{ secrets.TOKEN }} generateReleaseNotes: true - name: Checkout diff --git a/CHANGELOG.md b/CHANGELOG.md index 1571e28..a05cf4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.0.2+3 +- Auto update app version support +- Update settings for changelog view +- + + # 1.0.2+2 - Update setting pages to add app info, bug report feature - Added Localization diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 9cb4f64..243ade3 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -11,7 +11,6 @@ import 'package:flutter_sharez/core/theme/theme_controller.dart'; import 'package:flutter_sharez/l10n/l10n.dart'; import 'package:flutter_sharez/shared/helper/global_helper.dart'; import 'package:flutter_sharez/shared/pods/locale_pod.dart'; -import 'package:flutter_sharez/shared/widget/no_internet_widget.dart'; ///This class holds Material App or Cupertino App ///with routing,theming and locale setup . @@ -112,7 +111,7 @@ class _AppState extends ConsumerState with GlobalHelper { return Toast( navigatorKey: navigatorKey, child: child, - ).monitorConnection(); + ); }, ); } diff --git a/lib/core/router/router.dart b/lib/core/router/router.dart index ce3f70e..6ffb1a1 100644 --- a/lib/core/router/router.dart +++ b/lib/core/router/router.dart @@ -121,5 +121,18 @@ class AppRouter extends $AppRouter { ); }, ), + CustomRoute( + page: ChangelogRoute.page, + path: '/changeLog', + customRouteBuilder: + (BuildContext context, Widget child, AutoRoutePage page) { + return DialogRoute( + context: context, + // this is important + settings: page, + builder: (_) => child, + ); + }, + ), ]; } diff --git a/lib/core/router/router.gr.dart b/lib/core/router/router.gr.dart index 1930032..d64089c 100644 --- a/lib/core/router/router.gr.dart +++ b/lib/core/router/router.gr.dart @@ -8,55 +8,71 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i17; -import 'package:flutter/material.dart' as _i18; -import 'package:flutter_sharez/data/model/receiver_model.dart' as _i19; -import 'package:flutter_sharez/data/model/sender_model.dart' as _i20; +import 'package:auto_route/auto_route.dart' as _i18; +import 'package:flutter/material.dart' as _i19; +import 'package:flutter_sharez/data/model/receiver_model.dart' as _i21; +import 'package:flutter_sharez/data/model/sender_model.dart' as _i22; +import 'package:flutter_sharez/data/model/update_model.dart' as _i20; import 'package:flutter_sharez/features/confirm_connection/view/confirm_connection_page.dart' - deferred as _i1; -import 'package:flutter_sharez/features/counter/view/counter_page.dart' deferred as _i2; +import 'package:flutter_sharez/features/counter/view/counter_page.dart' + deferred as _i3; import 'package:flutter_sharez/features/device_share/view/device_share_page.dart' - deferred as _i4; + deferred as _i5; import 'package:flutter_sharez/features/device_share/view/tabs/device_info_tab_page.dart' - deferred as _i3; + deferred as _i4; import 'package:flutter_sharez/features/device_share/view/tabs/sender_files_tab_page.dart' - deferred as _i15; + deferred as _i16; import 'package:flutter_sharez/features/downloads/view/downloads_page.dart' - deferred as _i5; -import 'package:flutter_sharez/features/file_selector/view/file_selector_page.dart' deferred as _i6; -import 'package:flutter_sharez/features/help_dialog/view/help_dialog_page.dart' +import 'package:flutter_sharez/features/file_selector/view/file_selector_page.dart' deferred as _i7; -import 'package:flutter_sharez/features/home/view/home_page.dart' +import 'package:flutter_sharez/features/help_dialog/view/help_dialog_page.dart' deferred as _i8; -import 'package:flutter_sharez/features/manual_connect/view/manual_connect_page.dart' +import 'package:flutter_sharez/features/home/view/home_page.dart' deferred as _i9; -import 'package:flutter_sharez/features/qr_scan/qr_scan_page.dart' +import 'package:flutter_sharez/features/manual_connect/view/manual_connect_page.dart' deferred as _i10; -import 'package:flutter_sharez/features/receive/view/receive_page.dart' +import 'package:flutter_sharez/features/qr_scan/qr_scan_page.dart' deferred as _i11; -import 'package:flutter_sharez/features/receive/view/receive_state_page.dart' +import 'package:flutter_sharez/features/receive/view/receive_page.dart' deferred as _i12; -import 'package:flutter_sharez/features/send/view/send_page.dart' +import 'package:flutter_sharez/features/receive/view/receive_state_page.dart' deferred as _i13; -import 'package:flutter_sharez/features/send/view/send_state_page.dart' +import 'package:flutter_sharez/features/send/view/send_page.dart' deferred as _i14; +import 'package:flutter_sharez/features/send/view/send_state_page.dart' + deferred as _i15; import 'package:flutter_sharez/features/settings/view/settings_page.dart' - deferred as _i16; + deferred as _i17; +import 'package:flutter_sharez/features/update_app_version/view/changelog_page.dart' + deferred as _i1; -abstract class $AppRouter extends _i17.RootStackRouter { +abstract class $AppRouter extends _i18.RootStackRouter { $AppRouter({super.navigatorKey}); @override - final Map pagesMap = { + final Map pagesMap = { + ChangelogRoute.name: (routeData) { + final args = routeData.argsAs(); + return _i18.AutoRoutePage( + routeData: routeData, + child: _i18.DeferredWidget( + _i1.loadLibrary, + () => _i1.ChangelogPage( + key: args.key, + updateModel: args.updateModel, + ), + ), + ); + }, ConfirmConnectionDialogRoute.name: (routeData) { final args = routeData.argsAs(); - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i1.loadLibrary, - () => _i1.ConfirmConnectionDialogPage( + child: _i18.DeferredWidget( + _i2.loadLibrary, + () => _i2.ConfirmConnectionDialogPage( key: args.key, receiverModel: args.receiverModel, onCofirmation: args.onCofirmation, @@ -65,21 +81,21 @@ abstract class $AppRouter extends _i17.RootStackRouter { ); }, CounterRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i2.loadLibrary, - () => _i2.CounterPage(), + child: _i18.DeferredWidget( + _i3.loadLibrary, + () => _i3.CounterPage(), ), ); }, DeviceInfoTabRoute.name: (routeData) { final args = routeData.argsAs(); - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i3.loadLibrary, - () => _i3.DeviceInfoTabPage( + child: _i18.DeferredWidget( + _i4.loadLibrary, + () => _i4.DeviceInfoTabPage( key: args.key, senderModel: args.senderModel, ), @@ -88,11 +104,11 @@ abstract class $AppRouter extends _i17.RootStackRouter { }, DeviceShareRoute.name: (routeData) { final args = routeData.argsAs(); - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i4.loadLibrary, - () => _i4.DeviceSharePage( + child: _i18.DeferredWidget( + _i5.loadLibrary, + () => _i5.DeviceSharePage( key: args.key, senderModel: args.senderModel, ), @@ -100,102 +116,102 @@ abstract class $AppRouter extends _i17.RootStackRouter { ); }, DownloadsRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i5.loadLibrary, - () => _i5.DownloadsPage(), + child: _i18.DeferredWidget( + _i6.loadLibrary, + () => _i6.DownloadsPage(), ), ); }, FileSelectorRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i6.loadLibrary, - () => _i6.FileSelectorPage(), + child: _i18.DeferredWidget( + _i7.loadLibrary, + () => _i7.FileSelectorPage(), ), ); }, HelpDialogRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i7.loadLibrary, - () => _i7.HelpDialogPage(), + child: _i18.DeferredWidget( + _i8.loadLibrary, + () => _i8.HelpDialogPage(), ), ); }, HomeRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i8.loadLibrary, - () => _i8.HomePage(), + child: _i18.DeferredWidget( + _i9.loadLibrary, + () => _i9.HomePage(), ), ); }, ManualConnectRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i9.loadLibrary, - () => _i9.ManualConnectPage(), + child: _i18.DeferredWidget( + _i10.loadLibrary, + () => _i10.ManualConnectPage(), ), ); }, QrScanRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i10.loadLibrary, - () => _i10.QrScanPage(), + child: _i18.DeferredWidget( + _i11.loadLibrary, + () => _i11.QrScanPage(), ), ); }, ReceiveRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i11.loadLibrary, - () => _i11.ReceivePage(), + child: _i18.DeferredWidget( + _i12.loadLibrary, + () => _i12.ReceivePage(), ), ); }, ReceiveStateRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i12.loadLibrary, - () => _i12.ReceiveStatePage(), + child: _i18.DeferredWidget( + _i13.loadLibrary, + () => _i13.ReceiveStatePage(), ), ); }, SendRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i13.loadLibrary, - () => _i13.SendPage(), + child: _i18.DeferredWidget( + _i14.loadLibrary, + () => _i14.SendPage(), ), ); }, SendStateRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i14.loadLibrary, - () => _i14.SendStatePage(), + child: _i18.DeferredWidget( + _i15.loadLibrary, + () => _i15.SendStatePage(), ), ); }, SenderFilesTabRoute.name: (routeData) { final args = routeData.argsAs(); - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i15.loadLibrary, - () => _i15.SenderFilesTabPage( + child: _i18.DeferredWidget( + _i16.loadLibrary, + () => _i16.SenderFilesTabPage( key: args.key, senderModel: args.senderModel, ), @@ -203,11 +219,11 @@ abstract class $AppRouter extends _i17.RootStackRouter { ); }, SettingsRoute.name: (routeData) { - return _i17.AutoRoutePage( + return _i18.AutoRoutePage( routeData: routeData, - child: _i17.DeferredWidget( - _i16.loadLibrary, - () => _i16.SettingsPage(), + child: _i18.DeferredWidget( + _i17.loadLibrary, + () => _i17.SettingsPage(), ), ); }, @@ -215,14 +231,52 @@ abstract class $AppRouter extends _i17.RootStackRouter { } /// generated route for -/// [_i1.ConfirmConnectionDialogPage] +/// [_i1.ChangelogPage] +class ChangelogRoute extends _i18.PageRouteInfo { + ChangelogRoute({ + _i19.Key? key, + required _i20.UpdateModel? updateModel, + List<_i18.PageRouteInfo>? children, + }) : super( + ChangelogRoute.name, + args: ChangelogRouteArgs( + key: key, + updateModel: updateModel, + ), + initialChildren: children, + ); + + static const String name = 'ChangelogRoute'; + + static const _i18.PageInfo page = + _i18.PageInfo(name); +} + +class ChangelogRouteArgs { + const ChangelogRouteArgs({ + this.key, + required this.updateModel, + }); + + final _i19.Key? key; + + final _i20.UpdateModel? updateModel; + + @override + String toString() { + return 'ChangelogRouteArgs{key: $key, updateModel: $updateModel}'; + } +} + +/// generated route for +/// [_i2.ConfirmConnectionDialogPage] class ConfirmConnectionDialogRoute - extends _i17.PageRouteInfo { + extends _i18.PageRouteInfo { ConfirmConnectionDialogRoute({ - _i18.Key? key, - required _i19.ReceiverModel receiverModel, + _i19.Key? key, + required _i21.ReceiverModel receiverModel, required void Function(bool) onCofirmation, - List<_i17.PageRouteInfo>? children, + List<_i18.PageRouteInfo>? children, }) : super( ConfirmConnectionDialogRoute.name, args: ConfirmConnectionDialogRouteArgs( @@ -235,8 +289,8 @@ class ConfirmConnectionDialogRoute static const String name = 'ConfirmConnectionDialogRoute'; - static const _i17.PageInfo page = - _i17.PageInfo(name); + static const _i18.PageInfo page = + _i18.PageInfo(name); } class ConfirmConnectionDialogRouteArgs { @@ -246,9 +300,9 @@ class ConfirmConnectionDialogRouteArgs { required this.onCofirmation, }); - final _i18.Key? key; + final _i19.Key? key; - final _i19.ReceiverModel receiverModel; + final _i21.ReceiverModel receiverModel; final void Function(bool) onCofirmation; @@ -259,9 +313,9 @@ class ConfirmConnectionDialogRouteArgs { } /// generated route for -/// [_i2.CounterPage] -class CounterRoute extends _i17.PageRouteInfo { - const CounterRoute({List<_i17.PageRouteInfo>? children}) +/// [_i3.CounterPage] +class CounterRoute extends _i18.PageRouteInfo { + const CounterRoute({List<_i18.PageRouteInfo>? children}) : super( CounterRoute.name, initialChildren: children, @@ -269,16 +323,16 @@ class CounterRoute extends _i17.PageRouteInfo { static const String name = 'CounterRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i3.DeviceInfoTabPage] -class DeviceInfoTabRoute extends _i17.PageRouteInfo { +/// [_i4.DeviceInfoTabPage] +class DeviceInfoTabRoute extends _i18.PageRouteInfo { DeviceInfoTabRoute({ - _i18.Key? key, - required _i20.SenderModel senderModel, - List<_i17.PageRouteInfo>? children, + _i19.Key? key, + required _i22.SenderModel senderModel, + List<_i18.PageRouteInfo>? children, }) : super( DeviceInfoTabRoute.name, args: DeviceInfoTabRouteArgs( @@ -290,8 +344,8 @@ class DeviceInfoTabRoute extends _i17.PageRouteInfo { static const String name = 'DeviceInfoTabRoute'; - static const _i17.PageInfo page = - _i17.PageInfo(name); + static const _i18.PageInfo page = + _i18.PageInfo(name); } class DeviceInfoTabRouteArgs { @@ -300,9 +354,9 @@ class DeviceInfoTabRouteArgs { required this.senderModel, }); - final _i18.Key? key; + final _i19.Key? key; - final _i20.SenderModel senderModel; + final _i22.SenderModel senderModel; @override String toString() { @@ -311,12 +365,12 @@ class DeviceInfoTabRouteArgs { } /// generated route for -/// [_i4.DeviceSharePage] -class DeviceShareRoute extends _i17.PageRouteInfo { +/// [_i5.DeviceSharePage] +class DeviceShareRoute extends _i18.PageRouteInfo { DeviceShareRoute({ - _i18.Key? key, - required _i20.SenderModel senderModel, - List<_i17.PageRouteInfo>? children, + _i19.Key? key, + required _i22.SenderModel senderModel, + List<_i18.PageRouteInfo>? children, }) : super( DeviceShareRoute.name, args: DeviceShareRouteArgs( @@ -328,8 +382,8 @@ class DeviceShareRoute extends _i17.PageRouteInfo { static const String name = 'DeviceShareRoute'; - static const _i17.PageInfo page = - _i17.PageInfo(name); + static const _i18.PageInfo page = + _i18.PageInfo(name); } class DeviceShareRouteArgs { @@ -338,9 +392,9 @@ class DeviceShareRouteArgs { required this.senderModel, }); - final _i18.Key? key; + final _i19.Key? key; - final _i20.SenderModel senderModel; + final _i22.SenderModel senderModel; @override String toString() { @@ -349,9 +403,9 @@ class DeviceShareRouteArgs { } /// generated route for -/// [_i5.DownloadsPage] -class DownloadsRoute extends _i17.PageRouteInfo { - const DownloadsRoute({List<_i17.PageRouteInfo>? children}) +/// [_i6.DownloadsPage] +class DownloadsRoute extends _i18.PageRouteInfo { + const DownloadsRoute({List<_i18.PageRouteInfo>? children}) : super( DownloadsRoute.name, initialChildren: children, @@ -359,13 +413,13 @@ class DownloadsRoute extends _i17.PageRouteInfo { static const String name = 'DownloadsRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i6.FileSelectorPage] -class FileSelectorRoute extends _i17.PageRouteInfo { - const FileSelectorRoute({List<_i17.PageRouteInfo>? children}) +/// [_i7.FileSelectorPage] +class FileSelectorRoute extends _i18.PageRouteInfo { + const FileSelectorRoute({List<_i18.PageRouteInfo>? children}) : super( FileSelectorRoute.name, initialChildren: children, @@ -373,13 +427,13 @@ class FileSelectorRoute extends _i17.PageRouteInfo { static const String name = 'FileSelectorRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i7.HelpDialogPage] -class HelpDialogRoute extends _i17.PageRouteInfo { - const HelpDialogRoute({List<_i17.PageRouteInfo>? children}) +/// [_i8.HelpDialogPage] +class HelpDialogRoute extends _i18.PageRouteInfo { + const HelpDialogRoute({List<_i18.PageRouteInfo>? children}) : super( HelpDialogRoute.name, initialChildren: children, @@ -387,13 +441,13 @@ class HelpDialogRoute extends _i17.PageRouteInfo { static const String name = 'HelpDialogRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i8.HomePage] -class HomeRoute extends _i17.PageRouteInfo { - const HomeRoute({List<_i17.PageRouteInfo>? children}) +/// [_i9.HomePage] +class HomeRoute extends _i18.PageRouteInfo { + const HomeRoute({List<_i18.PageRouteInfo>? children}) : super( HomeRoute.name, initialChildren: children, @@ -401,13 +455,13 @@ class HomeRoute extends _i17.PageRouteInfo { static const String name = 'HomeRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i9.ManualConnectPage] -class ManualConnectRoute extends _i17.PageRouteInfo { - const ManualConnectRoute({List<_i17.PageRouteInfo>? children}) +/// [_i10.ManualConnectPage] +class ManualConnectRoute extends _i18.PageRouteInfo { + const ManualConnectRoute({List<_i18.PageRouteInfo>? children}) : super( ManualConnectRoute.name, initialChildren: children, @@ -415,13 +469,13 @@ class ManualConnectRoute extends _i17.PageRouteInfo { static const String name = 'ManualConnectRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i10.QrScanPage] -class QrScanRoute extends _i17.PageRouteInfo { - const QrScanRoute({List<_i17.PageRouteInfo>? children}) +/// [_i11.QrScanPage] +class QrScanRoute extends _i18.PageRouteInfo { + const QrScanRoute({List<_i18.PageRouteInfo>? children}) : super( QrScanRoute.name, initialChildren: children, @@ -429,13 +483,13 @@ class QrScanRoute extends _i17.PageRouteInfo { static const String name = 'QrScanRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i11.ReceivePage] -class ReceiveRoute extends _i17.PageRouteInfo { - const ReceiveRoute({List<_i17.PageRouteInfo>? children}) +/// [_i12.ReceivePage] +class ReceiveRoute extends _i18.PageRouteInfo { + const ReceiveRoute({List<_i18.PageRouteInfo>? children}) : super( ReceiveRoute.name, initialChildren: children, @@ -443,13 +497,13 @@ class ReceiveRoute extends _i17.PageRouteInfo { static const String name = 'ReceiveRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i12.ReceiveStatePage] -class ReceiveStateRoute extends _i17.PageRouteInfo { - const ReceiveStateRoute({List<_i17.PageRouteInfo>? children}) +/// [_i13.ReceiveStatePage] +class ReceiveStateRoute extends _i18.PageRouteInfo { + const ReceiveStateRoute({List<_i18.PageRouteInfo>? children}) : super( ReceiveStateRoute.name, initialChildren: children, @@ -457,13 +511,13 @@ class ReceiveStateRoute extends _i17.PageRouteInfo { static const String name = 'ReceiveStateRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i13.SendPage] -class SendRoute extends _i17.PageRouteInfo { - const SendRoute({List<_i17.PageRouteInfo>? children}) +/// [_i14.SendPage] +class SendRoute extends _i18.PageRouteInfo { + const SendRoute({List<_i18.PageRouteInfo>? children}) : super( SendRoute.name, initialChildren: children, @@ -471,13 +525,13 @@ class SendRoute extends _i17.PageRouteInfo { static const String name = 'SendRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i14.SendStatePage] -class SendStateRoute extends _i17.PageRouteInfo { - const SendStateRoute({List<_i17.PageRouteInfo>? children}) +/// [_i15.SendStatePage] +class SendStateRoute extends _i18.PageRouteInfo { + const SendStateRoute({List<_i18.PageRouteInfo>? children}) : super( SendStateRoute.name, initialChildren: children, @@ -485,16 +539,16 @@ class SendStateRoute extends _i17.PageRouteInfo { static const String name = 'SendStateRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } /// generated route for -/// [_i15.SenderFilesTabPage] -class SenderFilesTabRoute extends _i17.PageRouteInfo { +/// [_i16.SenderFilesTabPage] +class SenderFilesTabRoute extends _i18.PageRouteInfo { SenderFilesTabRoute({ - _i18.Key? key, - required _i20.SenderModel senderModel, - List<_i17.PageRouteInfo>? children, + _i19.Key? key, + required _i22.SenderModel senderModel, + List<_i18.PageRouteInfo>? children, }) : super( SenderFilesTabRoute.name, args: SenderFilesTabRouteArgs( @@ -506,8 +560,8 @@ class SenderFilesTabRoute extends _i17.PageRouteInfo { static const String name = 'SenderFilesTabRoute'; - static const _i17.PageInfo page = - _i17.PageInfo(name); + static const _i18.PageInfo page = + _i18.PageInfo(name); } class SenderFilesTabRouteArgs { @@ -516,9 +570,9 @@ class SenderFilesTabRouteArgs { required this.senderModel, }); - final _i18.Key? key; + final _i19.Key? key; - final _i20.SenderModel senderModel; + final _i22.SenderModel senderModel; @override String toString() { @@ -527,9 +581,9 @@ class SenderFilesTabRouteArgs { } /// generated route for -/// [_i16.SettingsPage] -class SettingsRoute extends _i17.PageRouteInfo { - const SettingsRoute({List<_i17.PageRouteInfo>? children}) +/// [_i17.SettingsPage] +class SettingsRoute extends _i18.PageRouteInfo { + const SettingsRoute({List<_i18.PageRouteInfo>? children}) : super( SettingsRoute.name, initialChildren: children, @@ -537,5 +591,5 @@ class SettingsRoute extends _i17.PageRouteInfo { static const String name = 'SettingsRoute'; - static const _i17.PageInfo page = _i17.PageInfo(name); + static const _i18.PageInfo page = _i18.PageInfo(name); } diff --git a/lib/data/model/update_model.dart b/lib/data/model/update_model.dart new file mode 100644 index 0000000..a4aaa5f --- /dev/null +++ b/lib/data/model/update_model.dart @@ -0,0 +1,716 @@ +// ignore_for_file: non_constant_identifier_names + +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; + +class UpdateModel { + final String url; + final String assets_url; + final String upload_url; + final String html_url; + final int id; + final Author author; + final String node_id; + final String tag_name; + final String target_commitish; + final String name; + final bool draft; + final bool prerelease; + final String created_at; + final String published_at; + final List assets; + final String tarball_url; + final String zipball_url; + final String body; + final int mentions_count; + UpdateModel({ + required this.url, + required this.assets_url, + required this.upload_url, + required this.html_url, + required this.id, + required this.author, + required this.node_id, + required this.tag_name, + required this.target_commitish, + required this.name, + required this.draft, + required this.prerelease, + required this.created_at, + required this.published_at, + required this.assets, + required this.tarball_url, + required this.zipball_url, + required this.body, + required this.mentions_count, + }); + + UpdateModel copyWith({ + String? url, + String? assets_url, + String? upload_url, + String? html_url, + int? id, + Author? author, + String? node_id, + String? tag_name, + String? target_commitish, + String? name, + bool? draft, + bool? prerelease, + String? created_at, + String? published_at, + List? assets, + String? tarball_url, + String? zipball_url, + String? body, + int? mentions_count, + }) { + return UpdateModel( + url: url ?? this.url, + assets_url: assets_url ?? this.assets_url, + upload_url: upload_url ?? this.upload_url, + html_url: html_url ?? this.html_url, + id: id ?? this.id, + author: author ?? this.author, + node_id: node_id ?? this.node_id, + tag_name: tag_name ?? this.tag_name, + target_commitish: target_commitish ?? this.target_commitish, + name: name ?? this.name, + draft: draft ?? this.draft, + prerelease: prerelease ?? this.prerelease, + created_at: created_at ?? this.created_at, + published_at: published_at ?? this.published_at, + assets: assets ?? this.assets, + tarball_url: tarball_url ?? this.tarball_url, + zipball_url: zipball_url ?? this.zipball_url, + body: body ?? this.body, + mentions_count: mentions_count ?? this.mentions_count, + ); + } + + Map toMap() { + return { + 'url': url, + 'assets_url': assets_url, + 'upload_url': upload_url, + 'html_url': html_url, + 'id': id, + 'author': author.toMap(), + 'node_id': node_id, + 'tag_name': tag_name, + 'target_commitish': target_commitish, + 'name': name, + 'draft': draft, + 'prerelease': prerelease, + 'created_at': created_at, + 'published_at': published_at, + 'assets': assets.map((x) => x.toMap()).toList(), + 'tarball_url': tarball_url, + 'zipball_url': zipball_url, + 'body': body, + 'mentions_count': mentions_count, + }; + } + + factory UpdateModel.fromMap(Map map) { + return UpdateModel( + url: map['url'] ?? '', + assets_url: map['assets_url'] ?? '', + upload_url: map['upload_url'] ?? '', + html_url: map['html_url'] ?? '', + id: map['id']?.toInt() ?? 0, + author: Author.fromMap(map['author']), + node_id: map['node_id'] ?? '', + tag_name: map['tag_name'] ?? '', + target_commitish: map['target_commitish'] ?? '', + name: map['name'] ?? '', + draft: map['draft'] ?? false, + prerelease: map['prerelease'] ?? false, + created_at: map['created_at'] ?? '', + published_at: map['published_at'] ?? '', + assets: List.from( + map['assets']?.map((x) => Asset.fromMap(x)) ?? const []), + tarball_url: map['tarball_url'] ?? '', + zipball_url: map['zipball_url'] ?? '', + body: map['body'] ?? '', + mentions_count: map['mentions_count']?.toInt() ?? 0, + ); + } + + String toJson() => json.encode(toMap()); + + factory UpdateModel.fromJson(String source) => + UpdateModel.fromMap(json.decode(source)); + + @override + String toString() { + return 'UpdateModel(url: $url, assets_url: $assets_url, upload_url: $upload_url, html_url: $html_url, id: $id, author: $author, node_id: $node_id, tag_name: $tag_name, target_commitish: $target_commitish, name: $name, draft: $draft, prerelease: $prerelease, created_at: $created_at, published_at: $published_at, assets: $assets, tarball_url: $tarball_url, zipball_url: $zipball_url, body: $body, mentions_count: $mentions_count)'; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is UpdateModel && + other.url == url && + other.assets_url == assets_url && + other.upload_url == upload_url && + other.html_url == html_url && + other.id == id && + other.author == author && + other.node_id == node_id && + other.tag_name == tag_name && + other.target_commitish == target_commitish && + other.name == name && + other.draft == draft && + other.prerelease == prerelease && + other.created_at == created_at && + other.published_at == published_at && + listEquals(other.assets, assets) && + other.tarball_url == tarball_url && + other.zipball_url == zipball_url && + other.body == body && + other.mentions_count == mentions_count; + } + + @override + int get hashCode { + return url.hashCode ^ + assets_url.hashCode ^ + upload_url.hashCode ^ + html_url.hashCode ^ + id.hashCode ^ + author.hashCode ^ + node_id.hashCode ^ + tag_name.hashCode ^ + target_commitish.hashCode ^ + name.hashCode ^ + draft.hashCode ^ + prerelease.hashCode ^ + created_at.hashCode ^ + published_at.hashCode ^ + assets.hashCode ^ + tarball_url.hashCode ^ + zipball_url.hashCode ^ + body.hashCode ^ + mentions_count.hashCode; + } +} + +class Author { + final String login; + final int id; + final String node_id; + final String avatar_url; + final String gravatar_id; + final String url; + final String html_url; + final String followers_url; + final String following_url; + final String gists_url; + final String starred_url; + final String subscriptions_url; + final String organizations_url; + final String repos_url; + final String events_url; + final String received_events_url; + final String type; + final bool site_admin; + Author({ + required this.login, + required this.id, + required this.node_id, + required this.avatar_url, + required this.gravatar_id, + required this.url, + required this.html_url, + required this.followers_url, + required this.following_url, + required this.gists_url, + required this.starred_url, + required this.subscriptions_url, + required this.organizations_url, + required this.repos_url, + required this.events_url, + required this.received_events_url, + required this.type, + required this.site_admin, + }); + + Author copyWith({ + String? login, + int? id, + String? node_id, + String? avatar_url, + String? gravatar_id, + String? url, + String? html_url, + String? followers_url, + String? following_url, + String? gists_url, + String? starred_url, + String? subscriptions_url, + String? organizations_url, + String? repos_url, + String? events_url, + String? received_events_url, + String? type, + bool? site_admin, + }) { + return Author( + login: login ?? this.login, + id: id ?? this.id, + node_id: node_id ?? this.node_id, + avatar_url: avatar_url ?? this.avatar_url, + gravatar_id: gravatar_id ?? this.gravatar_id, + url: url ?? this.url, + html_url: html_url ?? this.html_url, + followers_url: followers_url ?? this.followers_url, + following_url: following_url ?? this.following_url, + gists_url: gists_url ?? this.gists_url, + starred_url: starred_url ?? this.starred_url, + subscriptions_url: subscriptions_url ?? this.subscriptions_url, + organizations_url: organizations_url ?? this.organizations_url, + repos_url: repos_url ?? this.repos_url, + events_url: events_url ?? this.events_url, + received_events_url: received_events_url ?? this.received_events_url, + type: type ?? this.type, + site_admin: site_admin ?? this.site_admin, + ); + } + + Map toMap() { + return { + 'login': login, + 'id': id, + 'node_id': node_id, + 'avatar_url': avatar_url, + 'gravatar_id': gravatar_id, + 'url': url, + 'html_url': html_url, + 'followers_url': followers_url, + 'following_url': following_url, + 'gists_url': gists_url, + 'starred_url': starred_url, + 'subscriptions_url': subscriptions_url, + 'organizations_url': organizations_url, + 'repos_url': repos_url, + 'events_url': events_url, + 'received_events_url': received_events_url, + 'type': type, + 'site_admin': site_admin, + }; + } + + factory Author.fromMap(Map map) { + return Author( + login: map['login'] ?? '', + id: map['id']?.toInt() ?? 0, + node_id: map['node_id'] ?? '', + avatar_url: map['avatar_url'] ?? '', + gravatar_id: map['gravatar_id'] ?? '', + url: map['url'] ?? '', + html_url: map['html_url'] ?? '', + followers_url: map['followers_url'] ?? '', + following_url: map['following_url'] ?? '', + gists_url: map['gists_url'] ?? '', + starred_url: map['starred_url'] ?? '', + subscriptions_url: map['subscriptions_url'] ?? '', + organizations_url: map['organizations_url'] ?? '', + repos_url: map['repos_url'] ?? '', + events_url: map['events_url'] ?? '', + received_events_url: map['received_events_url'] ?? '', + type: map['type'] ?? '', + site_admin: map['site_admin'] ?? false, + ); + } + + String toJson() => json.encode(toMap()); + + factory Author.fromJson(String source) => Author.fromMap(json.decode(source)); + + @override + String toString() { + return 'Author(login: $login, id: $id, node_id: $node_id, avatar_url: $avatar_url, gravatar_id: $gravatar_id, url: $url, html_url: $html_url, followers_url: $followers_url, following_url: $following_url, gists_url: $gists_url, starred_url: $starred_url, subscriptions_url: $subscriptions_url, organizations_url: $organizations_url, repos_url: $repos_url, events_url: $events_url, received_events_url: $received_events_url, type: $type, site_admin: $site_admin)'; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Author && + other.login == login && + other.id == id && + other.node_id == node_id && + other.avatar_url == avatar_url && + other.gravatar_id == gravatar_id && + other.url == url && + other.html_url == html_url && + other.followers_url == followers_url && + other.following_url == following_url && + other.gists_url == gists_url && + other.starred_url == starred_url && + other.subscriptions_url == subscriptions_url && + other.organizations_url == organizations_url && + other.repos_url == repos_url && + other.events_url == events_url && + other.received_events_url == received_events_url && + other.type == type && + other.site_admin == site_admin; + } + + @override + int get hashCode { + return login.hashCode ^ + id.hashCode ^ + node_id.hashCode ^ + avatar_url.hashCode ^ + gravatar_id.hashCode ^ + url.hashCode ^ + html_url.hashCode ^ + followers_url.hashCode ^ + following_url.hashCode ^ + gists_url.hashCode ^ + starred_url.hashCode ^ + subscriptions_url.hashCode ^ + organizations_url.hashCode ^ + repos_url.hashCode ^ + events_url.hashCode ^ + received_events_url.hashCode ^ + type.hashCode ^ + site_admin.hashCode; + } +} + +class Asset { + final String url; + final int id; + final String node_id; + final String name; + final String label; + final Uploader uploader; + final String content_type; + final String state; + final int size; + final int download_count; + final String created_at; + final String updated_at; + final String browser_download_url; + Asset({ + required this.url, + required this.id, + required this.node_id, + required this.name, + required this.label, + required this.uploader, + required this.content_type, + required this.state, + required this.size, + required this.download_count, + required this.created_at, + required this.updated_at, + required this.browser_download_url, + }); + + Asset copyWith({ + String? url, + int? id, + String? node_id, + String? name, + String? label, + Uploader? uploader, + String? content_type, + String? state, + int? size, + int? download_count, + String? created_at, + String? updated_at, + String? browser_download_url, + }) { + return Asset( + url: url ?? this.url, + id: id ?? this.id, + node_id: node_id ?? this.node_id, + name: name ?? this.name, + label: label ?? this.label, + uploader: uploader ?? this.uploader, + content_type: content_type ?? this.content_type, + state: state ?? this.state, + size: size ?? this.size, + download_count: download_count ?? this.download_count, + created_at: created_at ?? this.created_at, + updated_at: updated_at ?? this.updated_at, + browser_download_url: browser_download_url ?? this.browser_download_url, + ); + } + + Map toMap() { + return { + 'url': url, + 'id': id, + 'node_id': node_id, + 'name': name, + 'label': label, + 'uploader': uploader.toMap(), + 'content_type': content_type, + 'state': state, + 'size': size, + 'download_count': download_count, + 'created_at': created_at, + 'updated_at': updated_at, + 'browser_download_url': browser_download_url, + }; + } + + factory Asset.fromMap(Map map) { + return Asset( + url: map['url'] ?? '', + id: map['id']?.toInt() ?? 0, + node_id: map['node_id'] ?? '', + name: map['name'] ?? '', + label: map['label'] ?? '', + uploader: Uploader.fromMap(map['uploader']), + content_type: map['content_type'] ?? '', + state: map['state'] ?? '', + size: map['size']?.toInt() ?? 0, + download_count: map['download_count']?.toInt() ?? 0, + created_at: map['created_at'] ?? '', + updated_at: map['updated_at'] ?? '', + browser_download_url: map['browser_download_url'] ?? '', + ); + } + + String toJson() => json.encode(toMap()); + + factory Asset.fromJson(String source) => Asset.fromMap(json.decode(source)); + + @override + String toString() { + return 'Asset(url: $url, id: $id, node_id: $node_id, name: $name, label: $label, uploader: $uploader, content_type: $content_type, state: $state, size: $size, download_count: $download_count, created_at: $created_at, updated_at: $updated_at, browser_download_url: $browser_download_url)'; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Asset && + other.url == url && + other.id == id && + other.node_id == node_id && + other.name == name && + other.label == label && + other.uploader == uploader && + other.content_type == content_type && + other.state == state && + other.size == size && + other.download_count == download_count && + other.created_at == created_at && + other.updated_at == updated_at && + other.browser_download_url == browser_download_url; + } + + @override + int get hashCode { + return url.hashCode ^ + id.hashCode ^ + node_id.hashCode ^ + name.hashCode ^ + label.hashCode ^ + uploader.hashCode ^ + content_type.hashCode ^ + state.hashCode ^ + size.hashCode ^ + download_count.hashCode ^ + created_at.hashCode ^ + updated_at.hashCode ^ + browser_download_url.hashCode; + } +} + +class Uploader { + final String login; + final int id; + final String node_id; + final String avatar_url; + final String gravatar_id; + final String url; + final String html_url; + final String followers_url; + final String following_url; + final String gists_url; + final String starred_url; + final String subscriptions_url; + final String organizations_url; + final String repos_url; + final String events_url; + final String received_events_url; + final String type; + final bool site_admin; + Uploader({ + required this.login, + required this.id, + required this.node_id, + required this.avatar_url, + required this.gravatar_id, + required this.url, + required this.html_url, + required this.followers_url, + required this.following_url, + required this.gists_url, + required this.starred_url, + required this.subscriptions_url, + required this.organizations_url, + required this.repos_url, + required this.events_url, + required this.received_events_url, + required this.type, + required this.site_admin, + }); + + Uploader copyWith({ + String? login, + int? id, + String? node_id, + String? avatar_url, + String? gravatar_id, + String? url, + String? html_url, + String? followers_url, + String? following_url, + String? gists_url, + String? starred_url, + String? subscriptions_url, + String? organizations_url, + String? repos_url, + String? events_url, + String? received_events_url, + String? type, + bool? site_admin, + }) { + return Uploader( + login: login ?? this.login, + id: id ?? this.id, + node_id: node_id ?? this.node_id, + avatar_url: avatar_url ?? this.avatar_url, + gravatar_id: gravatar_id ?? this.gravatar_id, + url: url ?? this.url, + html_url: html_url ?? this.html_url, + followers_url: followers_url ?? this.followers_url, + following_url: following_url ?? this.following_url, + gists_url: gists_url ?? this.gists_url, + starred_url: starred_url ?? this.starred_url, + subscriptions_url: subscriptions_url ?? this.subscriptions_url, + organizations_url: organizations_url ?? this.organizations_url, + repos_url: repos_url ?? this.repos_url, + events_url: events_url ?? this.events_url, + received_events_url: received_events_url ?? this.received_events_url, + type: type ?? this.type, + site_admin: site_admin ?? this.site_admin, + ); + } + + Map toMap() { + return { + 'login': login, + 'id': id, + 'node_id': node_id, + 'avatar_url': avatar_url, + 'gravatar_id': gravatar_id, + 'url': url, + 'html_url': html_url, + 'followers_url': followers_url, + 'following_url': following_url, + 'gists_url': gists_url, + 'starred_url': starred_url, + 'subscriptions_url': subscriptions_url, + 'organizations_url': organizations_url, + 'repos_url': repos_url, + 'events_url': events_url, + 'received_events_url': received_events_url, + 'type': type, + 'site_admin': site_admin, + }; + } + + factory Uploader.fromMap(Map map) { + return Uploader( + login: map['login'] ?? '', + id: map['id']?.toInt() ?? 0, + node_id: map['node_id'] ?? '', + avatar_url: map['avatar_url'] ?? '', + gravatar_id: map['gravatar_id'] ?? '', + url: map['url'] ?? '', + html_url: map['html_url'] ?? '', + followers_url: map['followers_url'] ?? '', + following_url: map['following_url'] ?? '', + gists_url: map['gists_url'] ?? '', + starred_url: map['starred_url'] ?? '', + subscriptions_url: map['subscriptions_url'] ?? '', + organizations_url: map['organizations_url'] ?? '', + repos_url: map['repos_url'] ?? '', + events_url: map['events_url'] ?? '', + received_events_url: map['received_events_url'] ?? '', + type: map['type'] ?? '', + site_admin: map['site_admin'] ?? false, + ); + } + + String toJson() => json.encode(toMap()); + + factory Uploader.fromJson(String source) => + Uploader.fromMap(json.decode(source)); + + @override + String toString() { + return 'Uploader(login: $login, id: $id, node_id: $node_id, avatar_url: $avatar_url, gravatar_id: $gravatar_id, url: $url, html_url: $html_url, followers_url: $followers_url, following_url: $following_url, gists_url: $gists_url, starred_url: $starred_url, subscriptions_url: $subscriptions_url, organizations_url: $organizations_url, repos_url: $repos_url, events_url: $events_url, received_events_url: $received_events_url, type: $type, site_admin: $site_admin)'; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Uploader && + other.login == login && + other.id == id && + other.node_id == node_id && + other.avatar_url == avatar_url && + other.gravatar_id == gravatar_id && + other.url == url && + other.html_url == html_url && + other.followers_url == followers_url && + other.following_url == following_url && + other.gists_url == gists_url && + other.starred_url == starred_url && + other.subscriptions_url == subscriptions_url && + other.organizations_url == organizations_url && + other.repos_url == repos_url && + other.events_url == events_url && + other.received_events_url == received_events_url && + other.type == type && + other.site_admin == site_admin; + } + + @override + int get hashCode { + return login.hashCode ^ + id.hashCode ^ + node_id.hashCode ^ + avatar_url.hashCode ^ + gravatar_id.hashCode ^ + url.hashCode ^ + html_url.hashCode ^ + followers_url.hashCode ^ + following_url.hashCode ^ + gists_url.hashCode ^ + starred_url.hashCode ^ + subscriptions_url.hashCode ^ + organizations_url.hashCode ^ + repos_url.hashCode ^ + events_url.hashCode ^ + received_events_url.hashCode ^ + type.hashCode ^ + site_admin.hashCode; + } +} diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index e297dec..cede1dd 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -1,15 +1,28 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_sharez/core/router/router.gr.dart'; +import 'package:flutter_sharez/core/router/router_pod.dart'; +import 'package:flutter_sharez/features/update_app_version/controller/check_update_available.dart'; @RoutePage( deferredLoading: true, ) -class HomePage extends StatelessWidget { +class HomePage extends ConsumerWidget { const HomePage({Key? key}) : super(key: key); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + ref.listen( + checkUpdateAvailablePod, + (previous, next) { + if (next.value != null) { + ref + .read(autorouterProvider) + .navigate(ChangelogRoute(updateModel: next.value)); + } + }, + ); return AutoTabsScaffold( routes: const [ SendRoute(), diff --git a/lib/features/settings/view/widget/about_app_tile.dart b/lib/features/settings/view/widget/about_app_tile.dart index 465d7f9..f867bdc 100644 --- a/lib/features/settings/view/widget/about_app_tile.dart +++ b/lib/features/settings/view/widget/about_app_tile.dart @@ -4,6 +4,7 @@ import 'package:flutter_sharez/bootstrap.dart'; import 'package:flutter_sharez/core/router/router.gr.dart'; import 'package:flutter_sharez/core/router/router_pod.dart'; import 'package:flutter_sharez/features/settings/controller/current_version_pod.dart'; +import 'package:flutter_sharez/features/update_app_version/view/update_app_version_icon.dart'; import 'package:flutter_sharez/shared/riverpod_ext/asynvalue_easy_when.dart'; import 'package:velocity_x/velocity_x.dart'; @@ -74,6 +75,7 @@ class AboutTile extends StatelessWidget { ); }, ), + const UpdateAppVersionIcon() ].hStack(axisSize: MainAxisSize.min), iconColor: Colors.blue, ); diff --git a/lib/features/update_app_version/controller/check_update_available.dart b/lib/features/update_app_version/controller/check_update_available.dart new file mode 100644 index 0000000..6f880d9 --- /dev/null +++ b/lib/features/update_app_version/controller/check_update_available.dart @@ -0,0 +1,26 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_sharez/data/model/update_model.dart'; +import 'package:flutter_sharez/features/settings/controller/current_version_pod.dart'; +import 'package:flutter_sharez/features/update_app_version/controller/check_update_version.dart'; +import 'package:version/version.dart'; + +final checkUpdateAvailablePod = FutureProvider.autoDispose( + (ref) async { + final updateModel = await ref.watch(getUpdateModelPod.future); + final currentAppVersion = + (await ref.watch(currentVersionPod.future))!.replaceFirst('v', ''); + final tagname = updateModel.name.replaceFirst('v', ''); + Version currentVersion = Version.parse(currentAppVersion); + + Version latestVersion = Version.parse(tagname); + + /// updatemodel when update available + + if (latestVersion > currentVersion) { + return updateModel; + } else { + return null; + } + }, + name: 'checkUpdateAvailablePod', +); diff --git a/lib/features/update_app_version/controller/check_update_version.dart b/lib/features/update_app_version/controller/check_update_version.dart new file mode 100644 index 0000000..461a55a --- /dev/null +++ b/lib/features/update_app_version/controller/check_update_version.dart @@ -0,0 +1,17 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_sharez/data/model/update_model.dart'; +import 'package:flutter_sharez/shared/api_client/dio/dio_client_provider.dart'; +import 'package:flutter_sharez/shared/riverpod_ext/cancel_extensions.dart'; + +final getUpdateModelPod = FutureProvider.autoDispose( + (ref) async { + final dio = ref.watch(dioProvider( + "https://api.github.com/repos/Shreemanarjun/flutter_sharez")); + final result = await dio.get( + '/releases/latest', + cancelToken: ref.cancelToken(), + ); + return UpdateModel.fromMap(result.data); + }, + name: 'getUpdateModelPod', +); diff --git a/lib/features/update_app_version/controller/get_changelog_pod.dart b/lib/features/update_app_version/controller/get_changelog_pod.dart new file mode 100644 index 0000000..ac94b7e --- /dev/null +++ b/lib/features/update_app_version/controller/get_changelog_pod.dart @@ -0,0 +1,20 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_sharez/shared/api_client/dio/dio_client_provider.dart'; +import 'package:flutter_sharez/shared/riverpod_ext/cancel_extensions.dart'; + +final getChangeLogPod = FutureProvider.autoDispose( + (ref) async { + final dio = ref.watch(dioProvider( + "https://raw.githubusercontent.com/Shreemanarjun/flutter_sharez/main")); + final result = await dio.get( + '/CHANGELOG.md', + cancelToken: ref.cancelToken(), + ); + if (result.statusCode == 200) { + return result.data.toString(); + } else { + throw "Error getting changelog"; + } + }, + name: 'getChangeLogPod', +); diff --git a/lib/features/update_app_version/view/changelog_page.dart b/lib/features/update_app_version/view/changelog_page.dart new file mode 100644 index 0000000..5f456f5 --- /dev/null +++ b/lib/features/update_app_version/view/changelog_page.dart @@ -0,0 +1,99 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_sharez/bootstrap.dart'; +import 'package:flutter_sharez/data/model/update_model.dart'; +import 'package:flutter_sharez/features/update_app_version/controller/get_changelog_pod.dart'; +import 'package:flutter_sharez/shared/riverpod_ext/asynvalue_easy_when.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:velocity_x/velocity_x.dart'; + +@RoutePage( + deferredLoading: true, +) +class ChangelogPage extends StatefulWidget { + final UpdateModel? updateModel; + const ChangelogPage({Key? key, required this.updateModel}) : super(key: key); + + @override + State createState() => _ChangelogPageState(); +} + +class _ChangelogPageState extends State { + void downloadApp(final assetURl) async { + final uri = Uri.parse(assetURl); + if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) { + throw Exception('Could not launch $uri'); + } + } + + @override + Widget build(BuildContext context) { + return Dialog( + child: Consumer( + builder: (context, ref, child) { + final changelogs = ref.watch(getChangeLogPod); + return changelogs.easyWhen( + data: (data) => [ + if (widget.updateModel != null) + "A new update available \nšŸŽ‰ ${widget.updateModel?.name}" + .text + .bold + .xl2 + .amber500 + .center + .make() + .p12() + else + "No updates available for now !" + .text + .bold + .xl2 + .center + .amber500 + .make() + .p12(), + if (widget.updateModel != null) + [ + FilledButton( + onPressed: () { + talker.debug(widget.updateModel); + downloadApp(widget.updateModel?.html_url.toString()); + }, + child: "Download".text.make(), + ), + FilledButton( + onPressed: () { + context.back(); + }, + child: "Cancel".text.make(), + ), + ] + .hStack( + alignment: MainAxisAlignment.spaceAround, + ) + .p16() + else + FilledButton( + onPressed: () { + context.back(); + }, + child: "Cancel".text.make(), + ), + "Changelogs".text.bold.xl3.make(), + Markdown( + data: data, + physics: const ClampingScrollPhysics(), + ).expand(), + ] + .vStack( + axisSize: MainAxisSize.min, + ) + .p12(), + ); + }, + ), + ); + } +} diff --git a/lib/features/update_app_version/view/update_app_version_icon.dart b/lib/features/update_app_version/view/update_app_version_icon.dart new file mode 100644 index 0000000..671c87f --- /dev/null +++ b/lib/features/update_app_version/view/update_app_version_icon.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_sharez/core/router/router.gr.dart'; +import 'package:flutter_sharez/core/router/router_pod.dart'; +import 'package:flutter_sharez/features/update_app_version/controller/check_update_available.dart'; +import 'package:flutter_sharez/features/update_app_version/view/widgets/animated_sync.dart'; +import 'package:flutter_sharez/shared/riverpod_ext/asynvalue_easy_when.dart'; + +class UpdateAppVersionIcon extends ConsumerWidget { + const UpdateAppVersionIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final updateCheckAsync = ref.watch(checkUpdateAvailablePod); + return updateCheckAsync.easyWhen( + data: (updatemodel) { + return IconButton( + onPressed: () { + ref + .read(autorouterProvider) + .navigate(ChangelogRoute(updateModel: updatemodel)); + }, + icon: Icon( + Icons.sync, + color: updatemodel != null ? Colors.green : null, + ), + ); + }, + errorWidget: (error, stackTrace) => IconButton( + onPressed: () { + ref + .read(autorouterProvider) + .navigate(ChangelogRoute(updateModel: null)); + }, + icon: const Icon( + Icons.sync, + color: Colors.red, + )), + loadingWidget: () => const SpinningIconButton( + iconData: Icons.sync, + ), + ); + } +} diff --git a/lib/features/update_app_version/view/widgets/animated_sync.dart b/lib/features/update_app_version/view/widgets/animated_sync.dart new file mode 100644 index 0000000..a187f4f --- /dev/null +++ b/lib/features/update_app_version/view/widgets/animated_sync.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; + +class SpinningIconButton extends StatefulWidget { + final IconData iconData; + + const SpinningIconButton({ + Key? key, + required this.iconData, + }) : super(key: key); + + @override + State createState() => _SpinningIconButtonState(); +} + +class _SpinningIconButtonState extends State + with SingleTickerProviderStateMixin { + late final AnimationController controller; + late final Animation _animation; + @override + void initState() { + controller = + AnimationController(vsync: this, duration: const Duration(seconds: 10)); + _animation = CurvedAnimation( + parent: controller, + // Use whatever curve you would like, for more details refer to the Curves class + curve: Curves.linearToEaseOut, + ); + controller.forward(); + + super.initState(); + } + + @override + void dispose() { + controller.stop(); + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RotationTransition( + turns: _animation, + child: IconButton( + icon: Icon(widget.iconData), + onPressed: null, + )); + } +} diff --git a/lib/main.dart b/lib/main.dart index 7c66203..9aa10cb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:device_preview_screenshot/device_preview_screenshot.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_sharez/app/app.dart'; import 'package:flutter_sharez/bootstrap.dart'; @@ -24,7 +23,7 @@ void main() async { // device preview enable bootstrap( () => DevicePreview( - enabled: !kReleaseMode, + enabled: false, tools: [ ...DevicePreview.defaultTools, DevicePreviewScreenshot( diff --git a/pubspec.lock b/pubspec.lock index f19d0d4..71b2e31 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -488,6 +488,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_markdown: + dependency: "direct main" + description: + name: flutter_markdown + sha256: d4a1cb250c4e059586af0235f32e02882860a508e189b61f2b31b8810c1e1330 + url: "https://pub.dev" + source: hosted + version: "0.6.17+2" flutter_native_splash: dependency: "direct main" description: @@ -724,6 +732,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.6.0" + markdown: + dependency: transitive + description: + name: markdown + sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd + url: "https://pub.dev" + source: hosted + version: "7.1.1" matcher: dependency: transitive description: @@ -1481,6 +1497,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.1" + version: + dependency: "direct main" + description: + name: version + sha256: "3d4140128e6ea10d83da32fef2fa4003fccbf6852217bb854845802f04191f94" + url: "https://pub.dev" + source: hosted + version: "3.0.2" very_good_analysis: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index da3a58f..e71cb8d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sharez description: "" -version: 1.0.2+2 +version: 1.0.2+3 publish_to: none environment: @@ -25,6 +25,7 @@ dependencies: flutter_displaymode: ^0.6.0 flutter_localizations: sdk: flutter + flutter_markdown: ^0.6.17+2 flutter_native_splash: ^2.3.2 flutter_riverpod: ^2.3.10 flutter_speed_dial: ^7.0.0 @@ -47,6 +48,7 @@ dependencies: talker_flutter: ^3.2.10 url_launcher: ^6.1.14 velocity_x: ^4.1.1 + version: ^3.0.2 dev_dependencies: auto_route_generator: ^7.3.1