From ccb780be508f4000fe6de195976c4fd29c1cbe7b Mon Sep 17 00:00:00 2001 From: Varakh Date: Tue, 30 Nov 2021 00:44:22 +0100 Subject: [PATCH] Initial upgrade to null-safety and latest dependency versions --- CHANGELOG.md | 9 +- README.md | 5 +- lib/app.dart | 21 ++-- lib/core/datamodels/dialog_request.dart | 8 +- lib/core/datamodels/dialog_response.dart | 2 +- lib/core/error/rest_service_exception.dart | 2 +- lib/core/error/service_exception.dart | 2 +- lib/core/manager/dialog_manager.dart | 17 ++-- lib/core/manager/lifecycle_manager.dart | 18 ++-- lib/core/models/rest/apikey.dart | 8 +- lib/core/models/rest/apikeys.dart | 7 +- lib/core/models/rest/apikeys_response.dart | 5 +- lib/core/models/rest/config.dart | 6 +- lib/core/models/rest/config_response.dart | 5 +- .../models/rest/create_apikey_response.dart | 5 +- lib/core/models/rest/history.dart | 7 +- lib/core/models/rest/history_item.dart | 14 ++- .../models/rest/history_multipaste_item.dart | 9 +- .../rest/history_multipaste_item_entry.dart | 17 ++++ lib/core/models/rest/history_response.dart | 5 +- lib/core/models/rest/rest_error.dart | 9 +- lib/core/models/rest/uploaded.dart | 5 +- lib/core/models/rest/uploaded_multi.dart | 5 +- .../models/rest/uploaded_multi_response.dart | 5 +- lib/core/models/rest/uploaded_response.dart | 5 +- lib/core/models/session.dart | 5 +- lib/core/models/uploaded_paste.dart | 21 ++-- lib/core/repositories/file_repository.dart | 18 ++-- lib/core/repositories/user_repository.dart | 13 ++- lib/core/services/api.dart | 67 ++++++++----- lib/core/services/dialog_service.dart | 36 ++++--- lib/core/services/file_service.dart | 7 +- lib/core/services/link_service.dart | 3 +- lib/core/services/navigation_service.dart | 6 +- lib/core/services/permission_service.dart | 16 ++-- lib/core/services/session_service.dart | 4 +- lib/core/services/storage_service.dart | 6 +- lib/core/services/user_service.dart | 12 ++- lib/core/util/formatter_util.dart | 2 +- lib/core/viewmodels/about_model.dart | 2 +- lib/core/viewmodels/base_model.dart | 10 +- lib/core/viewmodels/history_model.dart | 30 +++--- lib/core/viewmodels/login_model.dart | 29 ++++-- lib/core/viewmodels/profile_model.dart | 33 ++++--- lib/core/viewmodels/upload_model.dart | 68 +++++++------ lib/ui/views/base_view.dart | 10 +- lib/ui/views/history_view.dart | 35 ++++--- lib/ui/views/login_view.dart | 19 ++-- lib/ui/views/profile_view.dart | 10 +- lib/ui/views/startup_view.dart | 2 +- lib/ui/views/tabbar_anonymous.dart | 6 +- lib/ui/views/tabbar_authenticated.dart | 49 ++++++---- lib/ui/views/tabbar_container_view.dart | 5 +- lib/ui/views/upload_view.dart | 95 +++++++++++++------ lib/ui/widgets/centered_error_row.dart | 8 +- lib/ui/widgets/login_header_apikey.dart | 6 +- lib/ui/widgets/login_header_credentials.dart | 10 +- lib/ui/widgets/my_appbar.dart | 15 ++- lib/ui/widgets/swipe_navigation.dart | 7 +- pubspec.yaml | 34 +++---- 60 files changed, 561 insertions(+), 339 deletions(-) create mode 100644 lib/core/models/rest/history_multipaste_item_entry.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 59a0973..91d91a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ # CHANGELOG -## 1.3.3+13 - UNRELEASED -* Increased target SDK to 30 +## 1.4.0+13 - UNRELEASED +* Increased target SDK to `30` +* Upgraded to Dart `2.14.4` +* Upgraded to use null-safety +* Upgraded internal dependencies to latest versions ## 1.3.3+12 * Automatically switch to initial tab when coming from the share menu @@ -48,4 +51,4 @@ * Automatic refresh history if something has been uploaded ## 1.0.0+1 -* Initial release \ No newline at end of file +* Initial release diff --git a/README.md b/README.md index 75bce79..656dc9b 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ Start by installing dependencies and generating entities! ### Working versions for SDK ``` -[✓] Flutter (Channel stable, 2.0.6, on Linux, locale de_DE.UTF-8) +Flutter version 2.5.3 +Dart version 2.14.4 ``` ## Dependencies @@ -152,4 +153,4 @@ This should not happen under normal circumstances, please file an issue if it do Ensure to be on the version mentioned above which should be in the stable branch. If everything breaks, start from fresh via `flutter clean` and maybe re-do all necessary steps to get the app -working in the first place. \ No newline at end of file +working in the first place. diff --git a/lib/app.dart b/lib/app.dart index 38a5739..20550ce 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -24,26 +24,29 @@ class MyApp extends StatelessWidget { return LocalizationProvider( state: LocalizationProvider.of(context).state, - child: StreamProvider( + child: StreamProvider( initialData: null, - create: (context) => locator().swipeEventController.stream, - child: StreamProvider( + create: (context) => + locator().swipeEventController.stream, + child: StreamProvider( initialData: null, - create: (context) => locator().refreshEventController.stream, - child: StreamProvider( + create: (context) => + locator().refreshEventController.stream, + child: StreamProvider( initialData: Session.initial(), - create: (context) => locator().sessionController.stream, + create: (context) => + locator().sessionController.stream, child: LifeCycleManager( child: MaterialApp( title: translate('app.title'), builder: (context, child) => Navigator( key: locator().dialogNavigationKey, - onGenerateRoute: (settings) => - MaterialPageRoute(builder: (context) => DialogManager(child: child)), + onGenerateRoute: (settings) => MaterialPageRoute( + builder: (context) => DialogManager(child: child)), ), theme: ThemeData( brightness: Brightness.light, - primarySwatch: primaryAccentColor, + primarySwatch: primaryAccentColor as MaterialColor?, primaryColor: primaryAccentColor), onGenerateRoute: AppRouter.generateRoute, navigatorKey: locator().navigationKey, diff --git a/lib/core/datamodels/dialog_request.dart b/lib/core/datamodels/dialog_request.dart index 44f0903..1c66899 100644 --- a/lib/core/datamodels/dialog_request.dart +++ b/lib/core/datamodels/dialog_request.dart @@ -1,8 +1,8 @@ class DialogRequest { - final String title; - final String description; - final String buttonTitleAccept; - final String buttonTitleDeny; + final String? title; + final String? description; + final String? buttonTitleAccept; + final String? buttonTitleDeny; DialogRequest({ this.title, diff --git a/lib/core/datamodels/dialog_response.dart b/lib/core/datamodels/dialog_response.dart index 8143350..37814ca 100644 --- a/lib/core/datamodels/dialog_response.dart +++ b/lib/core/datamodels/dialog_response.dart @@ -1,5 +1,5 @@ class DialogResponse { - final bool confirmed; + final bool? confirmed; DialogResponse({ this.confirmed, diff --git a/lib/core/error/rest_service_exception.dart b/lib/core/error/rest_service_exception.dart index 18d02c5..8592f86 100644 --- a/lib/core/error/rest_service_exception.dart +++ b/lib/core/error/rest_service_exception.dart @@ -5,7 +5,7 @@ class RestServiceException extends ServiceException { final int statusCode; final dynamic responseBody; - RestServiceException(this.statusCode, {this.responseBody, String message}) + RestServiceException(this.statusCode, {this.responseBody, String? message}) : super(code: ErrorCode.REST_ERROR, message: message); String toString() { diff --git a/lib/core/error/service_exception.dart b/lib/core/error/service_exception.dart index a190869..0b0f6ee 100644 --- a/lib/core/error/service_exception.dart +++ b/lib/core/error/service_exception.dart @@ -2,7 +2,7 @@ import '../enums/error_code.dart'; class ServiceException implements Exception { final ErrorCode code; - final String message; + final String? message; ServiceException({this.code = ErrorCode.GENERAL_ERROR, this.message = ''}); diff --git a/lib/core/manager/dialog_manager.dart b/lib/core/manager/dialog_manager.dart index 8a16af2..5fec7ab 100644 --- a/lib/core/manager/dialog_manager.dart +++ b/lib/core/manager/dialog_manager.dart @@ -6,9 +6,9 @@ import '../datamodels/dialog_response.dart'; import '../services/dialog_service.dart'; class DialogManager extends StatefulWidget { - final Widget child; + final Widget? child; - DialogManager({Key key, this.child}) : super(key: key); + DialogManager({Key? key, this.child}) : super(key: key); _DialogManagerState createState() => _DialogManagerState(); } @@ -24,15 +24,16 @@ class _DialogManagerState extends State { @override Widget build(BuildContext context) { - return widget.child; + return widget.child!; } void _showDialog(DialogRequest request) { List actions = []; - if (request.buttonTitleDeny != null && request.buttonTitleDeny.isNotEmpty) { + if (request.buttonTitleDeny != null && + request.buttonTitleDeny!.isNotEmpty) { Widget denyBtn = TextButton( - child: Text(request.buttonTitleDeny), + child: Text(request.buttonTitleDeny!), onPressed: () { _dialogService.dialogComplete(DialogResponse(confirmed: false)); }, @@ -41,7 +42,7 @@ class _DialogManagerState extends State { } Widget confirmBtn = TextButton( - child: Text(request.buttonTitleAccept), + child: Text(request.buttonTitleAccept!), onPressed: () { _dialogService.dialogComplete(DialogResponse(confirmed: true)); }, @@ -49,8 +50,8 @@ class _DialogManagerState extends State { actions.add(confirmBtn); AlertDialog alert = AlertDialog( - title: Text(request.title), - content: Text(request.description), + title: Text(request.title!), + content: Text(request.description!), actions: actions, ); diff --git a/lib/core/manager/lifecycle_manager.dart b/lib/core/manager/lifecycle_manager.dart index 0cb5aa4..1004beb 100644 --- a/lib/core/manager/lifecycle_manager.dart +++ b/lib/core/manager/lifecycle_manager.dart @@ -9,33 +9,37 @@ import '../util/logger.dart'; /// Stop and start long running services class LifeCycleManager extends StatefulWidget { - final Widget child; + final Widget? child; - LifeCycleManager({Key key, this.child}) : super(key: key); + LifeCycleManager({Key? key, this.child}) : super(key: key); _LifeCycleManagerState createState() => _LifeCycleManagerState(); } -class _LifeCycleManagerState extends State with WidgetsBindingObserver { +class _LifeCycleManagerState extends State + with WidgetsBindingObserver { final Logger logger = getLogger(); - List servicesToManage = [locator(), locator()]; + List servicesToManage = [ + locator(), + locator() + ]; @override Widget build(BuildContext context) { - return widget.child; + return widget.child!; } @override void initState() { super.initState(); - WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance!.addObserver(this); } @override void dispose() { super.dispose(); - WidgetsBinding.instance.removeObserver(this); + WidgetsBinding.instance!.removeObserver(this); } @override diff --git a/lib/core/models/rest/apikey.dart b/lib/core/models/rest/apikey.dart index 2cdcacf..2386259 100644 --- a/lib/core/models/rest/apikey.dart +++ b/lib/core/models/rest/apikey.dart @@ -13,9 +13,13 @@ class ApiKey { @JsonKey(required: true, name: 'access_level') final String accessLevel; - final String comment; + final String? comment; - ApiKey({this.key, this.created, this.accessLevel, this.comment}); + ApiKey( + {required this.key, + required this.created, + required this.accessLevel, + this.comment}); // JSON Init factory ApiKey.fromJson(Map json) => _$ApiKeyFromJson(json); diff --git a/lib/core/models/rest/apikeys.dart b/lib/core/models/rest/apikeys.dart index 5519931..f6b30a5 100644 --- a/lib/core/models/rest/apikeys.dart +++ b/lib/core/models/rest/apikeys.dart @@ -6,13 +6,14 @@ part 'apikeys.g.dart'; @JsonSerializable() class ApiKeys { - @JsonKey(name: "items") + @JsonKey(name: "items", required: true) final Map apikeys; - ApiKeys({this.apikeys}); + ApiKeys({required this.apikeys}); // JSON Init - factory ApiKeys.fromJson(Map json) => _$ApiKeysFromJson(json); + factory ApiKeys.fromJson(Map json) => + _$ApiKeysFromJson(json); // JSON Export Map toJson() => _$ApiKeysToJson(this); diff --git a/lib/core/models/rest/apikeys_response.dart b/lib/core/models/rest/apikeys_response.dart index ba5440c..a2bd8f8 100644 --- a/lib/core/models/rest/apikeys_response.dart +++ b/lib/core/models/rest/apikeys_response.dart @@ -12,10 +12,11 @@ class ApiKeysResponse { @JsonKey(required: true) final ApiKeys data; - ApiKeysResponse({this.status, this.data}); + ApiKeysResponse({required this.status, required this.data}); // JSON Init - factory ApiKeysResponse.fromJson(Map json) => _$ApiKeysResponseFromJson(json); + factory ApiKeysResponse.fromJson(Map json) => + _$ApiKeysResponseFromJson(json); // JSON Export Map toJson() => _$ApiKeysResponseToJson(this); diff --git a/lib/core/models/rest/config.dart b/lib/core/models/rest/config.dart index 4444344..c1a7e83 100644 --- a/lib/core/models/rest/config.dart +++ b/lib/core/models/rest/config.dart @@ -16,7 +16,11 @@ class Config { @JsonKey(name: "request_max_size", required: true) final num requestMaxSize; - Config({this.uploadMaxSize, this.maxFilesPerRequest, this.maxInputVars, this.requestMaxSize}); + Config( + {required this.uploadMaxSize, + required this.maxFilesPerRequest, + required this.maxInputVars, + required this.requestMaxSize}); // JSON Init factory Config.fromJson(Map json) => _$ConfigFromJson(json); diff --git a/lib/core/models/rest/config_response.dart b/lib/core/models/rest/config_response.dart index 014e58f..d25ac7d 100644 --- a/lib/core/models/rest/config_response.dart +++ b/lib/core/models/rest/config_response.dart @@ -12,10 +12,11 @@ class ConfigResponse { @JsonKey(required: true) final Config data; - ConfigResponse({this.status, this.data}); + ConfigResponse({required this.status, required this.data}); // JSON Init - factory ConfigResponse.fromJson(Map json) => _$ConfigResponseFromJson(json); + factory ConfigResponse.fromJson(Map json) => + _$ConfigResponseFromJson(json); // JSON Export Map toJson() => _$ConfigResponseToJson(this); diff --git a/lib/core/models/rest/create_apikey_response.dart b/lib/core/models/rest/create_apikey_response.dart index cdd415d..55efe99 100644 --- a/lib/core/models/rest/create_apikey_response.dart +++ b/lib/core/models/rest/create_apikey_response.dart @@ -10,10 +10,11 @@ class CreateApiKeyResponse { @JsonKey(required: true) final Map data; - CreateApiKeyResponse({this.status, this.data}); + CreateApiKeyResponse({required this.status, required this.data}); // JSON Init - factory CreateApiKeyResponse.fromJson(Map json) => _$CreateApiKeyResponseFromJson(json); + factory CreateApiKeyResponse.fromJson(Map json) => + _$CreateApiKeyResponseFromJson(json); // JSON Export Map toJson() => _$CreateApiKeyResponseToJson(this); diff --git a/lib/core/models/rest/history.dart b/lib/core/models/rest/history.dart index edf7a5e..94ec42d 100644 --- a/lib/core/models/rest/history.dart +++ b/lib/core/models/rest/history.dart @@ -14,12 +14,13 @@ class History { final Map multipasteItems; @JsonKey(name: "total_size") - final String totalSize; + final String? totalSize; - History({this.items, this.multipasteItems, this.totalSize}); + History({required this.items, required this.multipasteItems, this.totalSize}); // JSON Init - factory History.fromJson(Map json) => _$HistoryFromJson(json); + factory History.fromJson(Map json) => + _$HistoryFromJson(json); // JSON Export Map toJson() => _$HistoryToJson(this); diff --git a/lib/core/models/rest/history_item.dart b/lib/core/models/rest/history_item.dart index add2130..54364e1 100644 --- a/lib/core/models/rest/history_item.dart +++ b/lib/core/models/rest/history_item.dart @@ -11,12 +11,20 @@ class HistoryItem { final String filesize; final String hash; final String mimetype; - final String thumbnail; + final String? thumbnail; - HistoryItem({this.date, this.filename, this.filesize, this.hash, this.id, this.mimetype, this.thumbnail}); + HistoryItem( + {required this.date, + required this.filename, + required this.filesize, + required this.hash, + required this.id, + required this.mimetype, + this.thumbnail}); // JSON Init - factory HistoryItem.fromJson(Map json) => _$HistoryItemFromJson(json); + factory HistoryItem.fromJson(Map json) => + _$HistoryItemFromJson(json); // JSON Export Map toJson() => _$HistoryItemToJson(this); diff --git a/lib/core/models/rest/history_multipaste_item.dart b/lib/core/models/rest/history_multipaste_item.dart index f0d9b24..1b56dcd 100644 --- a/lib/core/models/rest/history_multipaste_item.dart +++ b/lib/core/models/rest/history_multipaste_item.dart @@ -1,21 +1,22 @@ import 'package:json_annotation/json_annotation.dart'; -import 'history_item.dart'; +import 'history_multipaste_item_entry.dart'; part 'history_multipaste_item.g.dart'; @JsonSerializable() class HistoryMultipasteItem { final String date; - final Map items; + final Map items; @JsonKey(name: "url_id") final String urlId; - HistoryMultipasteItem({this.date, this.items, this.urlId}); + HistoryMultipasteItem(this.items, {required this.date, required this.urlId}); // JSON Init - factory HistoryMultipasteItem.fromJson(Map json) => _$HistoryMultipasteItemFromJson(json); + factory HistoryMultipasteItem.fromJson(Map json) => + _$HistoryMultipasteItemFromJson(json); // JSON Export Map toJson() => _$HistoryMultipasteItemToJson(this); diff --git a/lib/core/models/rest/history_multipaste_item_entry.dart b/lib/core/models/rest/history_multipaste_item_entry.dart new file mode 100644 index 0000000..fb37fb2 --- /dev/null +++ b/lib/core/models/rest/history_multipaste_item_entry.dart @@ -0,0 +1,17 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'history_multipaste_item_entry.g.dart'; + +@JsonSerializable() +class HistoryMultipasteItemEntry { + final String id; + + HistoryMultipasteItemEntry({required this.id}); + + // JSON Init + factory HistoryMultipasteItemEntry.fromJson(Map json) => + _$HistoryMultipasteItemEntryFromJson(json); + + // JSON Export + Map toJson() => _$HistoryMultipasteItemEntryToJson(this); +} diff --git a/lib/core/models/rest/history_response.dart b/lib/core/models/rest/history_response.dart index b891242..fa8f904 100644 --- a/lib/core/models/rest/history_response.dart +++ b/lib/core/models/rest/history_response.dart @@ -12,10 +12,11 @@ class HistoryResponse { @JsonKey(required: true) final History data; - HistoryResponse({this.status, this.data}); + HistoryResponse({required this.status, required this.data}); // JSON Init - factory HistoryResponse.fromJson(Map json) => _$HistoryResponseFromJson(json); + factory HistoryResponse.fromJson(Map json) => + _$HistoryResponseFromJson(json); // JSON Export Map toJson() => _$HistoryResponseToJson(this); diff --git a/lib/core/models/rest/rest_error.dart b/lib/core/models/rest/rest_error.dart index 4c13200..6db731c 100644 --- a/lib/core/models/rest/rest_error.dart +++ b/lib/core/models/rest/rest_error.dart @@ -10,12 +10,13 @@ class RestError { final String errorId; RestError({ - this.status, - this.message, - this.errorId, + required this.status, + required this.message, + required this.errorId, }); // JSON Init - factory RestError.fromJson(Map json) => _$RestErrorFromJson(json); + factory RestError.fromJson(Map json) => + _$RestErrorFromJson(json); // JSON Export Map toJson() => _$RestErrorToJson(this); diff --git a/lib/core/models/rest/uploaded.dart b/lib/core/models/rest/uploaded.dart index 81b9e84..28f0a6a 100644 --- a/lib/core/models/rest/uploaded.dart +++ b/lib/core/models/rest/uploaded.dart @@ -10,10 +10,11 @@ class Uploaded { @JsonKey(required: true) final List urls; - Uploaded({this.ids, this.urls}); + Uploaded({required this.ids, required this.urls}); // JSON Init - factory Uploaded.fromJson(Map json) => _$UploadedFromJson(json); + factory Uploaded.fromJson(Map json) => + _$UploadedFromJson(json); // JSON Export Map toJson() => _$UploadedToJson(this); diff --git a/lib/core/models/rest/uploaded_multi.dart b/lib/core/models/rest/uploaded_multi.dart index 26c52bc..f404269 100644 --- a/lib/core/models/rest/uploaded_multi.dart +++ b/lib/core/models/rest/uploaded_multi.dart @@ -10,10 +10,11 @@ class UploadedMulti { @JsonKey(required: true, name: "url_id") final String urlId; - UploadedMulti({this.url, this.urlId}); + UploadedMulti({required this.url, required this.urlId}); // JSON Init - factory UploadedMulti.fromJson(Map json) => _$UploadedMultiFromJson(json); + factory UploadedMulti.fromJson(Map json) => + _$UploadedMultiFromJson(json); // JSON Export Map toJson() => _$UploadedMultiToJson(this); diff --git a/lib/core/models/rest/uploaded_multi_response.dart b/lib/core/models/rest/uploaded_multi_response.dart index 0ba806d..51a03a2 100644 --- a/lib/core/models/rest/uploaded_multi_response.dart +++ b/lib/core/models/rest/uploaded_multi_response.dart @@ -12,10 +12,11 @@ class UploadedMultiResponse { @JsonKey(required: true) final UploadedMulti data; - UploadedMultiResponse({this.status, this.data}); + UploadedMultiResponse({required this.status, required this.data}); // JSON Init - factory UploadedMultiResponse.fromJson(Map json) => _$UploadedMultiResponseFromJson(json); + factory UploadedMultiResponse.fromJson(Map json) => + _$UploadedMultiResponseFromJson(json); // JSON Export Map toJson() => _$UploadedMultiResponseToJson(this); diff --git a/lib/core/models/rest/uploaded_response.dart b/lib/core/models/rest/uploaded_response.dart index 1a63a1b..707373f 100644 --- a/lib/core/models/rest/uploaded_response.dart +++ b/lib/core/models/rest/uploaded_response.dart @@ -12,10 +12,11 @@ class UploadedResponse { @JsonKey(required: true) final Uploaded data; - UploadedResponse({this.status, this.data}); + UploadedResponse({required this.status, required this.data}); // JSON Init - factory UploadedResponse.fromJson(Map json) => _$UploadedResponseFromJson(json); + factory UploadedResponse.fromJson(Map json) => + _$UploadedResponseFromJson(json); // JSON Export Map toJson() => _$UploadedResponseToJson(this); diff --git a/lib/core/models/session.dart b/lib/core/models/session.dart index 9909015..4d07ac5 100644 --- a/lib/core/models/session.dart +++ b/lib/core/models/session.dart @@ -7,13 +7,14 @@ class Session { final String url; final String apiKey; - Session({this.url, this.apiKey}); + Session({required this.url, required this.apiKey}); Session.initial() : url = '', apiKey = ''; - factory Session.fromJson(Map json) => _$SessionFromJson(json); + factory Session.fromJson(Map json) => + _$SessionFromJson(json); Map toJson() => _$SessionToJson(this); } diff --git a/lib/core/models/uploaded_paste.dart b/lib/core/models/uploaded_paste.dart index 4575fd9..7db14b6 100644 --- a/lib/core/models/uploaded_paste.dart +++ b/lib/core/models/uploaded_paste.dart @@ -4,29 +4,30 @@ part 'uploaded_paste.g.dart'; @JsonSerializable() class UploadedPaste { - final DateTime date; - final String filename; - final num filesize; - final String hash; + final DateTime? date; + final String? filename; + final num? filesize; + final String? hash; final String id; - final String mimetype; - final String thumbnail; - final bool isMulti; - final List items; + final String? mimetype; + final String? thumbnail; + final bool? isMulti; + final List? items; UploadedPaste( {this.date, this.filename, this.filesize, this.hash, - this.id, + required this.id, this.mimetype, this.thumbnail, this.isMulti, this.items}); // JSON Init - factory UploadedPaste.fromJson(Map json) => _$UploadedPasteFromJson(json); + factory UploadedPaste.fromJson(Map json) => + _$UploadedPasteFromJson(json); // JSON Export Map toJson() => _$UploadedPasteToJson(this); diff --git a/lib/core/repositories/file_repository.dart b/lib/core/repositories/file_repository.dart index 0580f10..8196fa8 100644 --- a/lib/core/repositories/file_repository.dart +++ b/lib/core/repositories/file_repository.dart @@ -27,12 +27,16 @@ class FileRepository { return parsedResponse.data; } - Future postDelete(String id) async { - await _api.post('/file/delete', fields: {'ids[1]': id}); + Future postDelete(String id) async { + var fields = Map.fromEntries([MapEntry("ids[1]", id)]); + var response = await _api.post('/file/delete', fields: fields); + return response; } - Future postUpload(List files, Map additionalFiles) async { - var response = await _api.post('/file/upload', files: files, additionalFiles: additionalFiles); + Future postUpload( + List? files, Map? additionalFiles) async { + var response = await _api.post('/file/upload', + files: files, additionalFiles: additionalFiles); return UploadedResponse.fromJson(json.decode(response.body)); } @@ -40,10 +44,12 @@ class FileRepository { Map multiPasteIds = Map(); ids.forEach((element) { - multiPasteIds.putIfAbsent("ids[${ids.indexOf(element) + 1}]", () => element); + multiPasteIds.putIfAbsent( + "ids[${ids.indexOf(element) + 1}]", () => element); }); - var response = await _api.post('/file/create_multipaste', fields: multiPasteIds); + var response = + await _api.post('/file/create_multipaste', fields: multiPasteIds); return UploadedMultiResponse.fromJson(json.decode(response.body)); } } diff --git a/lib/core/repositories/user_repository.dart b/lib/core/repositories/user_repository.dart index 3482741..bb74855 100644 --- a/lib/core/repositories/user_repository.dart +++ b/lib/core/repositories/user_repository.dart @@ -8,12 +8,17 @@ import '../services/api.dart'; class UserRepository { Api _api = locator(); - Future postApiKey( - String url, String username, String password, String accessLevel, String comment) async { + Future postApiKey(String url, String username, + String password, String accessLevel, String comment) async { _api.setUrl(url); - var response = await _api.post('/user/create_apikey', - fields: {'username': username, 'password': password, 'access_level': accessLevel, 'comment': comment}); + var fields = Map.fromEntries([ + MapEntry("username", username), + MapEntry("password", password), + MapEntry("access_level", accessLevel), + MapEntry("comment", comment), + ]); + var response = await _api.post('/user/create_apikey', fields: fields); return CreateApiKeyResponse.fromJson(json.decode(response.body)); } diff --git a/lib/core/services/api.dart b/lib/core/services/api.dart index 5f9c577..0bdb16a 100644 --- a/lib/core/services/api.dart +++ b/lib/core/services/api.dart @@ -24,25 +24,34 @@ class Api implements ApiErrorConverter { String _url = ""; String _apiKey = ""; - Map _headers = {"Content-Type": _applicationJson, "Accept": _applicationJson}; + Map _headers = { + "Content-Type": _applicationJson, + "Accept": _applicationJson + }; Duration _timeout = Duration(seconds: Constants.apiRequestTimeoutLimit); Future fetch(String route) async { try { - _logger - .d("Requesting GET API endpoint '${_url + route}' with headers '$_headers' and maximum timeout '$_timeout'"); - var response = await http.get(Uri.parse(_url + route), headers: _headers).timeout(_timeout); + _logger.d( + "Requesting GET API endpoint '${_url + route}' with headers '$_headers' and maximum timeout '$_timeout'"); + var response = await http + .get(Uri.parse(_url + route), headers: _headers) + .timeout(_timeout); handleRestErrors(response); return response; } on TimeoutException { - throw ServiceException(code: ErrorCode.SOCKET_TIMEOUT, message: _errorTimeout); + throw ServiceException( + code: ErrorCode.SOCKET_TIMEOUT, message: _errorTimeout); } on SocketException { - throw ServiceException(code: ErrorCode.SOCKET_ERROR, message: _errorNoConnection); + throw ServiceException( + code: ErrorCode.SOCKET_ERROR, message: _errorNoConnection); } } Future post(String route, - {Map fields, List files, Map additionalFiles}) async { + {Map? fields, + List? files, + Map? additionalFiles}) async { try { var uri = Uri.parse(_url + route); var request = http.MultipartRequest('POST', uri) @@ -54,32 +63,39 @@ class Api implements ApiErrorConverter { } if (fields != null && fields.isNotEmpty) { - request.fields.addAll(fields); + request.fields.addAll(fields as Map); } if (files != null && files.isNotEmpty) { files.forEach((element) async { - request.files.add(await http.MultipartFile.fromPath('file[${files.indexOf(element) + 1}]', element.path)); + request.files.add(await http.MultipartFile.fromPath( + 'file[${files.indexOf(element) + 1}]', element.path)); }); } if (additionalFiles != null && additionalFiles.length > 0) { List keys = additionalFiles.keys.toList(); additionalFiles.forEach((key, value) { - var index = files != null ? files.length + keys.indexOf(key) + 1 : keys.indexOf(key) + 1; - request.files.add(http.MultipartFile.fromString('file[$index]', value, filename: key)); + var index = files != null + ? files.length + keys.indexOf(key) + 1 + : keys.indexOf(key) + 1; + request.files.add(http.MultipartFile.fromString('file[$index]', value, + filename: key)); }); } - _logger.d("Requesting POST API endpoint '${uri.toString()}' and ${request.files.length} files"); + _logger.d( + "Requesting POST API endpoint '${uri.toString()}' and ${request.files.length} files"); var multiResponse = await request.send(); var response = await http.Response.fromStream(multiResponse); handleRestErrors(response); return response; } on TimeoutException { - throw ServiceException(code: ErrorCode.SOCKET_TIMEOUT, message: _errorTimeout); + throw ServiceException( + code: ErrorCode.SOCKET_TIMEOUT, message: _errorTimeout); } on SocketException { - throw ServiceException(code: ErrorCode.SOCKET_ERROR, message: _errorNoConnection); + throw ServiceException( + code: ErrorCode.SOCKET_ERROR, message: _errorNoConnection); } } @@ -107,20 +123,21 @@ class Api implements ApiErrorConverter { /// have a json decoded object. Replace this with a custom /// conversion method by overwriting the interface if needed void handleRestErrors(http.Response response) { - if (response != null) { - if (response.statusCode != HttpStatus.ok && response.statusCode != HttpStatus.noContent) { - if (response.headers.containsKey(HttpHeaders.contentTypeHeader)) { - ContentType responseContentType = ContentType.parse(response.headers[HttpHeaders.contentTypeHeader]); + if (response.statusCode != HttpStatus.ok && + response.statusCode != HttpStatus.noContent) { + if (response.headers.containsKey(HttpHeaders.contentTypeHeader)) { + ContentType responseContentType = + ContentType.parse(response.headers[HttpHeaders.contentTypeHeader]!); - if (ContentType.json.primaryType == responseContentType.primaryType && - ContentType.json.subType == responseContentType.subType) { - var parsedBody = convert(response); - throw new RestServiceException(response.statusCode, responseBody: parsedBody); - } + if (ContentType.json.primaryType == responseContentType.primaryType && + ContentType.json.subType == responseContentType.subType) { + var parsedBody = convert(response); + throw new RestServiceException(response.statusCode, + responseBody: parsedBody); } - - throw new RestServiceException(response.statusCode); } + + throw new RestServiceException(response.statusCode); } } diff --git a/lib/core/services/dialog_service.dart b/lib/core/services/dialog_service.dart index 2769a50..2b6788e 100644 --- a/lib/core/services/dialog_service.dart +++ b/lib/core/services/dialog_service.dart @@ -8,8 +8,8 @@ import '../datamodels/dialog_response.dart'; class DialogService { GlobalKey _dialogNavigationKey = GlobalKey(); - Function(DialogRequest) _showDialogListener; - Completer _dialogCompleter; + late Function(DialogRequest) _showDialogListener; + Completer? _dialogCompleter; GlobalKey get dialogNavigationKey => _dialogNavigationKey; @@ -18,35 +18,43 @@ class DialogService { } Future showDialog({ - String title, - String description, - String buttonTitleAccept, + String? title, + String? description, + String? buttonTitleAccept, }) { _dialogCompleter = Completer(); _showDialogListener(DialogRequest( title: title, description: description, buttonTitleAccept: - buttonTitleAccept == null || buttonTitleAccept.isEmpty ? translate('dialog.confirm') : buttonTitleAccept)); - return _dialogCompleter.future; + buttonTitleAccept == null || buttonTitleAccept.isEmpty + ? translate('dialog.confirm') + : buttonTitleAccept)); + return _dialogCompleter!.future; } Future showConfirmationDialog( - {String title, String description, String buttonTitleAccept, String buttonTitleDeny}) { + {String? title, + String? description, + String? buttonTitleAccept, + String? buttonTitleDeny}) { _dialogCompleter = Completer(); _showDialogListener(DialogRequest( title: title, description: description, buttonTitleAccept: - buttonTitleAccept == null || buttonTitleAccept.isEmpty ? translate('dialog.confirm') : buttonTitleAccept, - buttonTitleDeny: - buttonTitleDeny == null || buttonTitleDeny.isEmpty ? translate('dialog.cancel') : buttonTitleDeny)); - return _dialogCompleter.future; + buttonTitleAccept == null || buttonTitleAccept.isEmpty + ? translate('dialog.confirm') + : buttonTitleAccept, + buttonTitleDeny: buttonTitleDeny == null || buttonTitleDeny.isEmpty + ? translate('dialog.cancel') + : buttonTitleDeny)); + return _dialogCompleter!.future; } void dialogComplete(DialogResponse response) { - _dialogNavigationKey.currentState.pop(); - _dialogCompleter.complete(response); + _dialogNavigationKey.currentState!.pop(); + _dialogCompleter!.complete(response); _dialogCompleter = null; } } diff --git a/lib/core/services/file_service.dart b/lib/core/services/file_service.dart index 729f3c6..8ce822f 100644 --- a/lib/core/services/file_service.dart +++ b/lib/core/services/file_service.dart @@ -15,15 +15,16 @@ class FileService { return await _fileRepository.getConfig(url); } - Future getHistory() async { + FutureOr getHistory() async { return await _fileRepository.getHistory(); } - Future deletePaste(String id) async { + Future deletePaste(String id) async { return await _fileRepository.postDelete(id); } - Future uploadPaste(List files, Map additionalFiles) async { + Future uploadPaste( + List? files, Map? additionalFiles) async { return await _fileRepository.postUpload(files, additionalFiles); } diff --git a/lib/core/services/link_service.dart b/lib/core/services/link_service.dart index 4c75c56..5541b90 100644 --- a/lib/core/services/link_service.dart +++ b/lib/core/services/link_service.dart @@ -17,7 +17,8 @@ class LinkService { _logger.e('Could not launch link $link'); _dialogService.showDialog( title: translate('link.dialog.title'), - description: translate('link.dialog.description', args: {'link': link})); + description: + translate('link.dialog.description', args: {'link': link})); } } } diff --git a/lib/core/services/navigation_service.dart b/lib/core/services/navigation_service.dart index 6a0d948..abcc186 100644 --- a/lib/core/services/navigation_service.dart +++ b/lib/core/services/navigation_service.dart @@ -12,16 +12,16 @@ class NavigationService { void pop() { logger.d('NavigationService: pop'); - _navigationKey.currentState.pop(); + _navigationKey.currentState!.pop(); } Future navigateTo(String routeName, {dynamic arguments}) { logger.d('NavigationService: navigateTo $routeName'); - return _navigationKey.currentState.pushNamed(routeName, arguments: arguments); + return _navigationKey.currentState!.pushNamed(routeName, arguments: arguments); } Future navigateAndReplaceTo(String routeName, {dynamic arguments}) { logger.d('NavigationService: navigateAndReplaceTo $routeName'); - return _navigationKey.currentState.pushReplacementNamed(routeName, arguments: arguments); + return _navigationKey.currentState!.pushReplacementNamed(routeName, arguments: arguments); } } diff --git a/lib/core/services/permission_service.dart b/lib/core/services/permission_service.dart index fe23b90..548331c 100644 --- a/lib/core/services/permission_service.dart +++ b/lib/core/services/permission_service.dart @@ -17,9 +17,9 @@ class PermissionService extends StoppableService { final DialogService _dialogService = locator(); final StorageService _storageService = locator(); - Timer _serviceCheckTimer; + Timer? _serviceCheckTimer; - PermissionStatus _permissionStatus; + PermissionStatus? _permissionStatus; bool _permanentlyIgnored = false; bool _devicePermissionDialogActive = false; @@ -57,7 +57,8 @@ class PermissionService extends StoppableService { return; } - var ignoredDialog = await _storageService.hasStoragePermissionDialogIgnored(); + var ignoredDialog = + await _storageService.hasStoragePermissionDialogIgnored(); if (ignoredDialog) { _logger.d('Permanently ignored permission request, skipping'); @@ -79,7 +80,7 @@ class PermissionService extends StoppableService { buttonTitleAccept: translate('permission_service.dialog.grant'), buttonTitleDeny: translate('permission_service.dialog.ignore')); - if (!response.confirmed) { + if (!response.confirmed!) { await _storageService.storeStoragePermissionDialogIgnored(); } else { _devicePermissionDialogActive = true; @@ -104,8 +105,9 @@ class PermissionService extends StoppableService { super.start(); await checkEnabledAndPermission(); - _serviceCheckTimer = - Timer.periodic(Duration(milliseconds: Constants.mediaPermissionCheckInterval), (_serviceTimer) async { + _serviceCheckTimer = Timer.periodic( + Duration(milliseconds: Constants.mediaPermissionCheckInterval), + (_serviceTimer) async { if (!super.serviceStopped) { await checkEnabledAndPermission(); } else { @@ -124,7 +126,7 @@ class PermissionService extends StoppableService { void _removeServiceCheckTimer() { if (_serviceCheckTimer != null) { - _serviceCheckTimer.cancel(); + _serviceCheckTimer!.cancel(); _serviceCheckTimer = null; _logger.d('Removed service check timer'); } diff --git a/lib/core/services/session_service.dart b/lib/core/services/session_service.dart index aeeeb3e..073ae20 100644 --- a/lib/core/services/session_service.dart +++ b/lib/core/services/session_service.dart @@ -14,9 +14,9 @@ class SessionService extends StoppableService { final StorageService _storageService = locator(); final Api _api = locator(); - StreamController sessionController = StreamController(); + StreamController sessionController = StreamController(); - void setApiConfig(String url, String apiKey) { + void setApiConfig(String url, String? apiKey) { _logger.d('Setting API config for session'); _api.setUrl(url); _api.addApiKeyAuthorization(apiKey); diff --git a/lib/core/services/storage_service.dart b/lib/core/services/storage_service.dart index 7d5e297..2aa4c83 100644 --- a/lib/core/services/storage_service.dart +++ b/lib/core/services/storage_service.dart @@ -13,7 +13,7 @@ class StorageService { return _store(_LAST_URL_KEY, url); } - Future retrieveLastUrl() async { + Future retrieveLastUrl() async { return await _retrieve(_LAST_URL_KEY); } @@ -27,7 +27,7 @@ class StorageService { Future retrieveSession() async { var retrieve = await _retrieve(_SESSION_KEY); - return Session.fromJson(json.decode(retrieve)); + return Session.fromJson(json.decode(retrieve!)); } Future hasSession() { @@ -56,7 +56,7 @@ class StorageService { return prefs.remove(key); } - Future _retrieve(String key) async { + Future _retrieve(String key) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.getString(key); } diff --git a/lib/core/services/user_service.dart b/lib/core/services/user_service.dart index f0501f7..36bd78c 100644 --- a/lib/core/services/user_service.dart +++ b/lib/core/services/user_service.dart @@ -12,9 +12,10 @@ class UserService { final FileService _fileService = locator(); final UserRepository _userRepository = locator(); - Future createApiKey( - String url, String username, String password, String accessLevel, String comment) async { - return await _userRepository.postApiKey(url, username, password, accessLevel, comment); + Future createApiKey(String url, String username, + String password, String accessLevel, String comment) async { + return await _userRepository.postApiKey( + url, username, password, accessLevel, comment); } Future getApiKeys() async { @@ -25,8 +26,9 @@ class UserService { Future checkAccessLevelIsAtLeastApiKey() async { try { await _fileService.getHistory(); - } catch (e) { - throw new ServiceException(code: ErrorCode.INVALID_API_KEY, message: e.message); + } on ServiceException catch (e) { + throw new ServiceException( + code: ErrorCode.INVALID_API_KEY, message: e.message); } } } diff --git a/lib/core/util/formatter_util.dart b/lib/core/util/formatter_util.dart index d88f126..8043f13 100644 --- a/lib/core/util/formatter_util.dart +++ b/lib/core/util/formatter_util.dart @@ -6,7 +6,7 @@ class FormatterUtil { /// Format epoch timestamp static String formatEpoch(num millis) { DateFormat dateFormat = DateFormat().add_yMEd().add_Hm(); - return dateFormat.format(DateTime.fromMillisecondsSinceEpoch(millis)); + return dateFormat.format(DateTime.fromMillisecondsSinceEpoch(millis as int)); } static String formatBytes(int bytes, int decimals) { diff --git a/lib/core/viewmodels/about_model.dart b/lib/core/viewmodels/about_model.dart index 7f8cb8c..304781f 100644 --- a/lib/core/viewmodels/about_model.dart +++ b/lib/core/viewmodels/about_model.dart @@ -1,4 +1,4 @@ -import 'package:package_info/package_info.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import '../../core/services/link_service.dart'; import '../../locator.dart'; diff --git a/lib/core/viewmodels/base_model.dart b/lib/core/viewmodels/base_model.dart index ced2419..d36efc3 100644 --- a/lib/core/viewmodels/base_model.dart +++ b/lib/core/viewmodels/base_model.dart @@ -12,13 +12,13 @@ class BaseModel extends ChangeNotifier { bool _isDisposed = false; - Map _stateMap = {STATE_VIEW: ViewState.Idle, STATE_MESSAGE: null}; + Map _stateMap = {STATE_VIEW: ViewState.Idle, STATE_MESSAGE: null}; - ViewState get state => _stateMap[STATE_VIEW]; + ViewState? get state => _stateMap[STATE_VIEW] as ViewState?; - String get stateMessage => _stateMap[STATE_MESSAGE]; + String? get stateMessage => _stateMap[STATE_MESSAGE] as String?; - void setStateValue(String key, Object stateValue) { + void setStateValue(String key, Object? stateValue) { if (_stateMap.containsKey(key)) { _stateMap.update(key, (value) => stateValue); } else { @@ -44,7 +44,7 @@ class BaseModel extends ChangeNotifier { setStateValue(STATE_VIEW, stateView); } - void setStateMessage(String stateMessage) { + void setStateMessage(String? stateMessage) { setStateValue(STATE_MESSAGE, stateMessage); } diff --git a/lib/core/viewmodels/history_model.dart b/lib/core/viewmodels/history_model.dart index 3e5b962..6a2bd17 100644 --- a/lib/core/viewmodels/history_model.dart +++ b/lib/core/viewmodels/history_model.dart @@ -28,13 +28,14 @@ class HistoryModel extends BaseModel { final LinkService _linkService = locator(); final DialogService _dialogService = locator(); - StreamSubscription _refreshTriggerSubscription; + late StreamSubscription _refreshTriggerSubscription; List pastes = []; - String errorMessage; + String? errorMessage; void init() { - _refreshTriggerSubscription = _refreshService.refreshEventController.stream.listen((event) { + _refreshTriggerSubscription = + _refreshService.refreshEventController.stream.listen((event) { if (event == RefreshEvent.RefreshHistory) { _logger.d('History needs a refresh'); getHistory(); @@ -48,13 +49,14 @@ class HistoryModel extends BaseModel { try { pastes.clear(); History _history = await _fileService.getHistory(); - if (_history.items != null) { + if (_history.items.isNotEmpty) { _history.items.forEach((key, value) { var millisecondsSinceEpoch = int.parse(value.date) * 1000; pastes.add( UploadedPaste( id: key, - date: DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch), + date: + DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch), filename: value.filename, filesize: int.parse(value.filesize), hash: value.hash, @@ -66,7 +68,7 @@ class HistoryModel extends BaseModel { }); } - if (_history.multipasteItems != null) { + if (_history.multipasteItems.isNotEmpty) { _history.multipasteItems.forEach((key, multiPaste) { var millisecondsSinceEpoch = int.parse(multiPaste.date) * 1000; pastes.add(UploadedPaste( @@ -77,7 +79,7 @@ class HistoryModel extends BaseModel { }); } - pastes.sort((a, b) => a.date.compareTo(b.date)); + pastes.sort((a, b) => a.date!.compareTo(b.date!)); errorMessage = null; } catch (e) { if (e is RestServiceException) { @@ -90,9 +92,11 @@ class HistoryModel extends BaseModel { e.responseBody is RestError && e.responseBody.message != null) { if (e.statusCode == HttpStatus.badRequest) { - errorMessage = translate('api.bad_request', args: {'reason': e.responseBody.message}); + errorMessage = translate('api.bad_request', + args: {'reason': e.responseBody.message}); } else { - errorMessage = translate('api.general_rest_error_payload', args: {'message': e.responseBody.message}); + errorMessage = translate('api.general_rest_error_payload', + args: {'message': e.responseBody.message}); } } else { errorMessage = translate('api.general_rest_error'); @@ -115,11 +119,12 @@ class HistoryModel extends BaseModel { Future deletePaste(String id) async { DialogResponse res = await _dialogService.showConfirmationDialog( title: translate('history.delete_dialog.title'), - description: translate('history.delete_dialog.description', args: {'id': id}), + description: + translate('history.delete_dialog.description', args: {'id': id}), buttonTitleAccept: translate('history.delete_dialog.accept'), buttonTitleDeny: translate('history.delete_dialog.deny')); - if (!res.confirmed) { + if (!res.confirmed!) { return; } @@ -139,7 +144,8 @@ class HistoryModel extends BaseModel { e.statusCode != HttpStatus.forbidden && e.responseBody is RestError && e.responseBody.message != null) { - errorMessage = translate('api.general_rest_error_payload', args: {'message': e.responseBody.message}); + errorMessage = translate('api.general_rest_error_payload', + args: {'message': e.responseBody.message}); } else { errorMessage = translate('api.general_rest_error'); } diff --git a/lib/core/viewmodels/login_model.dart b/lib/core/viewmodels/login_model.dart index 637e4bd..8249a2f 100644 --- a/lib/core/viewmodels/login_model.dart +++ b/lib/core/viewmodels/login_model.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -40,7 +41,7 @@ class LoginModel extends BaseModel { final Logger _logger = getLogger(); bool useCredentialsLogin = true; - String errorMessage; + String? errorMessage; void toggleLoginMethod() { setStateView(ViewState.Busy); @@ -53,7 +54,7 @@ class LoginModel extends BaseModel { if (hasLastUrl) { setStateView(ViewState.Busy); - var s = await _storageService.retrieveLastUrl(); + var s = await (_storageService.retrieveLastUrl() as FutureOr); if (s.isNotEmpty) { _uriController = new TextEditingController(text: s); @@ -116,8 +117,20 @@ class LoginModel extends BaseModel { try { if (useCredentialsLogin) { CreateApiKeyResponse apiKeyResponse = await _userService.createApiKey( - url, username, password, 'apikey', 'fbmobile-${new DateTime.now().millisecondsSinceEpoch}'); - success = await _sessionService.login(url, apiKeyResponse.data['new_key']); + url, + username, + password, + 'apikey', + 'fbmobile-${new DateTime.now().millisecondsSinceEpoch}'); + + var newKey = apiKeyResponse.data['new_key']; + if (newKey != null) { + success = await _sessionService.login(url, newKey); + } else { + throw new ServiceException( + code: ErrorCode.INVALID_API_KEY, + message: translate('login.errors.invalid_api_key')); + } } else { _sessionService.setApiConfig(url, apiKey); await _userService.checkAccessLevelIsAtLeastApiKey(); @@ -128,13 +141,15 @@ class LoginModel extends BaseModel { if (e is RestServiceException) { if (e.statusCode == HttpStatus.unauthorized) { errorMessage = translate('login.errors.wrong_credentials'); - } else if (e.statusCode != HttpStatus.unauthorized && e.statusCode == HttpStatus.forbidden) { + } else if (e.statusCode != HttpStatus.unauthorized && + e.statusCode == HttpStatus.forbidden) { errorMessage = translate('login.errors.forbidden'); } else if (e.statusCode == HttpStatus.notFound) { errorMessage = translate('api.incompatible_error_not_found'); } if (e.statusCode == HttpStatus.badRequest) { - errorMessage = translate('api.bad_request', args: {'reason': e.responseBody.message}); + errorMessage = translate('api.bad_request', + args: {'reason': e.responseBody.message}); } else { errorMessage = translate('api.general_rest_error'); } @@ -152,7 +167,7 @@ class LoginModel extends BaseModel { throw e; } - if (errorMessage.isNotEmpty) { + if (errorMessage!.isNotEmpty) { _sessionService.logout(); } diff --git a/lib/core/viewmodels/profile_model.dart b/lib/core/viewmodels/profile_model.dart index 180e356..9fc1297 100644 --- a/lib/core/viewmodels/profile_model.dart +++ b/lib/core/viewmodels/profile_model.dart @@ -24,25 +24,28 @@ class ProfileModel extends BaseModel { final FileService _fileService = locator(); final Logger _logger = getLogger(); - String errorMessage; + String? errorMessage; Future logout() async { var dialogResult = await _dialogService.showConfirmationDialog( - title: translate('logout.title'), description: translate('logout.confirm')); + title: translate('logout.title'), + description: translate('logout.confirm')); - if (dialogResult.confirmed) { + if (dialogResult.confirmed!) { await _sessionService.logout(); } } - Future revealApiKey(String apiKey) async { + Future revealApiKey(String? apiKey) async { await _dialogService.showDialog( title: translate('profile.revealed_api_key.title'), - description: translate('profile.revealed_api_key.description', args: {'apiKey': apiKey})); + description: translate('profile.revealed_api_key.description', + args: {'apiKey': apiKey})); } Future showConfig(String url) async { - Config config; + setStateView(ViewState.Busy); + Config? config; try { config = await _fileService.getConfig(url); errorMessage = null; @@ -50,13 +53,15 @@ class ProfileModel extends BaseModel { if (e is RestServiceException) { if (e.statusCode == HttpStatus.unauthorized) { errorMessage = translate('login.errors.wrong_credentials'); - } else if (e.statusCode != HttpStatus.unauthorized && e.statusCode == HttpStatus.forbidden) { + } else if (e.statusCode != HttpStatus.unauthorized && + e.statusCode == HttpStatus.forbidden) { errorMessage = translate('login.errors.forbidden'); } else if (e.statusCode == HttpStatus.notFound) { errorMessage = translate('api.incompatible_error_not_found'); } if (e.statusCode == HttpStatus.badRequest) { - errorMessage = translate('api.bad_request', args: {'reason': e.responseBody.message}); + errorMessage = translate('api.bad_request', + args: {'reason': e.responseBody.message}); } else { errorMessage = translate('api.general_rest_error'); } @@ -66,6 +71,7 @@ class ProfileModel extends BaseModel { errorMessage = translate('api.socket_timeout'); } else { errorMessage = translate('app.unknown_error'); + setStateView(ViewState.Idle); _sessionService.logout(); setStateView(ViewState.Idle); _logger.e('An unknown error occurred', e); @@ -73,19 +79,24 @@ class ProfileModel extends BaseModel { } } + setStateView(ViewState.Idle); + if (config != null && errorMessage == null) { await _dialogService.showDialog( title: translate('profile.shown_config.title'), description: translate('profile.shown_config.description', args: { - 'uploadMaxSize': FormatterUtil.formatBytes(config.uploadMaxSize, 2), + 'uploadMaxSize': + FormatterUtil.formatBytes(config.uploadMaxSize as int, 2), 'maxFilesPerRequest': config.maxFilesPerRequest, 'maxInputVars': config.maxInputVars, - 'requestMaxSize': FormatterUtil.formatBytes(config.requestMaxSize, 2) + 'requestMaxSize': + FormatterUtil.formatBytes(config.requestMaxSize as int, 2) })); } else { await _dialogService.showDialog( title: translate('profile.shown_config.error.title'), - description: translate('profile.shown_config.error.description', args: {'message': errorMessage})); + description: translate('profile.shown_config.error.description', + args: {'message': errorMessage})); } } diff --git a/lib/core/viewmodels/upload_model.dart b/lib/core/viewmodels/upload_model.dart index 495ee29..5f12980 100644 --- a/lib/core/viewmodels/upload_model.dart +++ b/lib/core/viewmodels/upload_model.dart @@ -37,14 +37,14 @@ class UploadModel extends BaseModel { TextEditingController _pasteTextController = TextEditingController(); bool pasteTextTouched = false; - StreamSubscription _intentDataStreamSubscription; + late StreamSubscription _intentDataStreamSubscription; bool createMulti = false; - String fileName; - List paths; - String _extension; + String? fileName; + List? paths; + String? _extension; bool loadingPath = false; - String errorMessage; + String? errorMessage; TextEditingController get pasteTextController => _pasteTextController; @@ -55,8 +55,9 @@ class UploadModel extends BaseModel { }); // For sharing images coming from outside the app while the app is in the memory - _intentDataStreamSubscription = ReceiveSharingIntent.getMediaStream().listen((List value) { - if (value != null && value.length > 0) { + _intentDataStreamSubscription = ReceiveSharingIntent.getMediaStream() + .listen((List value) { + if (value.length > 0) { setStateView(ViewState.Busy); paths = value.map((sharedFile) { return PlatformFile.fromMap({ @@ -67,7 +68,7 @@ class UploadModel extends BaseModel { }); }).toList(); setStateView(ViewState.Idle); - if (paths.isNotEmpty && paths.length > 0) { + if (paths!.isNotEmpty && paths!.length > 0) { _swipeService.addEvent(SwipeEvent.Start); } } @@ -77,7 +78,7 @@ class UploadModel extends BaseModel { // For sharing images coming from outside the app while the app is closed ReceiveSharingIntent.getInitialMedia().then((List value) { - if (value != null && value.length > 0) { + if (value.length > 0) { setStateView(ViewState.Busy); paths = value.map((sharedFile) { return PlatformFile.fromMap({ @@ -88,7 +89,7 @@ class UploadModel extends BaseModel { }); }).toList(); setStateView(ViewState.Idle); - if (paths.isNotEmpty && paths.length > 0) { + if (paths!.isNotEmpty && paths!.length > 0) { _swipeService.addEvent(SwipeEvent.Start); } } @@ -97,8 +98,9 @@ class UploadModel extends BaseModel { }); // For sharing or opening urls/text coming from outside the app while the app is in the memory - _intentDataStreamSubscription = ReceiveSharingIntent.getTextStream().listen((String value) { - if (value != null && value.isNotEmpty) { + _intentDataStreamSubscription = + ReceiveSharingIntent.getTextStream().listen((String value) { + if (value.isNotEmpty) { setStateView(ViewState.Busy); pasteTextController.text = value; setStateView(ViewState.Idle); @@ -109,12 +111,12 @@ class UploadModel extends BaseModel { }); // For sharing or opening urls/text coming from outside the app while the app is closed - ReceiveSharingIntent.getInitialText().then((String value) { + ReceiveSharingIntent.getInitialText().then((String? value) { if (value != null && value.isNotEmpty) { setStateView(ViewState.Busy); pasteTextController.text = value; setStateView(ViewState.Idle); - if (paths.isNotEmpty && paths.length > 0) { + if (paths!.isNotEmpty && paths!.length > 0) { _swipeService.addEvent(SwipeEvent.Start); } } @@ -132,7 +134,7 @@ class UploadModel extends BaseModel { _swipeService.addEvent(SwipeEvent.Start); } - String generatePasteLinks(Map uploads, String url) { + String? generatePasteLinks(Map? uploads, String url) { if (uploads != null && uploads.length > 0) { var links = ''; @@ -165,7 +167,9 @@ class UploadModel extends BaseModel { allowMultiple: true, withData: false, withReadStream: true, - allowedExtensions: (_extension?.isNotEmpty ?? false) ? _extension?.replaceAll(' ', '')?.split(',') : null, + allowedExtensions: (_extension?.isNotEmpty ?? false) + ? _extension?.replaceAll(' ', '').split(',') + : null, )) ?.files; } on PlatformException catch (e) { @@ -175,7 +179,7 @@ class UploadModel extends BaseModel { } loadingPath = false; - fileName = paths != null ? paths.map((e) => e.name).toString() : '...'; + fileName = paths != null ? paths!.map((e) => e.name).toString() : '...'; setStateMessage(null); setStateView(ViewState.Idle); @@ -190,31 +194,35 @@ class UploadModel extends BaseModel { setStateView(ViewState.Idle); } - Future> upload() async { + Future?> upload() async { setStateView(ViewState.Busy); setStateMessage(translate('upload.uploading_now')); Map uploadedPasteIds = new Map(); try { - List files; - Map additionalFiles; + List? files; + Map? additionalFiles; - if (pasteTextController.text != null && pasteTextController.text.isNotEmpty) { - additionalFiles = Map.from( - {'paste-${(new DateTime.now().millisecondsSinceEpoch / 1000).round()}.txt': pasteTextController.text}); + if (pasteTextController.text.isNotEmpty) { + additionalFiles = Map.from({ + 'paste-${(new DateTime.now().millisecondsSinceEpoch / 1000).round()}.txt': + pasteTextController.text + }); } - if (paths != null && paths.length > 0) { - files = paths.map((e) => new File(e.path)).toList(); + if (paths != null && paths!.length > 0) { + files = paths!.map((e) => new File(e.path!)).toList(); } - UploadedResponse response = await _fileService.uploadPaste(files, additionalFiles); + UploadedResponse response = + await _fileService.uploadPaste(files, additionalFiles); response.data.ids.forEach((element) { uploadedPasteIds.putIfAbsent(element, () => false); }); if (createMulti && response.data.ids.length > 1) { - UploadedMultiResponse multiResponse = await _fileService.uploadMultiPaste(response.data.ids); + UploadedMultiResponse multiResponse = + await _fileService.uploadMultiPaste(response.data.ids); uploadedPasteIds.putIfAbsent(multiResponse.data.urlId, () => true); } @@ -234,9 +242,11 @@ class UploadModel extends BaseModel { e.responseBody is RestError && e.responseBody.message != null) { if (e.statusCode == HttpStatus.badRequest) { - errorMessage = translate('api.bad_request', args: {'reason': e.responseBody.message}); + errorMessage = translate('api.bad_request', + args: {'reason': e.responseBody.message}); } else { - errorMessage = translate('api.general_rest_error_payload', args: {'message': e.responseBody.message}); + errorMessage = translate('api.general_rest_error_payload', + args: {'message': e.responseBody.message}); } } else { errorMessage = translate('api.general_rest_error'); diff --git a/lib/ui/views/base_view.dart b/lib/ui/views/base_view.dart index 6e9d805..9bd523f 100644 --- a/lib/ui/views/base_view.dart +++ b/lib/ui/views/base_view.dart @@ -7,8 +7,8 @@ import '../../core/viewmodels/base_model.dart'; import '../../locator.dart'; class BaseView extends StatefulWidget { - final Widget Function(BuildContext context, T model, Widget child) builder; - final Function(T) onModelReady; + final Widget Function(BuildContext context, T model, Widget? child)? builder; + final Function(T)? onModelReady; BaseView({this.builder, this.onModelReady}); @@ -24,14 +24,16 @@ class _BaseViewState extends State> { @override void initState() { if (widget.onModelReady != null) { - widget.onModelReady(model); + widget.onModelReady!(model); } super.initState(); } @override Widget build(BuildContext context) { - return ChangeNotifierProvider(create: (context) => model, child: Consumer(builder: widget.builder)); + return ChangeNotifierProvider( + create: (context) => model, + child: Consumer(builder: widget.builder!)); } @override diff --git a/lib/ui/views/history_view.dart b/lib/ui/views/history_view.dart index 8fc1f82..3ab8f4e 100644 --- a/lib/ui/views/history_view.dart +++ b/lib/ui/views/history_view.dart @@ -41,7 +41,9 @@ class HistoryView extends StatelessWidget { : (model.errorMessage == null ? Container( padding: EdgeInsets.all(0), - child: RefreshIndicator(onRefresh: () => model.getHistory(), child: _renderItems(model, url, context))) + child: RefreshIndicator( + onRefresh: () async => await model.getHistory(), + child: _renderItems(model, url, context))) : Container( padding: EdgeInsets.all(25), child: CenteredErrorRow( @@ -61,7 +63,8 @@ class HistoryView extends StatelessWidget { var openInBrowserButton = _renderOpenInBrowser(model, fullPasteUrl); var dateWidget = ListTile( - title: Text(FormatterUtil.formatEpoch(paste.date.millisecondsSinceEpoch)), + title: Text( + FormatterUtil.formatEpoch(paste.date!.millisecondsSinceEpoch)), subtitle: Text(translate('history.date')), ); @@ -73,7 +76,8 @@ class HistoryView extends StatelessWidget { var copyWidget = ListTile( title: Text(translate('history.copy_link.description')), trailing: IconButton( - icon: Icon(Icons.copy, color: blueColor, textDirection: TextDirection.ltr), + icon: Icon(Icons.copy, + color: blueColor, textDirection: TextDirection.ltr), onPressed: () { FlutterClipboard.copy(fullPasteUrl).then((value) { final snackBar = SnackBar( @@ -96,16 +100,16 @@ class HistoryView extends StatelessWidget { trailing: IconButton( icon: Icon(Icons.delete, color: redColor), onPressed: () { - return model.deletePaste(paste.id); + model.deletePaste(paste.id); })); - if (!paste.isMulti) { + if (!paste.isMulti!) { var titleWidget = ListTile( title: Text(paste.filename ?? paste.id), subtitle: Text(translate('history.filename')), ); var fileSizeWidget = ListTile( - title: Text(FormatterUtil.formatBytes(paste.filesize, 2)), + title: Text(FormatterUtil.formatBytes(paste.filesize as int, 2)), subtitle: Text(translate('history.filesize')), ); var idWidget = ListTile( @@ -113,7 +117,7 @@ class HistoryView extends StatelessWidget { subtitle: Text(translate('history.id')), ); var mimeTypeWidget = ListTile( - title: Text(paste.mimetype), + title: Text(paste.mimetype!), subtitle: Text(translate('history.mimetype')), ); @@ -122,9 +126,9 @@ class HistoryView extends StatelessWidget { widgets.add(fileSizeWidget); widgets.add(mimeTypeWidget); } else { - paste.items.forEach((element) { + paste.items!.forEach((element) { widgets.add(ListTile( - title: Text(element), + title: Text(element!), subtitle: Text(translate('history.multipaste_element')), trailing: _renderOpenInBrowser(model, '$url/$element'), )); @@ -164,12 +168,14 @@ class HistoryView extends StatelessWidget { trailing: Wrap(children: [ openInBrowserButton, IconButton( - icon: Icon(Icons.share, color: blueColor, textDirection: TextDirection.ltr), - onPressed: () { - return Share.share(fullPasteUrl); + icon: Icon(Icons.share, + color: blueColor, textDirection: TextDirection.ltr), + onPressed: () async { + await Share.share(fullPasteUrl); }) ]), - subtitle: Text(!paste.isMulti ? paste.filename : '', style: TextStyle(fontStyle: FontStyle.italic)), + subtitle: Text(!paste.isMulti! ? paste.filename! : '', + style: TextStyle(fontStyle: FontStyle.italic)), ), )); }); @@ -190,7 +196,8 @@ class HistoryView extends StatelessWidget { Widget _renderOpenInBrowser(HistoryModel model, String url) { return IconButton( - icon: Icon(Icons.open_in_new, color: blueColor, textDirection: TextDirection.ltr), + icon: Icon(Icons.open_in_new, + color: blueColor, textDirection: TextDirection.ltr), onPressed: () { return model.openLink(url); }); diff --git a/lib/ui/views/login_view.dart b/lib/ui/views/login_view.dart index b075a56..9004545 100644 --- a/lib/ui/views/login_view.dart +++ b/lib/ui/views/login_view.dart @@ -59,13 +59,18 @@ class LoginView extends StatelessWidget { child: Icon(Icons.help, color: buttonBackgroundColor), onTap: () { _dialogService.showDialog( - title: translate('login.compatibility_dialog.title'), - description: translate('login.compatibility_dialog.body')); + title: translate( + 'login.compatibility_dialog.title'), + description: translate( + 'login.compatibility_dialog.body')); }, ), InkWell( - child: - Icon(model.useCredentialsLogin ? Icons.person_outline : Icons.vpn_key, color: blueColor), + child: Icon( + model.useCredentialsLogin + ? Icons.person_outline + : Icons.vpn_key, + color: blueColor), onTap: () { model.toggleLoginMethod(); }, @@ -85,11 +90,13 @@ class LoginView extends StatelessWidget { apiKeyController: model.apiKeyController), UIHelper.verticalSpaceMedium(), ElevatedButton( - child: Text(translate('login.button'), style: TextStyle(color: buttonForegroundColor)), + child: Text(translate('login.button'), + style: TextStyle(color: buttonForegroundColor)), onPressed: () async { var loginSuccess = await model.login(); if (loginSuccess) { - _navigationService.navigateAndReplaceTo(HomeView.routeName); + _navigationService + .navigateAndReplaceTo(HomeView.routeName); } }, ) diff --git a/lib/ui/views/profile_view.dart b/lib/ui/views/profile_view.dart index fa0c211..27be973 100644 --- a/lib/ui/views/profile_view.dart +++ b/lib/ui/views/profile_view.dart @@ -59,8 +59,8 @@ class ProfileView extends StatelessWidget { translate('profile.show_config'), style: TextStyle(color: buttonForegroundColor), ), - onPressed: () { - return model.showConfig(url); + onPressed: () async { + await model.showConfig(url); })), UIHelper.verticalSpaceMedium(), Padding( @@ -72,7 +72,7 @@ class ProfileView extends StatelessWidget { style: TextStyle(color: buttonForegroundColor), ), onPressed: () { - return model.revealApiKey(apiKey); + model.revealApiKey(apiKey); })), UIHelper.verticalSpaceMedium(), Padding( @@ -83,8 +83,8 @@ class ProfileView extends StatelessWidget { translate('profile.logout'), style: TextStyle(color: buttonForegroundColor), ), - onPressed: () { - return model.logout(); + onPressed: () async { + await model.logout(); })), ], ); diff --git a/lib/ui/views/startup_view.dart b/lib/ui/views/startup_view.dart index 39b478e..512ebd2 100644 --- a/lib/ui/views/startup_view.dart +++ b/lib/ui/views/startup_view.dart @@ -22,7 +22,7 @@ class StartUpView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ CircularProgressIndicator(), - (model.stateMessage.isNotEmpty ? Text(model.stateMessage) : Container()) + (model.stateMessage!.isNotEmpty ? Text(model.stateMessage!) : Container()) ])) : Container())); } diff --git a/lib/ui/views/tabbar_anonymous.dart b/lib/ui/views/tabbar_anonymous.dart index 0e2800a..ef302bd 100644 --- a/lib/ui/views/tabbar_anonymous.dart +++ b/lib/ui/views/tabbar_anonymous.dart @@ -11,7 +11,7 @@ class AnonymousTabBarView extends StatefulWidget { } class AnonymousTabBarState extends State with SingleTickerProviderStateMixin { - TabController _tabController; + TabController? _tabController; int _currentTabIndex = 0; List _realPages = [LoginView()]; @@ -33,7 +33,7 @@ class AnonymousTabBarState extends State with SingleTickerP super.initState(); _tabController = TabController(length: _realPages.length, vsync: this) ..addListener(() { - int selectedIndex = _tabController.index; + int selectedIndex = _tabController!.index; if (_currentTabIndex != selectedIndex) { if (!_hasInit[selectedIndex]) { _tabPages[selectedIndex] = _realPages[selectedIndex]; @@ -46,7 +46,7 @@ class AnonymousTabBarState extends State with SingleTickerP @override void dispose() { - _tabController.dispose(); + _tabController!.dispose(); super.dispose(); } diff --git a/lib/ui/views/tabbar_authenticated.dart b/lib/ui/views/tabbar_authenticated.dart index 3866312..31fdd44 100644 --- a/lib/ui/views/tabbar_authenticated.dart +++ b/lib/ui/views/tabbar_authenticated.dart @@ -20,12 +20,13 @@ class AuthenticatedTabBarView extends StatefulWidget { AuthenticatedTabBarState createState() => AuthenticatedTabBarState(); } -class AuthenticatedTabBarState extends State with SingleTickerProviderStateMixin { +class AuthenticatedTabBarState extends State + with SingleTickerProviderStateMixin { final Logger _logger = getLogger(); final SwipeService _swipeService = locator(); - StreamSubscription _swipeEventSubscription; - TabController _tabController; + late StreamSubscription _swipeEventSubscription; + TabController? _tabController; int _currentTabIndex = 0; List _realPages = [UploadView(), HistoryView(), ProfileView()]; @@ -41,7 +42,7 @@ class AuthenticatedTabBarState extends State with Singl super.initState(); _tabController = TabController(length: _realPages.length, vsync: this) ..addListener(() { - int selectedIndex = _tabController.index; + int selectedIndex = _tabController!.index; if (_currentTabIndex != selectedIndex) { if (!_hasInit[selectedIndex]) { _tabPages[selectedIndex] = _realPages[selectedIndex]; @@ -51,7 +52,8 @@ class AuthenticatedTabBarState extends State with Singl } }); - _swipeEventSubscription = _swipeService.swipeEventController.stream.listen((SwipeEvent event) { + _swipeEventSubscription = + _swipeService.swipeEventController.stream.listen((SwipeEvent event) { _logger.d('Received a swipe event for the authenticated tab bar: $event'); int targetIndex = _currentTabIndex; @@ -71,14 +73,15 @@ class AuthenticatedTabBarState extends State with Singl targetIndex = _tabPages.length - 1; } - _logger.d("Changing to tab '$targetIndex' because of a swipe event '$event'"); - _tabController.animateTo(targetIndex); + _logger.d( + "Changing to tab '$targetIndex' because of a swipe event '$event'"); + _tabController!.animateTo(targetIndex); }); } @override void dispose() { - _tabController.dispose(); + _tabController!.dispose(); _swipeEventSubscription.cancel(); super.dispose(); } @@ -89,9 +92,12 @@ class AuthenticatedTabBarState extends State with Singl double yourWidth = width / 3; double yourHeight = 55; - Color colorTabItem0 = _currentTabIndex == 0 ? blueColor : primaryAccentColor; - Color colorTabItem1 = _currentTabIndex == 1 ? blueColor : primaryAccentColor; - Color colorTabItem2 = _currentTabIndex == 2 ? blueColor : primaryAccentColor; + Color colorTabItem0 = + _currentTabIndex == 0 ? blueColor : primaryAccentColor; + Color colorTabItem1 = + _currentTabIndex == 1 ? blueColor : primaryAccentColor; + Color colorTabItem2 = + _currentTabIndex == 2 ? blueColor : primaryAccentColor; List _tabsButton = [ Container( @@ -100,10 +106,13 @@ class AuthenticatedTabBarState extends State with Singl alignment: Alignment.center, child: Tab( icon: Icon( - _currentTabIndex == 0 ? Icons.upload_outlined : Icons.upload_rounded, + _currentTabIndex == 0 + ? Icons.upload_outlined + : Icons.upload_rounded, color: colorTabItem0, ), - child: Text(translate('tabs.upload'), style: TextStyle(color: colorTabItem0)), + child: Text(translate('tabs.upload'), + style: TextStyle(color: colorTabItem0)), ), ), Container( @@ -112,10 +121,13 @@ class AuthenticatedTabBarState extends State with Singl alignment: Alignment.center, child: Tab( icon: Icon( - _currentTabIndex == 1 ? Icons.history_outlined : Icons.history_rounded, + _currentTabIndex == 1 + ? Icons.history_outlined + : Icons.history_rounded, color: colorTabItem1, ), - child: Text(translate('tabs.history'), style: TextStyle(color: colorTabItem1)), + child: Text(translate('tabs.history'), + style: TextStyle(color: colorTabItem1)), ), ), Container( @@ -124,10 +136,13 @@ class AuthenticatedTabBarState extends State with Singl alignment: Alignment.center, child: Tab( icon: Icon( - _currentTabIndex == 2 ? Icons.person_outlined : Icons.person_rounded, + _currentTabIndex == 2 + ? Icons.person_outlined + : Icons.person_rounded, color: colorTabItem2, ), - child: Text(translate('tabs.profile'), style: TextStyle(color: colorTabItem2)), + child: Text(translate('tabs.profile'), + style: TextStyle(color: colorTabItem2)), ), ), ]; diff --git a/lib/ui/views/tabbar_container_view.dart b/lib/ui/views/tabbar_container_view.dart index 294431a..66ca2f6 100644 --- a/lib/ui/views/tabbar_container_view.dart +++ b/lib/ui/views/tabbar_container_view.dart @@ -8,8 +8,9 @@ import 'tabbar_authenticated.dart'; class TabBarContainerView extends StatelessWidget { @override Widget build(BuildContext context) { - Session currentSession = Provider.of(context); - bool isAuthenticated = currentSession != null && currentSession.apiKey.isNotEmpty; + Session? currentSession = Provider.of(context); + bool isAuthenticated = + currentSession != null ? currentSession.apiKey.isNotEmpty : false; if (isAuthenticated) { return AuthenticatedTabBarView(); diff --git a/lib/ui/views/upload_view.dart b/lib/ui/views/upload_view.dart index d589a25..be0c7fd 100644 --- a/lib/ui/views/upload_view.dart +++ b/lib/ui/views/upload_view.dart @@ -26,7 +26,8 @@ class UploadView extends StatelessWidget { } bool _isUploadButtonEnabled(UploadModel model) { - return model.pasteTextTouched || (model.paths != null && model.paths.length > 0); + return model.pasteTextTouched || + (model.paths != null && model.paths!.length > 0); } Widget _render(UploadModel model, BuildContext context) { @@ -39,7 +40,9 @@ class UploadView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ CircularProgressIndicator(), - (model.stateMessage != null && model.stateMessage.isNotEmpty ? Text(model.stateMessage) : Container()) + (model.stateMessage != null && model.stateMessage!.isNotEmpty + ? Text(model.stateMessage!) + : Container()) ])) : ListView(children: [ Padding( @@ -58,12 +61,15 @@ class UploadView extends StatelessWidget { color: buttonBackgroundColor, ), suffixIcon: IconButton( - onPressed: () => model.pasteTextController.clear(), + onPressed: () => + model.pasteTextController.clear(), icon: Icon(Icons.clear), ), hintText: translate('upload.text_to_be_pasted'), - contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), - border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)), + contentPadding: + EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(32.0)), ), controller: model.pasteTextController)), Padding( @@ -79,20 +85,24 @@ class UploadView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ ElevatedButton.icon( - icon: Icon(Icons.file_copy_sharp, color: blueColor), + icon: Icon(Icons.file_copy_sharp, + color: blueColor), onPressed: () => model.openFileExplorer(), label: Text( translate('upload.open_file_explorer'), - style: TextStyle(color: buttonForegroundColor), + style: + TextStyle(color: buttonForegroundColor), )), ElevatedButton.icon( icon: Icon(Icons.cancel, color: orangeColor), - onPressed: model.paths != null && model.paths.length > 0 + onPressed: model.paths != null && + model.paths!.length > 0 ? () => model.clearCachedFiles() : null, label: Text( translate('upload.clear_temporary_files'), - style: TextStyle(color: buttonForegroundColor), + style: + TextStyle(color: buttonForegroundColor), )), ], )), @@ -118,35 +128,48 @@ class UploadView extends StatelessWidget { onPressed: !_isUploadButtonEnabled(model) ? null : () async { - Map items = await model.upload(); - String clipboardContent = model.generatePasteLinks(items, url); + Map? items = + await model.upload(); + String? clipboardContent = model + .generatePasteLinks(items, url); - if (clipboardContent != null && clipboardContent.isNotEmpty) { - FlutterClipboard.copy(clipboardContent).then((value) { + if (clipboardContent != null && + clipboardContent.isNotEmpty) { + FlutterClipboard.copy( + clipboardContent) + .then((value) { final snackBar = SnackBar( action: SnackBarAction( - label: translate('upload.dismiss'), + label: translate( + 'upload.dismiss'), textColor: blueColor, onPressed: () { - ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of( + context) + .hideCurrentSnackBar(); }, ), - content: Text(translate('upload.uploaded')), + content: Text(translate( + 'upload.uploaded')), duration: Duration(seconds: 10), ); - ScaffoldMessenger.of(context).showSnackBar(snackBar); + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); }); } }, - icon: Icon(Icons.upload_rounded, color: greenColor), + icon: Icon(Icons.upload_rounded, + color: greenColor), label: Text( translate('upload.upload'), - style: TextStyle(color: buttonForegroundColor), + style: + TextStyle(color: buttonForegroundColor), )), ])), - model.errorMessage != null && model.errorMessage.isNotEmpty + model.errorMessage != null && model.errorMessage!.isNotEmpty ? (Padding( - padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), + padding: + const EdgeInsets.only(top: 10.0, bottom: 10.0), child: CenteredErrorRow(model.errorMessage))) : Container(), Builder( @@ -158,16 +181,28 @@ class UploadView extends StatelessWidget { : model.paths != null ? Container( padding: const EdgeInsets.only(bottom: 30.0), - height: MediaQuery.of(context).size.height * 0.50, + height: + MediaQuery.of(context).size.height * 0.50, child: ListView.separated( - itemCount: model.paths != null && model.paths.isNotEmpty ? model.paths.length : 1, - itemBuilder: (BuildContext context, int index) { - final bool isMultiPath = model.paths != null && model.paths.isNotEmpty; + itemCount: model.paths != null && + model.paths!.isNotEmpty + ? model.paths!.length + : 1, + itemBuilder: + (BuildContext context, int index) { + final bool isMultiPath = + model.paths != null && + model.paths!.isNotEmpty; final String name = (isMultiPath - ? model.paths.map((e) => e.name).toList()[index] + ? model.paths! + .map((e) => e.name) + .toList()[index]! : model.fileName ?? '...'); - final path = model.paths.length > 0 - ? model.paths.map((e) => e.path).toList()[index].toString() + final path = model.paths!.length > 0 + ? model.paths! + .map((e) => e.path) + .toList()[index] + .toString() : ''; return Card( @@ -178,7 +213,9 @@ class UploadView extends StatelessWidget { subtitle: Text(path), )); }, - separatorBuilder: (BuildContext context, int index) => const Divider(), + separatorBuilder: + (BuildContext context, int index) => + const Divider(), ), ) : Container(), diff --git a/lib/ui/widgets/centered_error_row.dart b/lib/ui/widgets/centered_error_row.dart index 102e07c..9e43cc9 100644 --- a/lib/ui/widgets/centered_error_row.dart +++ b/lib/ui/widgets/centered_error_row.dart @@ -3,8 +3,8 @@ import 'package:flutter/material.dart'; import '../shared/app_colors.dart'; class CenteredErrorRow extends StatelessWidget { - final Function retryCallback; - final String message; + final Function? retryCallback; + final String? message; CenteredErrorRow(this.message, {this.retryCallback}); @@ -20,7 +20,7 @@ class CenteredErrorRow extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Expanded(child: Center(child: Text(message, style: TextStyle(color: redColor)))), + Expanded(child: Center(child: Text(message!, style: TextStyle(color: redColor)))), ], ), (retryCallback != null @@ -33,7 +33,7 @@ class CenteredErrorRow extends StatelessWidget { icon: Icon(Icons.refresh), color: primaryAccentColor, onPressed: () { - retryCallback(); + retryCallback!(); }, )) ]) diff --git a/lib/ui/widgets/login_header_apikey.dart b/lib/ui/widgets/login_header_apikey.dart index 4bea7de..29c8622 100644 --- a/lib/ui/widgets/login_header_apikey.dart +++ b/lib/ui/widgets/login_header_apikey.dart @@ -8,14 +8,14 @@ class LoginApiKeyHeaders extends StatelessWidget { final TextEditingController uriController; final TextEditingController apiKeyController; - final String validationMessage; + final String? validationMessage; - LoginApiKeyHeaders({@required this.uriController, @required this.apiKeyController, this.validationMessage}); + LoginApiKeyHeaders({required this.uriController, required this.apiKeyController, this.validationMessage}); @override Widget build(BuildContext context) { return Column(children: [ - this.validationMessage != null ? Text(validationMessage, style: TextStyle(color: redColor)) : Container(), + this.validationMessage != null ? Text(validationMessage!, style: TextStyle(color: redColor)) : Container(), LoginTextField(uriController, translate('login.url_placeholder'), Icon(Icons.link), keyboardType: TextInputType.url), LoginTextField( diff --git a/lib/ui/widgets/login_header_credentials.dart b/lib/ui/widgets/login_header_credentials.dart index b4c85a5..f51e017 100644 --- a/lib/ui/widgets/login_header_credentials.dart +++ b/lib/ui/widgets/login_header_credentials.dart @@ -9,18 +9,18 @@ class LoginCredentialsHeaders extends StatelessWidget { final TextEditingController usernameController; final TextEditingController passwordController; - final String validationMessage; + final String? validationMessage; LoginCredentialsHeaders( - {@required this.uriController, - @required this.usernameController, - @required this.passwordController, + {required this.uriController, + required this.usernameController, + required this.passwordController, this.validationMessage}); @override Widget build(BuildContext context) { return Column(children: [ - this.validationMessage != null ? Text(validationMessage, style: TextStyle(color: redColor)) : Container(), + this.validationMessage != null ? Text(validationMessage!, style: TextStyle(color: redColor)) : Container(), LoginTextField(uriController, translate('login.url_placeholder'), Icon(Icons.link), keyboardType: TextInputType.url), LoginTextField(usernameController, translate('login.username_placeholder'), Icon(Icons.person), diff --git a/lib/ui/widgets/my_appbar.dart b/lib/ui/widgets/my_appbar.dart index 70e2417..85cae97 100644 --- a/lib/ui/widgets/my_appbar.dart +++ b/lib/ui/widgets/my_appbar.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import '../shared/app_colors.dart'; import '../widgets/about_iconbutton.dart'; @@ -7,15 +8,23 @@ class MyAppBar extends AppBar { static final List aboutEnabledWidgets = [AboutIconButton()]; static final List aboutDisabledWidgets = []; - MyAppBar({Key key, Widget title, List actionWidgets, bool enableAbout = true}) + MyAppBar( + {Key? key, + required Widget title, + List? actionWidgets, + bool enableAbout = true}) : super( key: key, title: Row(children: [title]), actions: _renderIconButtons(actionWidgets, enableAbout), - brightness: appBarBrightness, + systemOverlayStyle: SystemUiOverlayStyle( + systemNavigationBarColor: primaryAccentColor, // Navigation bar + statusBarColor: primaryAccentColor, // Status bar + ), backgroundColor: primaryAccentColor); - static List _renderIconButtons(List actionWidgets, bool aboutEnabled) { + static List _renderIconButtons( + List? actionWidgets, bool aboutEnabled) { if (actionWidgets == null) { actionWidgets = []; } diff --git a/lib/ui/widgets/swipe_navigation.dart b/lib/ui/widgets/swipe_navigation.dart index 552ddcb..c2dc4cc 100644 --- a/lib/ui/widgets/swipe_navigation.dart +++ b/lib/ui/widgets/swipe_navigation.dart @@ -7,11 +7,11 @@ import '../../locator.dart'; class SwipeNavigation extends StatefulWidget { /// Widget to be augmented with gesture detection. - final Widget child; + final Widget? child; /// Creates a [SwipeNavigation] widget. const SwipeNavigation({ - Key key, + Key? key, this.child, }) : super(key: key); @@ -32,6 +32,7 @@ class _SwipeNavigationState extends State { @override Widget build(BuildContext context) { - return SimpleGestureDetector(onHorizontalSwipe: _onHorizontalSwipe, child: widget.child); + return SimpleGestureDetector( + onHorizontalSwipe: _onHorizontalSwipe, child: widget.child!); } } diff --git a/pubspec.yaml b/pubspec.yaml index bda87c9..bef960d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,43 +11,43 @@ description: A mobile client for FileBin. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.3.4+13 +version: 1.4.0+13 environment: - sdk: ">=2.10.5 <3.0.0" + sdk: '>=2.14.4 <3.0.0' dependencies: flutter: sdk: flutter - cupertino_icons: 1.0.3 + cupertino_icons: 1.0.4 flutter_localizations: sdk: flutter - flutter_translate: 3.0.0 - provider: 5.0.0 - stacked: 2.1.1 - get_it: 6.1.1 # stacked requires ^6 - logger: 1.0.0 - shared_preferences: 2.0.5 - http: 0.13.3 + flutter_translate: 3.0.1 + provider: 6.0.1 + stacked: 2.2.7+1 + get_it: 7.2.0 + logger: 1.1.0 + shared_preferences: 2.0.9 + http: 0.13.4 validators: 3.0.0 flutter_linkify: 5.0.2 - url_launcher: 6.0.3 + url_launcher: 6.0.16 expandable: 5.0.1 - share: 2.0.1 + share: 2.0.4 file_picker: 3.0.1 clipboard: 0.1.3 receive_sharing_intent: 1.4.5 permission_handler: 7.1.0 - package_info: 2.0.0 + package_info_plus: 1.3.0 simple_gesture_detector: 0.2.0 - json_annotation: 3.1.1 # requires null-safety update + json_annotation: 4.3.0 dev_dependencies: flutter_test: sdk: flutter - build_runner: 1.11.5 # requires null-safety update - built_value_generator: 8.0.4 # requires null-safety update - json_serializable: 3.5.1 # requires null-safety update + build_runner: 2.1.5 + built_value_generator: 8.1.3 + json_serializable: 6.0.1 # For information on the generic Dart part of this file, see the # following page: https://www.dartlang.org/tools/pub/pubspec