bugfix/10-library-prevents-compile-and-wrong-color-in-login #11

Merged
Varakh merged 5 commits from bugfix/10-library-prevents-compile-and-wrong-color-in-login into master 2023-01-16 18:33:00 +00:00
53 changed files with 284 additions and 130 deletions
Showing only changes of commit 5e9ac38ca0 - Show all commits

View file

@ -18,8 +18,10 @@ import 'ui/shared/app_colors.dart';
import 'ui/views/startup_view.dart';
class MyApp extends StatelessWidget {
static final _defaultLightColorScheme = ColorScheme.fromSwatch(primarySwatch: myColor, brightness: Brightness.light);
static final _defaultDarkColorScheme = ColorScheme.fromSwatch(primarySwatch: myColor, brightness: Brightness.dark);
static final _defaultLightColorScheme = ColorScheme.fromSwatch(
primarySwatch: myColor, brightness: Brightness.light);
static final _defaultDarkColorScheme = ColorScheme.fromSwatch(
primarySwatch: myColor, brightness: Brightness.dark);
MyApp({super.key}) {
initializeDateFormatting('en');
@ -33,23 +35,30 @@ class MyApp extends StatelessWidget {
state: LocalizationProvider.of(context).state,
child: StreamProvider<RefreshEvent?>(
initialData: null,
create: (context) => locator<RefreshService>().refreshEventController.stream,
create: (context) =>
locator<RefreshService>().refreshEventController.stream,
child: StreamProvider<Session?>(
initialData: Session.initial(),
create: (context) => locator<SessionService>().sessionController.stream,
child: LifeCycleManager(child: DynamicColorBuilder(builder: (lightColorScheme, darkColorScheme) {
create: (context) =>
locator<SessionService>().sessionController.stream,
child: LifeCycleManager(child: DynamicColorBuilder(
builder: (lightColorScheme, darkColorScheme) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: translate('app.title'),
builder: (context, child) => Navigator(
key: locator<DialogService>().dialogNavigationKey,
onGenerateRoute: (settings) => MaterialPageRoute(builder: (context) => DialogManager(child: child)),
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => DialogManager(child: child)),
),
theme: ThemeData(
useMaterial3: true,
brightness: Brightness.light,
colorScheme: lightColorScheme ?? _defaultLightColorScheme),
darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme ?? _defaultDarkColorScheme),
colorScheme:
lightColorScheme ?? _defaultLightColorScheme),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: darkColorScheme ?? _defaultDarkColorScheme),
onGenerateRoute: AppRouter.generateRoute,
navigatorKey: locator<NavigationService>().navigationKey,
home: const StartUpView(),

View file

@ -31,7 +31,8 @@ class _DialogManagerState extends State<DialogManager> {
void _showDialog(DialogRequest request) {
List<Widget> actions = <Widget>[];
if (request.buttonTitleDeny != null && request.buttonTitleDeny!.isNotEmpty) {
if (request.buttonTitleDeny != null &&
request.buttonTitleDeny!.isNotEmpty) {
Widget denyBtn = TextButton(
child: Text(request.buttonTitleDeny!),
onPressed: () {

View file

@ -17,10 +17,14 @@ class LifeCycleManager extends StatefulWidget {
_LifeCycleManagerState createState() => _LifeCycleManagerState();
}
class _LifeCycleManagerState extends State<LifeCycleManager> with WidgetsBindingObserver {
class _LifeCycleManagerState extends State<LifeCycleManager>
with WidgetsBindingObserver {
final Logger logger = getLogger();
List<StoppableService> servicesToManage = [locator<SessionService>(), locator<PermissionService>()];
List<StoppableService> servicesToManage = [
locator<SessionService>(),
locator<PermissionService>()
];
@override
Widget build(BuildContext context) {

View file

@ -15,7 +15,11 @@ class ApiKey {
final String? comment;
ApiKey({required this.key, required this.created, required this.accessLevel, this.comment});
ApiKey(
{required this.key,
required this.created,
required this.accessLevel,
this.comment});
// JSON Init
factory ApiKey.fromJson(Map<String, dynamic> json) => _$ApiKeyFromJson(json);

View file

@ -12,7 +12,8 @@ class ApiKeys {
ApiKeys({required this.apikeys});
// JSON Init
factory ApiKeys.fromJson(Map<String, dynamic> json) => _$ApiKeysFromJson(json);
factory ApiKeys.fromJson(Map<String, dynamic> json) =>
_$ApiKeysFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$ApiKeysToJson(this);

View file

@ -15,7 +15,8 @@ class ApiKeysResponse {
ApiKeysResponse({required this.status, required this.data});
// JSON Init
factory ApiKeysResponse.fromJson(Map<String, dynamic> json) => _$ApiKeysResponseFromJson(json);
factory ApiKeysResponse.fromJson(Map<String, dynamic> json) =>
_$ApiKeysResponseFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$ApiKeysResponseToJson(this);

View file

@ -15,7 +15,8 @@ class ConfigResponse {
ConfigResponse({required this.status, required this.data});
// JSON Init
factory ConfigResponse.fromJson(Map<String, dynamic> json) => _$ConfigResponseFromJson(json);
factory ConfigResponse.fromJson(Map<String, dynamic> json) =>
_$ConfigResponseFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$ConfigResponseToJson(this);

View file

@ -13,7 +13,8 @@ class CreateApiKeyResponse {
CreateApiKeyResponse({required this.status, required this.data});
// JSON Init
factory CreateApiKeyResponse.fromJson(Map<String, dynamic> json) => _$CreateApiKeyResponseFromJson(json);
factory CreateApiKeyResponse.fromJson(Map<String, dynamic> json) =>
_$CreateApiKeyResponseFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$CreateApiKeyResponseToJson(this);

View file

@ -19,7 +19,8 @@ class History {
History({required this.items, required this.multipasteItems, this.totalSize});
// JSON Init
factory History.fromJson(Map<String, dynamic> json) => _$HistoryFromJson(json);
factory History.fromJson(Map<String, dynamic> json) =>
_$HistoryFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$HistoryToJson(this);

View file

@ -23,7 +23,8 @@ class HistoryItem {
this.thumbnail});
// JSON Init
factory HistoryItem.fromJson(Map<String, dynamic> json) => _$HistoryItemFromJson(json);
factory HistoryItem.fromJson(Map<String, dynamic> json) =>
_$HistoryItemFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$HistoryItemToJson(this);

View file

@ -15,7 +15,8 @@ class HistoryMultipasteItem {
HistoryMultipasteItem(this.items, {required this.date, required this.urlId});
// JSON Init
factory HistoryMultipasteItem.fromJson(Map<String, dynamic> json) => _$HistoryMultipasteItemFromJson(json);
factory HistoryMultipasteItem.fromJson(Map<String, dynamic> json) =>
_$HistoryMultipasteItemFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$HistoryMultipasteItemToJson(this);

View file

@ -9,7 +9,8 @@ class HistoryMultipasteItemEntry {
HistoryMultipasteItemEntry({required this.id});
// JSON Init
factory HistoryMultipasteItemEntry.fromJson(Map<String, dynamic> json) => _$HistoryMultipasteItemEntryFromJson(json);
factory HistoryMultipasteItemEntry.fromJson(Map<String, dynamic> json) =>
_$HistoryMultipasteItemEntryFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$HistoryMultipasteItemEntryToJson(this);

View file

@ -15,7 +15,8 @@ class HistoryResponse {
HistoryResponse({required this.status, required this.data});
// JSON Init
factory HistoryResponse.fromJson(Map<String, dynamic> json) => _$HistoryResponseFromJson(json);
factory HistoryResponse.fromJson(Map<String, dynamic> json) =>
_$HistoryResponseFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$HistoryResponseToJson(this);

View file

@ -15,7 +15,8 @@ class RestError {
required this.errorId,
}); // JSON Init
factory RestError.fromJson(Map<String, dynamic> json) => _$RestErrorFromJson(json);
factory RestError.fromJson(Map<String, dynamic> json) =>
_$RestErrorFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$RestErrorToJson(this);

View file

@ -13,7 +13,8 @@ class Uploaded {
Uploaded({required this.ids, required this.urls});
// JSON Init
factory Uploaded.fromJson(Map<String, dynamic> json) => _$UploadedFromJson(json);
factory Uploaded.fromJson(Map<String, dynamic> json) =>
_$UploadedFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$UploadedToJson(this);

View file

@ -13,7 +13,8 @@ class UploadedMulti {
UploadedMulti({required this.url, required this.urlId});
// JSON Init
factory UploadedMulti.fromJson(Map<String, dynamic> json) => _$UploadedMultiFromJson(json);
factory UploadedMulti.fromJson(Map<String, dynamic> json) =>
_$UploadedMultiFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$UploadedMultiToJson(this);

View file

@ -15,7 +15,8 @@ class UploadedMultiResponse {
UploadedMultiResponse({required this.status, required this.data});
// JSON Init
factory UploadedMultiResponse.fromJson(Map<String, dynamic> json) => _$UploadedMultiResponseFromJson(json);
factory UploadedMultiResponse.fromJson(Map<String, dynamic> json) =>
_$UploadedMultiResponseFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$UploadedMultiResponseToJson(this);

View file

@ -15,7 +15,8 @@ class UploadedResponse {
UploadedResponse({required this.status, required this.data});
// JSON Init
factory UploadedResponse.fromJson(Map<String, dynamic> json) => _$UploadedResponseFromJson(json);
factory UploadedResponse.fromJson(Map<String, dynamic> json) =>
_$UploadedResponseFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$UploadedResponseToJson(this);

View file

@ -13,7 +13,8 @@ class Session {
: url = '',
apiKey = '';
factory Session.fromJson(Map<String, dynamic> json) => _$SessionFromJson(json);
factory Session.fromJson(Map<String, dynamic> json) =>
_$SessionFromJson(json);
Map<String, dynamic> toJson() => _$SessionToJson(this);
}

View file

@ -26,7 +26,8 @@ class UploadedPaste {
this.items});
// JSON Init
factory UploadedPaste.fromJson(Map<String, dynamic> json) => _$UploadedPasteFromJson(json);
factory UploadedPaste.fromJson(Map<String, dynamic> json) =>
_$UploadedPasteFromJson(json);
// JSON Export
Map<String, dynamic> toJson() => _$UploadedPasteToJson(this);

View file

@ -33,8 +33,10 @@ class FileRepository {
return response;
}
Future<UploadedResponse> postUpload(List<File>? files, Map<String, String>? additionalFiles) async {
var response = await _api.post('/file/upload', files: files, additionalFiles: additionalFiles);
Future<UploadedResponse> postUpload(
List<File>? files, Map<String, String>? additionalFiles) async {
var response = await _api.post('/file/upload',
files: files, additionalFiles: additionalFiles);
return UploadedResponse.fromJson(json.decode(response.body));
}
@ -42,10 +44,12 @@ class FileRepository {
Map<String, String> multiPasteIds = {};
for (var element in ids) {
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));
}
}

View file

@ -8,8 +8,8 @@ import '../services/api.dart';
class UserRepository {
final Api _api = locator<Api>();
Future<CreateApiKeyResponse> postApiKey(
String url, String username, String password, String accessLevel, String comment) async {
Future<CreateApiKeyResponse> postApiKey(String url, String username,
String password, String accessLevel, String comment) async {
_api.setUrl(url);
var fields = Map.fromEntries([

View file

@ -24,25 +24,34 @@ class Api implements ApiErrorConverter {
String _url = "";
String _apiKey = "";
final Map<String, String> _headers = {"Content-Type": _applicationJson, "Accept": _applicationJson};
final Map<String, String> _headers = {
"Content-Type": _applicationJson,
"Accept": _applicationJson
};
Duration _timeout = const Duration(seconds: Constants.apiRequestTimeoutLimit);
Future<http.Response> fetch<T>(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.socketTimeout, message: _errorTimeout);
throw ServiceException(
code: ErrorCode.socketTimeout, message: _errorTimeout);
} on SocketException {
throw ServiceException(code: ErrorCode.socketError, message: _errorNoConnection);
throw ServiceException(
code: ErrorCode.socketError, message: _errorNoConnection);
}
}
Future<http.Response> post<T>(String route,
{Map<String, String?>? fields, List<File>? files, Map<String, String>? additionalFiles}) async {
{Map<String, String?>? fields,
List<File>? files,
Map<String, String>? additionalFiles}) async {
try {
var uri = Uri.parse(_url + route);
var request = http.MultipartRequest('POST', uri)
@ -59,27 +68,34 @@ class Api implements ApiErrorConverter {
if (files != null && files.isNotEmpty) {
for (var element in files) {
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.isNotEmpty) {
List<String> 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.socketTimeout, message: _errorTimeout);
throw ServiceException(
code: ErrorCode.socketTimeout, message: _errorTimeout);
} on SocketException {
throw ServiceException(code: ErrorCode.socketError, message: _errorNoConnection);
throw ServiceException(
code: ErrorCode.socketError, message: _errorNoConnection);
}
}
@ -107,14 +123,17 @@ 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.statusCode != HttpStatus.ok && response.statusCode != HttpStatus.noContent) {
if (response.statusCode != HttpStatus.ok &&
response.statusCode != HttpStatus.noContent) {
if (response.headers.containsKey(HttpHeaders.contentTypeHeader)) {
ContentType responseContentType = ContentType.parse(response.headers[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 RestServiceException(response.statusCode, responseBody: parsedBody);
throw RestServiceException(response.statusCode,
responseBody: parsedBody);
}
}

View file

@ -7,7 +7,8 @@ import '../datamodels/dialog_request.dart';
import '../datamodels/dialog_response.dart';
class DialogService {
final GlobalKey<NavigatorState> _dialogNavigationKey = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> _dialogNavigationKey =
GlobalKey<NavigatorState>();
late Function(DialogRequest) _showDialogListener;
Completer<DialogResponse>? _dialogCompleter;
@ -27,20 +28,28 @@ class DialogService {
title: title,
description: description,
buttonTitleAccept:
buttonTitleAccept == null || buttonTitleAccept.isEmpty ? translate('dialog.confirm') : buttonTitleAccept));
buttonTitleAccept == null || buttonTitleAccept.isEmpty
? translate('dialog.confirm')
: buttonTitleAccept));
return _dialogCompleter!.future;
}
Future<DialogResponse> showConfirmationDialog(
{String? title, String? description, String? buttonTitleAccept, String? buttonTitleDeny}) {
{String? title,
String? description,
String? buttonTitleAccept,
String? buttonTitleDeny}) {
_dialogCompleter = Completer<DialogResponse>();
_showDialogListener(DialogRequest(
title: title,
description: description,
buttonTitleAccept:
buttonTitleAccept == null || buttonTitleAccept.isEmpty ? translate('dialog.confirm') : buttonTitleAccept,
buttonTitleDeny:
buttonTitleDeny == null || buttonTitleDeny.isEmpty ? translate('dialog.cancel') : buttonTitleDeny));
buttonTitleAccept == null || buttonTitleAccept.isEmpty
? translate('dialog.confirm')
: buttonTitleAccept,
buttonTitleDeny: buttonTitleDeny == null || buttonTitleDeny.isEmpty
? translate('dialog.cancel')
: buttonTitleDeny));
return _dialogCompleter!.future;
}

View file

@ -23,7 +23,8 @@ class FileService {
return await _fileRepository.postDelete(id);
}
Future<UploadedResponse> uploadPaste(List<File>? files, Map<String, String>? additionalFiles) async {
Future<UploadedResponse> uploadPaste(
List<File>? files, Map<String, String>? additionalFiles) async {
return await _fileRepository.postUpload(files, additionalFiles);
}

View file

@ -19,7 +19,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}));
}
}
}

View file

@ -17,11 +17,13 @@ class NavigationService {
Future<dynamic> 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<dynamic> navigateAndReplaceTo(String routeName, {dynamic arguments}) {
logger.d('NavigationService: navigateAndReplaceTo $routeName');
return _navigationKey.currentState!.pushReplacementNamed(routeName, arguments: arguments);
return _navigationKey.currentState!
.pushReplacementNamed(routeName, arguments: arguments);
}
}

View file

@ -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');
@ -104,8 +105,9 @@ class PermissionService extends StoppableService {
super.start();
await checkEnabledAndPermission();
_serviceCheckTimer =
Timer.periodic(const Duration(milliseconds: Constants.mediaPermissionCheckInterval), (serviceTimer) async {
_serviceCheckTimer = Timer.periodic(
const Duration(milliseconds: Constants.mediaPermissionCheckInterval),
(serviceTimer) async {
if (!super.serviceStopped) {
await checkEnabledAndPermission();
} else {

View file

@ -3,7 +3,8 @@ import 'dart:async';
import '../enums/refresh_event.dart';
class RefreshService {
StreamController<RefreshEvent> refreshEventController = StreamController<RefreshEvent>.broadcast();
StreamController<RefreshEvent> refreshEventController =
StreamController<RefreshEvent>.broadcast();
void addEvent(RefreshEvent event) {
if (refreshEventController.hasListener) {

View file

@ -7,7 +7,8 @@ import '../models/session.dart';
class StorageService {
static const _sessionKey = 'session';
static const _lastUrlKey = 'last_url';
static const _storagePermissionDialogIgnoredKey = 'storage_permission_ignored';
static const _storagePermissionDialogIgnoredKey =
'storage_permission_ignored';
Future<bool> storeLastUrl(String url) {
return _store(_lastUrlKey, url);

View file

@ -12,9 +12,10 @@ class UserService {
final FileService _fileService = locator<FileService>();
final UserRepository _userRepository = locator<UserRepository>();
Future<CreateApiKeyResponse> createApiKey(
String url, String username, String password, String accessLevel, String comment) async {
return await _userRepository.postApiKey(url, username, password, accessLevel, comment);
Future<CreateApiKeyResponse> createApiKey(String url, String username,
String password, String accessLevel, String comment) async {
return await _userRepository.postApiKey(
url, username, password, accessLevel, comment);
}
Future<ApiKeysResponse> getApiKeys() async {

View file

@ -6,7 +6,8 @@ class FormatterUtil {
/// Format epoch timestamp
static String formatEpoch(num millis) {
DateFormat dateFormat = DateFormat().add_yMEd().add_Hm();
return dateFormat.format(DateTime.fromMillisecondsSinceEpoch(millis as int));
return dateFormat
.format(DateTime.fromMillisecondsSinceEpoch(millis as int));
}
static String formatBytes(int bytes, int decimals) {

View file

@ -12,7 +12,10 @@ class BaseModel extends ChangeNotifier {
bool _isDisposed = false;
final Map<String, Object?> _stateMap = {stateViewKey: ViewState.idle, stateMessageKey: null};
final Map<String, Object?> _stateMap = {
stateViewKey: ViewState.idle,
stateMessageKey: null
};
ViewState? get state => _stateMap[stateViewKey] as ViewState?;
@ -42,11 +45,14 @@ class BaseModel extends ChangeNotifier {
return null;
}
void setStateBoolValue(String key, bool stateValue) => _setStateValue(key, stateValue);
void setStateBoolValue(String key, bool stateValue) =>
_setStateValue(key, stateValue);
void setStateIntValue(String key, int? stateValue) => _setStateValue(key, stateValue);
void setStateIntValue(String key, int? stateValue) =>
_setStateValue(key, stateValue);
void setStateStringValue(String key, String? stateValue) => _setStateValue(key, stateValue);
void setStateStringValue(String key, String? stateValue) =>
_setStateValue(key, stateValue);
void _setStateValue(String key, Object? stateValue) {
if (_stateMap.containsKey(key)) {
@ -57,7 +63,8 @@ class BaseModel extends ChangeNotifier {
if (!_isDisposed) {
notifyListeners();
_logger.d("Notified state value update '($key, ${stateValue.toString()})'");
_logger
.d("Notified state value update '($key, ${stateValue.toString()})'");
}
}

View file

@ -34,7 +34,8 @@ class HistoryModel extends BaseModel {
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();
@ -54,7 +55,8 @@ class HistoryModel extends BaseModel {
pastes.add(
UploadedPaste(
id: key,
date: DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch),
date:
DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch),
filename: value.filename,
filesize: int.parse(value.filesize),
hash: value.hash,
@ -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,7 +119,8 @@ 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'));
@ -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');
}

View file

@ -117,13 +117,19 @@ class LoginModel extends BaseModel {
try {
if (useCredentialsLogin) {
CreateApiKeyResponse apiKeyResponse = await _userService.createApiKey(
url, username, password, 'apikey', 'fbmobile-${DateTime.now().millisecondsSinceEpoch}');
url,
username,
password,
'apikey',
'fbmobile-${DateTime.now().millisecondsSinceEpoch}');
var newKey = apiKeyResponse.data['new_key'];
if (newKey != null) {
success = await _sessionService.login(url, newKey);
} else {
throw ServiceException(code: ErrorCode.invalidApiKey, message: translate('login.errors.invalid_api_key'));
throw ServiceException(
code: ErrorCode.invalidApiKey,
message: translate('login.errors.invalid_api_key'));
}
} else {
_sessionService.setApiConfig(url, apiKey);
@ -135,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');
}

View file

@ -31,7 +31,8 @@ class ProfileModel extends BaseModel {
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!) {
await _sessionService.logout();
@ -41,7 +42,8 @@ class ProfileModel extends BaseModel {
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 {
@ -54,13 +56,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');
}
@ -84,15 +88,18 @@ class ProfileModel extends BaseModel {
await _dialogService.showDialog(
title: translate('profile.shown_config.title'),
description: translate('profile.shown_config.description', args: {
'uploadMaxSize': FormatterUtil.formatBytes(config.uploadMaxSize as int, 2),
'uploadMaxSize':
FormatterUtil.formatBytes(config.uploadMaxSize as int, 2),
'maxFilesPerRequest': config.maxFilesPerRequest,
'maxInputVars': config.maxInputVars,
'requestMaxSize': FormatterUtil.formatBytes(config.requestMaxSize as int, 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}));
}
}

View file

@ -11,7 +11,8 @@ void main() async {
setupLogger(Level.info);
setupLocator();
var delegate = await LocalizationDelegate.create(fallbackLocale: 'en', supportedLocales: ['en', 'en_US']);
var delegate = await LocalizationDelegate.create(
fallbackLocale: 'en', supportedLocales: ['en', 'en_US']);
WidgetsFlutterBinding.ensureInitialized();
runApp(LocalizedApp(delegate, MyApp()));

View file

@ -27,7 +27,8 @@ class AppRouter {
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text(translate('dev.no_route', args: {'route': settings.name})),
child: Text(translate('dev.no_route',
args: {'route': settings.name})),
),
));
}

View file

@ -38,7 +38,8 @@ class AboutView extends StatelessWidget {
padding: const EdgeInsets.all(0),
child: ListView(
shrinkWrap: true,
padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10, top: 10),
padding: const EdgeInsets.only(
left: 10.0, right: 10.0, bottom: 10, top: 10),
children: <Widget>[
UIHelper.verticalSpaceMedium(),
Center(child: logo),

View file

@ -31,7 +31,9 @@ class _BaseViewState<T extends BaseModel> extends State<BaseView<T>> {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T?>(create: (context) => model, child: Consumer<T>(builder: widget.builder!));
return ChangeNotifierProvider<T?>(
create: (context) => model,
child: Consumer<T>(builder: widget.builder!));
}
@override

View file

@ -27,8 +27,9 @@ class HistoryView extends StatelessWidget {
model.init();
return model.getHistory();
},
builder: (context, model, child) =>
Scaffold(appBar: MyAppBar(title: Text(translate('titles.history'))), body: _render(model, context)),
builder: (context, model, child) => Scaffold(
appBar: MyAppBar(title: Text(translate('titles.history'))),
body: _render(model, context)),
);
}
@ -41,7 +42,8 @@ class HistoryView extends StatelessWidget {
? Container(
padding: const EdgeInsets.all(0),
child: RefreshIndicator(
onRefresh: () async => await model.getHistory(), child: _renderItems(model, url, context)))
onRefresh: () async => await model.getHistory(),
child: _renderItems(model, url, context)))
: Container(
padding: const 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: const Icon(Icons.copy, color: blueColor, textDirection: TextDirection.ltr),
icon: const Icon(Icons.copy,
color: blueColor, textDirection: TextDirection.ltr),
onPressed: () {
FlutterClipboard.copy(fullPasteUrl).then((value) {
final snackBar = SnackBar(
@ -164,12 +168,14 @@ class HistoryView extends StatelessWidget {
trailing: Wrap(children: [
openInBrowserButton,
IconButton(
icon: const Icon(Icons.share, color: blueColor, textDirection: TextDirection.ltr),
icon: const Icon(Icons.share,
color: blueColor, textDirection: TextDirection.ltr),
onPressed: () async {
await Share.share(fullPasteUrl);
})
]),
subtitle: Text(!paste.isMulti! ? paste.filename! : '', style: const TextStyle(fontStyle: FontStyle.italic)),
subtitle: Text(!paste.isMulti! ? paste.filename! : '',
style: const TextStyle(fontStyle: FontStyle.italic)),
),
));
}
@ -190,7 +196,8 @@ class HistoryView extends StatelessWidget {
Widget _renderOpenInBrowser(HistoryModel model, String url) {
return IconButton(
icon: const Icon(Icons.open_in_new, color: blueColor, textDirection: TextDirection.ltr),
icon: const Icon(Icons.open_in_new,
color: blueColor, textDirection: TextDirection.ltr),
onPressed: () {
return model.openLink(url);
});

View file

@ -16,7 +16,9 @@ class HomeView extends StatelessWidget {
return BaseView<HomeModel>(
builder: (context, model, child) => Scaffold(
appBar: MyAppBar(title: Text(translate('app.title'))),
body: model.state == ViewState.busy ? const Center(child: CircularProgressIndicator()) : Container()),
body: model.state == ViewState.busy
? const Center(child: CircularProgressIndicator())
: Container()),
);
}
}

View file

@ -55,13 +55,18 @@ class LoginView extends StatelessWidget {
child: const Icon(Icons.help),
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();
},
@ -86,7 +91,8 @@ class LoginView extends StatelessWidget {
onPressed: () async {
var loginSuccess = await model.login();
if (loginSuccess) {
_navigationService.navigateAndReplaceTo(HomeView.routeName);
_navigationService
.navigateAndReplaceTo(HomeView.routeName);
}
},
)

View file

@ -14,7 +14,8 @@ class AuthenticatedNavBarView extends StatefulWidget {
AuthenticatedNavBarState createState() => AuthenticatedNavBarState();
}
class AuthenticatedNavBarState extends State<AuthenticatedNavBarView> with SingleTickerProviderStateMixin {
class AuthenticatedNavBarState extends State<AuthenticatedNavBarView>
with SingleTickerProviderStateMixin {
final Logger _logger = getLogger();
int _currentTabIndex = 0;

View file

@ -20,8 +20,9 @@ class ProfileView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BaseView<ProfileModel>(
builder: (context, model, child) =>
Scaffold(appBar: MyAppBar(title: Text(translate('titles.profile'))), body: _render(model, context)));
builder: (context, model, child) => Scaffold(
appBar: MyAppBar(title: Text(translate('titles.profile'))),
body: _render(model, context)));
}
Widget _render(ProfileModel model, BuildContext context) {

View file

@ -22,7 +22,9 @@ class StartUpView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const CircularProgressIndicator(),
(model.stateMessage!.isNotEmpty ? Text(model.stateMessage!) : Container())
(model.stateMessage!.isNotEmpty
? Text(model.stateMessage!)
: Container())
]))
: Container()));
}

View file

@ -11,7 +11,8 @@ class TabBarContainerView extends StatelessWidget {
@override
Widget build(BuildContext context) {
Session? currentSession = Provider.of<Session?>(context);
bool isAuthenticated = currentSession != null ? currentSession.apiKey.isNotEmpty : false;
bool isAuthenticated =
currentSession != null ? currentSession.apiKey.isNotEmpty : false;
if (isAuthenticated) {
return const AuthenticatedNavBarView();

View file

@ -196,9 +196,9 @@ class UploadView extends StatelessWidget {
: model.fileName ?? '...');
final size = model.paths!.isNotEmpty
? model.paths!
.map((e) => e.size)
.toList()[index]
.toString()
.map((e) => e.size)
.toList()[index]
.toString()
: '';
final path = model.paths!.isNotEmpty
? model.paths!

View file

@ -20,7 +20,10 @@ class CenteredErrorRow extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(child: Center(child: Text(message!, style: const TextStyle(color: redColor)))),
Expanded(
child: Center(
child: Text(message!,
style: const TextStyle(color: redColor)))),
],
),
(retryCallback != null

View file

@ -11,13 +11,19 @@ class LoginApiKeyHeaders extends StatelessWidget {
final String? validationMessage;
const LoginApiKeyHeaders(
{super.key, required this.uriController, required this.apiKeyController, this.validationMessage});
{super.key,
required this.uriController,
required this.apiKeyController,
this.validationMessage});
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
validationMessage != null ? Text(validationMessage!, style: const TextStyle(color: redColor)) : Container(),
LoginTextField(uriController, translate('login.url_placeholder'), const Icon(Icons.link),
validationMessage != null
? Text(validationMessage!, style: const TextStyle(color: redColor))
: Container(),
LoginTextField(uriController, translate('login.url_placeholder'),
const Icon(Icons.link),
keyboardType: TextInputType.url),
LoginTextField(
apiKeyController,

View file

@ -21,12 +21,17 @@ class LoginCredentialsHeaders extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
validationMessage != null ? Text(validationMessage!, style: const TextStyle(color: redColor)) : Container(),
LoginTextField(uriController, translate('login.url_placeholder'), const Icon(Icons.link),
validationMessage != null
? Text(validationMessage!, style: const TextStyle(color: redColor))
: Container(),
LoginTextField(uriController, translate('login.url_placeholder'),
const Icon(Icons.link),
keyboardType: TextInputType.url),
LoginTextField(usernameController, translate('login.username_placeholder'), const Icon(Icons.person),
LoginTextField(usernameController,
translate('login.username_placeholder'), const Icon(Icons.person),
keyboardType: TextInputType.name),
LoginTextField(passwordController, translate('login.password_placeholder'), const Icon(Icons.vpn_key),
LoginTextField(passwordController,
translate('login.password_placeholder'), const Icon(Icons.vpn_key),
obscureText: true),
]);
}

View file

@ -8,7 +8,9 @@ class LoginTextField extends StatelessWidget {
final Widget prefixIcon;
const LoginTextField(this.controller, this.placeHolder, this.prefixIcon,
{super.key, this.keyboardType = TextInputType.text, this.obscureText = false});
{super.key,
this.keyboardType = TextInputType.text,
this.obscureText = false});
@override
Widget build(BuildContext context) {
@ -28,7 +30,8 @@ class LoginTextField extends StatelessWidget {
prefixIcon: prefixIcon,
hintText: placeHolder,
contentPadding: const EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)),
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)),
),
controller: controller),
);

View file

@ -6,10 +6,18 @@ class MyAppBar extends AppBar {
static final List<Widget> aboutEnabledWidgets = [AboutIconButton()];
static final List<Widget> aboutDisabledWidgets = [];
MyAppBar({Key? key, required Widget title, List<Widget>? actionWidgets, bool enableAbout = true})
: super(key: key, title: Row(children: <Widget>[title]), actions: _renderIconButtons(actionWidgets, enableAbout));
MyAppBar(
{Key? key,
required Widget title,
List<Widget>? actionWidgets,
bool enableAbout = true})
: super(
key: key,
title: Row(children: <Widget>[title]),
actions: _renderIconButtons(actionWidgets, enableAbout));
static List<Widget> _renderIconButtons(List<Widget>? actionWidgets, bool aboutEnabled) {
static List<Widget> _renderIconButtons(
List<Widget>? actionWidgets, bool aboutEnabled) {
actionWidgets ??= [];
List<Widget> widgets = [...actionWidgets];