Compare commits

..

No commits in common. "a65c7d92531e070b6c57339ab2fafd9102cb7105" and "f9a2bb0df723dc13e65cb7b8c39657997c486005" have entirely different histories.

47 changed files with 256 additions and 322 deletions

View file

@ -1,7 +1,7 @@
# CHANGELOG # CHANGELOG
## 1.5.2+18 ## 1.5.2+18
* Added proper linting to project * ...
## 1.5.1+17 ## 1.5.1+17
* Fixed white background button in AppBar when light theme enabled * Fixed white background button in AppBar when light theme enabled

View file

@ -1,30 +0,0 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
library_private_types_in_public_api: false
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View file

@ -21,7 +21,7 @@ class MyApp extends StatelessWidget {
static final _defaultLightColorScheme = ColorScheme.fromSwatch(primarySwatch: myColor, brightness: Brightness.light); static final _defaultLightColorScheme = ColorScheme.fromSwatch(primarySwatch: myColor, brightness: Brightness.light);
static final _defaultDarkColorScheme = ColorScheme.fromSwatch(primarySwatch: myColor, brightness: Brightness.dark); static final _defaultDarkColorScheme = ColorScheme.fromSwatch(primarySwatch: myColor, brightness: Brightness.dark);
MyApp({super.key}) { MyApp() {
initializeDateFormatting('en'); initializeDateFormatting('en');
} }
@ -52,7 +52,7 @@ class MyApp extends StatelessWidget {
darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme ?? _defaultDarkColorScheme), darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme ?? _defaultDarkColorScheme),
onGenerateRoute: AppRouter.generateRoute, onGenerateRoute: AppRouter.generateRoute,
navigatorKey: locator<NavigationService>().navigationKey, navigatorKey: locator<NavigationService>().navigationKey,
home: const StartUpView(), home: StartUpView(),
supportedLocales: localizationDelegate.supportedLocales, supportedLocales: localizationDelegate.supportedLocales,
locale: localizationDelegate.currentLocale, locale: localizationDelegate.currentLocale,
); );

View file

@ -1,15 +1,15 @@
/// Enums for error codes /// Enums for error codes
enum ErrorCode { enum ErrorCode {
/// A generic error /// A generic error
generalError, GENERAL_ERROR,
/// Errors related to connections /// Errors related to connections
socketError, SOCKET_ERROR,
socketTimeout, SOCKET_TIMEOUT,
/// A REST error (response code wasn't 200 or 204) /// A REST error (response code wasn't 200 or 204)
restError, REST_ERROR,
/// Custom errors /// Custom errors
invalidApiKey INVALID_API_KEY
} }

View file

@ -1 +1 @@
enum RefreshEvent { refreshHistory } enum RefreshEvent { RefreshHistory }

View file

@ -1 +1 @@
enum ViewState { idle, busy } enum ViewState { Idle, Busy }

View file

@ -6,9 +6,8 @@ class RestServiceException extends ServiceException {
final dynamic responseBody; final dynamic responseBody;
RestServiceException(this.statusCode, {this.responseBody, String? message}) RestServiceException(this.statusCode, {this.responseBody, String? message})
: super(code: ErrorCode.restError, message: message); : super(code: ErrorCode.REST_ERROR, message: message);
@override
String toString() { String toString() {
return "$code $statusCode $message"; return "$code $statusCode $message";
} }

View file

@ -4,9 +4,8 @@ class ServiceException implements Exception {
final ErrorCode code; final ErrorCode code;
final String? message; final String? message;
ServiceException({this.code = ErrorCode.generalError, this.message = ''}); ServiceException({this.code = ErrorCode.GENERAL_ERROR, this.message = ''});
@override
String toString() { String toString() {
return "$code: $message"; return "$code: $message";
} }

View file

@ -8,14 +8,13 @@ import '../services/dialog_service.dart';
class DialogManager extends StatefulWidget { class DialogManager extends StatefulWidget {
final Widget? child; final Widget? child;
const DialogManager({Key? key, this.child}) : super(key: key); DialogManager({Key? key, this.child}) : super(key: key);
@override
_DialogManagerState createState() => _DialogManagerState(); _DialogManagerState createState() => _DialogManagerState();
} }
class _DialogManagerState extends State<DialogManager> { class _DialogManagerState extends State<DialogManager> {
final DialogService _dialogService = locator<DialogService>(); DialogService _dialogService = locator<DialogService>();
@override @override
void initState() { void initState() {

View file

@ -11,9 +11,8 @@ import '../util/logger.dart';
class LifeCycleManager extends StatefulWidget { class LifeCycleManager extends StatefulWidget {
final Widget? child; final Widget? child;
const LifeCycleManager({Key? key, this.child}) : super(key: key); LifeCycleManager({Key? key, this.child}) : super(key: key);
@override
_LifeCycleManagerState createState() => _LifeCycleManagerState(); _LifeCycleManagerState createState() => _LifeCycleManagerState();
} }
@ -44,12 +43,12 @@ class _LifeCycleManagerState extends State<LifeCycleManager> with WidgetsBinding
logger.d('LifeCycle event ${state.toString()}'); logger.d('LifeCycle event ${state.toString()}');
super.didChangeAppLifecycleState(state); super.didChangeAppLifecycleState(state);
for (var service in servicesToManage) { servicesToManage.forEach((service) {
if (state == AppLifecycleState.resumed) { if (state == AppLifecycleState.resumed) {
service.start(); service.start();
} else { } else {
service.stop(); service.stop();
} }
} });
} }
} }

View file

@ -11,7 +11,7 @@ import '../models/rest/uploaded_response.dart';
import '../services/api.dart'; import '../services/api.dart';
class FileRepository { class FileRepository {
final Api _api = locator<Api>(); Api _api = locator<Api>();
Future<History> getHistory() async { Future<History> getHistory() async {
var response = await _api.post('/file/history'); var response = await _api.post('/file/history');
@ -39,11 +39,11 @@ class FileRepository {
} }
Future<UploadedMultiResponse> postCreateMultiPaste(List<String> ids) async { Future<UploadedMultiResponse> postCreateMultiPaste(List<String> ids) async {
Map<String, String> multiPasteIds = {}; Map<String, String> multiPasteIds = Map();
for (var element in ids) { 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)); return UploadedMultiResponse.fromJson(json.decode(response.body));

View file

@ -6,7 +6,7 @@ import '../models/rest/create_apikey_response.dart';
import '../services/api.dart'; import '../services/api.dart';
class UserRepository { class UserRepository {
final Api _api = locator<Api>(); Api _api = locator<Api>();
Future<CreateApiKeyResponse> postApiKey( Future<CreateApiKeyResponse> postApiKey(
String url, String username, String password, String accessLevel, String comment) async { String url, String username, String password, String accessLevel, String comment) async {

View file

@ -24,8 +24,8 @@ class Api implements ApiErrorConverter {
String _url = ""; String _url = "";
String _apiKey = ""; String _apiKey = "";
final Map<String, String> _headers = {"Content-Type": _applicationJson, "Accept": _applicationJson}; Map<String, String> _headers = {"Content-Type": _applicationJson, "Accept": _applicationJson};
Duration _timeout = const Duration(seconds: Constants.apiRequestTimeoutLimit); Duration _timeout = Duration(seconds: Constants.apiRequestTimeoutLimit);
Future<http.Response> fetch<T>(String route) async { Future<http.Response> fetch<T>(String route) async {
try { try {
@ -35,9 +35,9 @@ class Api implements ApiErrorConverter {
handleRestErrors(response); handleRestErrors(response);
return response; return response;
} on TimeoutException { } on TimeoutException {
throw ServiceException(code: ErrorCode.socketTimeout, message: _errorTimeout); throw ServiceException(code: ErrorCode.SOCKET_TIMEOUT, message: _errorTimeout);
} on SocketException { } on SocketException {
throw ServiceException(code: ErrorCode.socketError, message: _errorNoConnection); throw ServiceException(code: ErrorCode.SOCKET_ERROR, message: _errorNoConnection);
} }
} }
@ -58,12 +58,12 @@ class Api implements ApiErrorConverter {
} }
if (files != null && files.isNotEmpty) { if (files != null && files.isNotEmpty) {
for (var element in files) { 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.isNotEmpty) { if (additionalFiles != null && additionalFiles.length > 0) {
List<String> keys = additionalFiles.keys.toList(); List<String> keys = additionalFiles.keys.toList();
additionalFiles.forEach((key, value) { additionalFiles.forEach((key, value) {
var index = files != null ? files.length + keys.indexOf(key) + 1 : keys.indexOf(key) + 1; var index = files != null ? files.length + keys.indexOf(key) + 1 : keys.indexOf(key) + 1;
@ -77,9 +77,9 @@ class Api implements ApiErrorConverter {
handleRestErrors(response); handleRestErrors(response);
return response; return response;
} on TimeoutException { } on TimeoutException {
throw ServiceException(code: ErrorCode.socketTimeout, message: _errorTimeout); throw ServiceException(code: ErrorCode.SOCKET_TIMEOUT, message: _errorTimeout);
} on SocketException { } on SocketException {
throw ServiceException(code: ErrorCode.socketError, message: _errorNoConnection); throw ServiceException(code: ErrorCode.SOCKET_ERROR, message: _errorNoConnection);
} }
} }
@ -114,11 +114,11 @@ class Api implements ApiErrorConverter {
if (ContentType.json.primaryType == responseContentType.primaryType && if (ContentType.json.primaryType == responseContentType.primaryType &&
ContentType.json.subType == responseContentType.subType) { ContentType.json.subType == responseContentType.subType) {
var parsedBody = convert(response); var parsedBody = convert(response);
throw RestServiceException(response.statusCode, responseBody: parsedBody); throw new RestServiceException(response.statusCode, responseBody: parsedBody);
} }
} }
throw RestServiceException(response.statusCode); throw new RestServiceException(response.statusCode);
} }
} }

View file

@ -7,7 +7,7 @@ import '../datamodels/dialog_request.dart';
import '../datamodels/dialog_response.dart'; import '../datamodels/dialog_response.dart';
class DialogService { class DialogService {
final GlobalKey<NavigatorState> _dialogNavigationKey = GlobalKey<NavigatorState>(); GlobalKey<NavigatorState> _dialogNavigationKey = GlobalKey<NavigatorState>();
late Function(DialogRequest) _showDialogListener; late Function(DialogRequest) _showDialogListener;
Completer<DialogResponse>? _dialogCompleter; Completer<DialogResponse>? _dialogCompleter;

View file

@ -4,7 +4,7 @@ import 'package:logger/logger.dart';
import '../util/logger.dart'; import '../util/logger.dart';
class NavigationService { class NavigationService {
final GlobalKey<NavigatorState> _navigationKey = GlobalKey<NavigatorState>(); GlobalKey<NavigatorState> _navigationKey = GlobalKey<NavigatorState>();
GlobalKey<NavigatorState> get navigationKey => _navigationKey; GlobalKey<NavigatorState> get navigationKey => _navigationKey;

View file

@ -105,11 +105,11 @@ class PermissionService extends StoppableService {
await checkEnabledAndPermission(); await checkEnabledAndPermission();
_serviceCheckTimer = _serviceCheckTimer =
Timer.periodic(const Duration(milliseconds: Constants.mediaPermissionCheckInterval), (serviceTimer) async { Timer.periodic(Duration(milliseconds: Constants.mediaPermissionCheckInterval), (_serviceTimer) async {
if (!super.serviceStopped) { if (!super.serviceStopped) {
await checkEnabledAndPermission(); await checkEnabledAndPermission();
} else { } else {
serviceTimer.cancel(); _serviceTimer.cancel();
} }
}); });
_logger.d('PermissionService started'); _logger.d('PermissionService started');

View file

@ -31,7 +31,7 @@ class SessionService extends StoppableService {
Future<bool> login(String url, String apiKey) async { Future<bool> login(String url, String apiKey) async {
setApiConfig(url, apiKey); setApiConfig(url, apiKey);
var session = Session(url: url, apiKey: apiKey); var session = new Session(url: url, apiKey: apiKey);
sessionController.add(session); sessionController.add(session);
await _storageService.storeSession(session); await _storageService.storeSession(session);
_logger.d('Session created'); _logger.d('Session created');

View file

@ -5,45 +5,45 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../models/session.dart'; import '../models/session.dart';
class StorageService { class StorageService {
static const _sessionKey = 'session'; static const _SESSION_KEY = 'session';
static const _lastUrlKey = 'last_url'; static const _LAST_URL_KEY = 'last_url';
static const _storagePermissionDialogIgnoredKey = 'storage_permission_ignored'; static const _STORAGE_PERMISSION_DIALOG_IGNORED = 'storage_permission_ignored';
Future<bool> storeLastUrl(String url) { Future<bool> storeLastUrl(String url) {
return _store(_lastUrlKey, url); return _store(_LAST_URL_KEY, url);
} }
Future<String?> retrieveLastUrl() async { Future<String?> retrieveLastUrl() async {
return await _retrieve(_lastUrlKey); return await _retrieve(_LAST_URL_KEY);
} }
Future<bool> hasLastUrl() async { Future<bool> hasLastUrl() async {
return await _exists(_lastUrlKey); return await _exists(_LAST_URL_KEY);
} }
Future<bool> storeSession(Session session) { Future<bool> storeSession(Session session) {
return _store(_sessionKey, json.encode(session)); return _store(_SESSION_KEY, json.encode(session));
} }
Future<Session> retrieveSession() async { Future<Session> retrieveSession() async {
var retrieve = await _retrieve(_sessionKey); var retrieve = await _retrieve(_SESSION_KEY);
return Session.fromJson(json.decode(retrieve!)); return Session.fromJson(json.decode(retrieve!));
} }
Future<bool> hasSession() { Future<bool> hasSession() {
return _exists(_sessionKey); return _exists(_SESSION_KEY);
} }
Future<bool> removeSession() { Future<bool> removeSession() {
return _remove(_sessionKey); return _remove(_SESSION_KEY);
} }
Future<bool> storeStoragePermissionDialogIgnored() { Future<bool> storeStoragePermissionDialogIgnored() {
return _store(_storagePermissionDialogIgnoredKey, true.toString()); return _store(_STORAGE_PERMISSION_DIALOG_IGNORED, true.toString());
} }
Future<bool> hasStoragePermissionDialogIgnored() { Future<bool> hasStoragePermissionDialogIgnored() {
return _exists(_storagePermissionDialogIgnoredKey); return _exists(_STORAGE_PERMISSION_DIALOG_IGNORED);
} }
Future<bool> _exists(String key) async { Future<bool> _exists(String key) async {

View file

@ -26,7 +26,7 @@ class UserService {
try { try {
await _fileService.getHistory(); await _fileService.getHistory();
} on ServiceException catch (e) { } on ServiceException catch (e) {
throw ServiceException(code: ErrorCode.invalidApiKey, message: e.message); throw new ServiceException(code: ErrorCode.INVALID_API_KEY, message: e.message);
} }
} }
} }

View file

@ -13,6 +13,6 @@ class FormatterUtil {
if (bytes <= 0) return "0 B"; if (bytes <= 0) return "0 B";
const suffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; const suffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
var i = (log(bytes) / log(1024)).floor(); var i = (log(bytes) / log(1024)).floor();
return '${(bytes / pow(1024, i)).toStringAsFixed(decimals)} ${suffixes[i]}'; return ((bytes / pow(1024, i)).toStringAsFixed(decimals)) + ' ' + suffixes[i];
} }
} }

View file

@ -20,10 +20,10 @@ class AboutModel extends BaseModel {
} }
Future<void> _initPackageInfo() async { Future<void> _initPackageInfo() async {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
final PackageInfo info = await PackageInfo.fromPlatform(); final PackageInfo info = await PackageInfo.fromPlatform();
packageInfo = info; packageInfo = info;
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
void openLink(String link) { void openLink(String link) {

View file

@ -5,18 +5,18 @@ import '../../core/util/logger.dart';
import '../enums/viewstate.dart'; import '../enums/viewstate.dart';
class BaseModel extends ChangeNotifier { class BaseModel extends ChangeNotifier {
static const String stateViewKey = 'viewState'; static const String STATE_VIEW = 'viewState';
static const String stateMessageKey = 'viewMessage'; static const String STATE_MESSAGE = 'viewMessage';
final Logger _logger = getLogger(); final Logger _logger = getLogger();
bool _isDisposed = false; bool _isDisposed = false;
final Map<String, Object?> _stateMap = {stateViewKey: ViewState.idle, stateMessageKey: null}; Map<String, Object?> _stateMap = {STATE_VIEW: ViewState.Idle, STATE_MESSAGE: null};
ViewState? get state => _stateMap[stateViewKey] as ViewState?; ViewState? get state => _stateMap[STATE_VIEW] as ViewState?;
String? get stateMessage => _stateMap[stateMessageKey] as String?; String? get stateMessage => _stateMap[STATE_MESSAGE] as String?;
bool getStateValueAsBoolean(String key) { bool getStateValueAsBoolean(String key) {
if (_stateMap.containsKey(key) && _stateMap[key] is bool) { if (_stateMap.containsKey(key) && _stateMap[key] is bool) {
@ -71,11 +71,11 @@ class BaseModel extends ChangeNotifier {
} }
void setStateView(ViewState stateView) { void setStateView(ViewState stateView) {
_setStateValue(stateViewKey, stateView); _setStateValue(STATE_VIEW, stateView);
} }
void setStateMessage(String? stateMessage) { void setStateMessage(String? stateMessage) {
_setStateValue(stateMessageKey, stateMessage); _setStateValue(STATE_MESSAGE, stateMessage);
} }
@override @override

View file

@ -35,7 +35,7 @@ class HistoryModel extends BaseModel {
void init() { void init() {
_refreshTriggerSubscription = _refreshService.refreshEventController.stream.listen((event) { _refreshTriggerSubscription = _refreshService.refreshEventController.stream.listen((event) {
if (event == RefreshEvent.refreshHistory) { if (event == RefreshEvent.RefreshHistory) {
_logger.d('History needs a refresh'); _logger.d('History needs a refresh');
getHistory(); getHistory();
} }
@ -43,13 +43,13 @@ class HistoryModel extends BaseModel {
} }
Future getHistory() async { Future getHistory() async {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
try { try {
pastes.clear(); pastes.clear();
History history = await _fileService.getHistory(); History _history = await _fileService.getHistory();
if (history.items.isNotEmpty) { if (_history.items.isNotEmpty) {
history.items.forEach((key, value) { _history.items.forEach((key, value) {
var millisecondsSinceEpoch = int.parse(value.date) * 1000; var millisecondsSinceEpoch = int.parse(value.date) * 1000;
pastes.add( pastes.add(
UploadedPaste( UploadedPaste(
@ -66,8 +66,8 @@ class HistoryModel extends BaseModel {
}); });
} }
if (history.multipasteItems.isNotEmpty) { if (_history.multipasteItems.isNotEmpty) {
history.multipasteItems.forEach((key, multiPaste) { _history.multipasteItems.forEach((key, multiPaste) {
var millisecondsSinceEpoch = int.parse(multiPaste.date) * 1000; var millisecondsSinceEpoch = int.parse(multiPaste.date) * 1000;
pastes.add(UploadedPaste( pastes.add(UploadedPaste(
id: key, id: key,
@ -97,19 +97,19 @@ class HistoryModel extends BaseModel {
} else { } else {
errorMessage = translate('api.general_rest_error'); errorMessage = translate('api.general_rest_error');
} }
} else if (e is ServiceException && e.code == ErrorCode.socketError) { } else if (e is ServiceException && e.code == ErrorCode.SOCKET_ERROR) {
errorMessage = translate('api.socket_error'); errorMessage = translate('api.socket_error');
} else if (e is ServiceException && e.code == ErrorCode.socketTimeout) { } else if (e is ServiceException && e.code == ErrorCode.SOCKET_TIMEOUT) {
errorMessage = translate('api.socket_timeout'); errorMessage = translate('api.socket_timeout');
} else { } else {
errorMessage = translate('app.unknown_error'); errorMessage = translate('app.unknown_error');
setStateView(ViewState.idle); setStateView(ViewState.Idle);
_logger.e('An unknown error occurred', e); _logger.e('An unknown error occurred', e);
rethrow; throw e;
} }
} }
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
Future deletePaste(String id) async { Future deletePaste(String id) async {
@ -123,7 +123,7 @@ class HistoryModel extends BaseModel {
return; return;
} }
setStateView(ViewState.busy); setStateView(ViewState.Busy);
try { try {
await _fileService.deletePaste(id); await _fileService.deletePaste(id);
@ -143,19 +143,19 @@ class HistoryModel extends BaseModel {
} else { } else {
errorMessage = translate('api.general_rest_error'); errorMessage = translate('api.general_rest_error');
} }
} else if (e is ServiceException && e.code == ErrorCode.socketError) { } else if (e is ServiceException && e.code == ErrorCode.SOCKET_ERROR) {
errorMessage = translate('api.socket_error'); errorMessage = translate('api.socket_error');
} else if (e is ServiceException && e.code == ErrorCode.socketTimeout) { } else if (e is ServiceException && e.code == ErrorCode.SOCKET_TIMEOUT) {
errorMessage = translate('api.socket_timeout'); errorMessage = translate('api.socket_timeout');
} else { } else {
errorMessage = translate('app.unknown_error'); errorMessage = translate('app.unknown_error');
setStateView(ViewState.idle); setStateView(ViewState.Idle);
_logger.e('An unknown error occurred', e); _logger.e('An unknown error occurred', e);
rethrow; throw e;
} }
} }
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
void openLink(String link) { void openLink(String link) {

View file

@ -20,12 +20,12 @@ import '../util/logger.dart';
import 'base_model.dart'; import 'base_model.dart';
class LoginModel extends BaseModel { class LoginModel extends BaseModel {
TextEditingController _uriController = TextEditingController(); TextEditingController _uriController = new TextEditingController();
final TextEditingController _userNameController = TextEditingController(); final TextEditingController _userNameController = new TextEditingController();
final TextEditingController _passwordController = TextEditingController(); final TextEditingController _passwordController = new TextEditingController();
final TextEditingController _apiKeyController = TextEditingController(); final TextEditingController _apiKeyController = new TextEditingController();
TextEditingController get uriController => _uriController; TextEditingController get uriController => _uriController;
@ -44,23 +44,23 @@ class LoginModel extends BaseModel {
String? errorMessage; String? errorMessage;
void toggleLoginMethod() { void toggleLoginMethod() {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
useCredentialsLogin = !useCredentialsLogin; useCredentialsLogin = !useCredentialsLogin;
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
void init() async { void init() async {
bool hasLastUrl = await _storageService.hasLastUrl(); bool hasLastUrl = await _storageService.hasLastUrl();
if (hasLastUrl) { if (hasLastUrl) {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
var s = await (_storageService.retrieveLastUrl() as FutureOr<String>); var s = await (_storageService.retrieveLastUrl() as FutureOr<String>);
if (s.isNotEmpty) { if (s.isNotEmpty) {
_uriController = TextEditingController(text: s); _uriController = new TextEditingController(text: s);
} }
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
} }
@ -70,45 +70,45 @@ class LoginModel extends BaseModel {
var password = passwordController.text; var password = passwordController.text;
var apiKey = apiKeyController.text; var apiKey = apiKeyController.text;
setStateView(ViewState.busy); setStateView(ViewState.Busy);
url = trim(url); url = trim(url);
username = trim(username); username = trim(username);
if (url.isEmpty) { if (url.isEmpty) {
errorMessage = translate('login.errors.empty_url'); errorMessage = translate('login.errors.empty_url');
setStateView(ViewState.idle); setStateView(ViewState.Idle);
return false; return false;
} }
if (!url.contains("https://") && !url.contains("http://")) { if (!url.contains("https://") && !url.contains("http://")) {
errorMessage = translate('login.errors.no_protocol'); errorMessage = translate('login.errors.no_protocol');
setStateView(ViewState.idle); setStateView(ViewState.Idle);
return false; return false;
} }
bool validUri = Uri.parse(url).isAbsolute; bool validUri = Uri.parse(url).isAbsolute;
if (!validUri || !isURL(url)) { if (!validUri || !isURL(url)) {
errorMessage = translate('login.errors.invalid_url'); errorMessage = translate('login.errors.invalid_url');
setStateView(ViewState.idle); setStateView(ViewState.Idle);
return false; return false;
} }
if (useCredentialsLogin) { if (useCredentialsLogin) {
if (username.isEmpty) { if (username.isEmpty) {
errorMessage = translate('login.errors.empty_username'); errorMessage = translate('login.errors.empty_username');
setStateView(ViewState.idle); setStateView(ViewState.Idle);
return false; return false;
} }
if (password.isEmpty) { if (password.isEmpty) {
errorMessage = translate('login.errors.empty_password'); errorMessage = translate('login.errors.empty_password');
setStateView(ViewState.idle); setStateView(ViewState.Idle);
return false; return false;
} }
} else { } else {
if (apiKey.isEmpty) { if (apiKey.isEmpty) {
errorMessage = translate('login.errors.empty_apikey'); errorMessage = translate('login.errors.empty_apikey');
setStateView(ViewState.idle); setStateView(ViewState.Idle);
return false; return false;
} }
} }
@ -117,13 +117,14 @@ class LoginModel extends BaseModel {
try { try {
if (useCredentialsLogin) { if (useCredentialsLogin) {
CreateApiKeyResponse apiKeyResponse = await _userService.createApiKey( CreateApiKeyResponse apiKeyResponse = await _userService.createApiKey(
url, username, password, 'apikey', 'fbmobile-${DateTime.now().millisecondsSinceEpoch}'); url, username, password, 'apikey', 'fbmobile-${new DateTime.now().millisecondsSinceEpoch}');
var newKey = apiKeyResponse.data['new_key']; var newKey = apiKeyResponse.data['new_key'];
if (newKey != null) { if (newKey != null) {
success = await _sessionService.login(url, newKey); success = await _sessionService.login(url, newKey);
} else { } else {
throw ServiceException(code: ErrorCode.invalidApiKey, message: translate('login.errors.invalid_api_key')); throw new ServiceException(
code: ErrorCode.INVALID_API_KEY, message: translate('login.errors.invalid_api_key'));
} }
} else { } else {
_sessionService.setApiConfig(url, apiKey); _sessionService.setApiConfig(url, apiKey);
@ -145,25 +146,25 @@ class LoginModel extends BaseModel {
} else { } else {
errorMessage = translate('api.general_rest_error'); errorMessage = translate('api.general_rest_error');
} }
} else if (e is ServiceException && e.code == ErrorCode.invalidApiKey) { } else if (e is ServiceException && e.code == ErrorCode.INVALID_API_KEY) {
errorMessage = translate('login.errors.invalid_api_key'); errorMessage = translate('login.errors.invalid_api_key');
} else if (e is ServiceException && e.code == ErrorCode.socketError) { } else if (e is ServiceException && e.code == ErrorCode.SOCKET_ERROR) {
errorMessage = translate('api.socket_error'); errorMessage = translate('api.socket_error');
} else if (e is ServiceException && e.code == ErrorCode.socketTimeout) { } else if (e is ServiceException && e.code == ErrorCode.SOCKET_TIMEOUT) {
errorMessage = translate('api.socket_timeout'); errorMessage = translate('api.socket_timeout');
} else { } else {
errorMessage = translate('app.unknown_error'); errorMessage = translate('app.unknown_error');
_sessionService.logout(); _sessionService.logout();
setStateView(ViewState.idle); setStateView(ViewState.Idle);
_logger.e('An unknown error occurred', e); _logger.e('An unknown error occurred', e);
rethrow; throw e;
} }
if (errorMessage!.isNotEmpty) { if (errorMessage!.isNotEmpty) {
_sessionService.logout(); _sessionService.logout();
} }
setStateView(ViewState.idle); setStateView(ViewState.Idle);
return success; return success;
} }

View file

@ -64,9 +64,9 @@ class ProfileModel extends BaseModel {
} else { } else {
errorMessage = translate('api.general_rest_error'); errorMessage = translate('api.general_rest_error');
} }
} else if (e is ServiceException && e.code == ErrorCode.socketError) { } else if (e is ServiceException && e.code == ErrorCode.SOCKET_ERROR) {
errorMessage = translate('api.socket_error'); errorMessage = translate('api.socket_error');
} else if (e is ServiceException && e.code == ErrorCode.socketTimeout) { } else if (e is ServiceException && e.code == ErrorCode.SOCKET_TIMEOUT) {
errorMessage = translate('api.socket_timeout'); errorMessage = translate('api.socket_timeout');
} else { } else {
errorMessage = translate('app.unknown_error'); errorMessage = translate('app.unknown_error');
@ -74,7 +74,7 @@ class ProfileModel extends BaseModel {
_sessionService.logout(); _sessionService.logout();
setStateBoolValue(_configurationButtonLoading, false); setStateBoolValue(_configurationButtonLoading, false);
_logger.e('An unknown error occurred', e); _logger.e('An unknown error occurred', e);
rethrow; throw e;
} }
} }

View file

@ -12,16 +12,16 @@ class StartUpViewModel extends BaseModel {
final NavigationService _navigationService = locator<NavigationService>(); final NavigationService _navigationService = locator<NavigationService>();
Future handleStartUpLogic() async { Future handleStartUpLogic() async {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
setStateMessage(translate('startup.init')); setStateMessage(translate('startup.init'));
await Future.delayed(const Duration(milliseconds: 150)); await Future.delayed(Duration(milliseconds: 150));
setStateMessage(translate('startup.start_services')); setStateMessage(translate('startup.start_services'));
await _sessionService.start(); await _sessionService.start();
await Future.delayed(const Duration(milliseconds: 150)); await Future.delayed(Duration(milliseconds: 150));
_navigationService.navigateAndReplaceTo(HomeView.routeName); _navigationService.navigateAndReplaceTo(HomeView.routeName);
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
} }

View file

@ -31,7 +31,7 @@ class UploadModel extends BaseModel {
final LinkService _linkService = locator<LinkService>(); final LinkService _linkService = locator<LinkService>();
final RefreshService _refreshService = locator<RefreshService>(); final RefreshService _refreshService = locator<RefreshService>();
final TextEditingController _pasteTextController = TextEditingController(); TextEditingController _pasteTextController = TextEditingController();
bool pasteTextTouched = false; bool pasteTextTouched = false;
late StreamSubscription _intentDataStreamSubscription; late StreamSubscription _intentDataStreamSubscription;
@ -53,8 +53,8 @@ class UploadModel extends BaseModel {
// For sharing images coming from outside the app while the app is in the memory // For sharing images coming from outside the app while the app is in the memory
_intentDataStreamSubscription = ReceiveSharingIntent.getMediaStream().listen((List<SharedMediaFile> value) { _intentDataStreamSubscription = ReceiveSharingIntent.getMediaStream().listen((List<SharedMediaFile> value) {
if (value.isNotEmpty) { if (value.length > 0) {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
paths = value.map((sharedFile) { paths = value.map((sharedFile) {
return PlatformFile.fromMap({ return PlatformFile.fromMap({
'path': sharedFile.path, 'path': sharedFile.path,
@ -63,7 +63,7 @@ class UploadModel extends BaseModel {
'bytes': null 'bytes': null
}); });
}).toList(); }).toList();
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
}, onError: (err) { }, onError: (err) {
_errorIntentHandle(err); _errorIntentHandle(err);
@ -71,8 +71,8 @@ class UploadModel extends BaseModel {
// For sharing images coming from outside the app while the app is closed // For sharing images coming from outside the app while the app is closed
ReceiveSharingIntent.getInitialMedia().then((List<SharedMediaFile> value) { ReceiveSharingIntent.getInitialMedia().then((List<SharedMediaFile> value) {
if (value.isNotEmpty) { if (value.length > 0) {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
paths = value.map((sharedFile) { paths = value.map((sharedFile) {
return PlatformFile.fromMap({ return PlatformFile.fromMap({
'path': sharedFile.path, 'path': sharedFile.path,
@ -81,7 +81,7 @@ class UploadModel extends BaseModel {
'bytes': null 'bytes': null
}); });
}).toList(); }).toList();
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
}, onError: (err) { }, onError: (err) {
_errorIntentHandle(err); _errorIntentHandle(err);
@ -90,9 +90,9 @@ class UploadModel extends BaseModel {
// For sharing or opening urls/text coming from outside the app while the app is in the memory // For sharing or opening urls/text coming from outside the app while the app is in the memory
_intentDataStreamSubscription = ReceiveSharingIntent.getTextStream().listen((String value) { _intentDataStreamSubscription = ReceiveSharingIntent.getTextStream().listen((String value) {
if (value.isNotEmpty) { if (value.isNotEmpty) {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
pasteTextController.text = value; pasteTextController.text = value;
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
}, onError: (err) { }, onError: (err) {
_errorIntentHandle(err); _errorIntentHandle(err);
@ -101,9 +101,9 @@ class UploadModel extends BaseModel {
// For sharing or opening urls/text coming from outside the app while the app is closed // 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) { if (value != null && value.isNotEmpty) {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
pasteTextController.text = value; pasteTextController.text = value;
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
}, onError: (err) { }, onError: (err) {
_errorIntentHandle(err); _errorIntentHandle(err);
@ -111,14 +111,14 @@ class UploadModel extends BaseModel {
} }
void _errorIntentHandle(err) { void _errorIntentHandle(err) {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
errorMessage = translate('upload.retrieval_intent'); errorMessage = translate('upload.retrieval_intent');
_logger.e('Error while retrieving shared data: $err'); _logger.e('Error while retrieving shared data: $err');
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
String? generatePasteLinks(Map<String, bool>? uploads, String url) { String? generatePasteLinks(Map<String, bool>? uploads, String url) {
if (uploads != null && uploads.isNotEmpty) { if (uploads != null && uploads.length > 0) {
var links = ''; var links = '';
uploads.forEach((id, isMulti) { uploads.forEach((id, isMulti) {
@ -134,13 +134,13 @@ class UploadModel extends BaseModel {
} }
void toggleCreateMulti() { void toggleCreateMulti() {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
createMulti = !createMulti; createMulti = !createMulti;
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
void openFileExplorer() async { void openFileExplorer() async {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
setStateMessage(translate('upload.file_explorer_open')); setStateMessage(translate('upload.file_explorer_open'));
loadingPath = true; loadingPath = true;
@ -163,40 +163,40 @@ class UploadModel extends BaseModel {
fileName = paths != null ? paths!.map((e) => e.name).toString() : '...'; fileName = paths != null ? paths!.map((e) => e.name).toString() : '...';
setStateMessage(null); setStateMessage(null);
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
void clearCachedFiles() async { void clearCachedFiles() async {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
await FilePicker.platform.clearTemporaryFiles(); await FilePicker.platform.clearTemporaryFiles();
paths = null; paths = null;
fileName = null; fileName = null;
errorMessage = null; errorMessage = null;
setStateView(ViewState.idle); setStateView(ViewState.Idle);
} }
Future<Map<String, bool>?> upload() async { Future<Map<String, bool>?> upload() async {
setStateView(ViewState.busy); setStateView(ViewState.Busy);
setStateMessage(translate('upload.uploading_now')); setStateMessage(translate('upload.uploading_now'));
Map<String, bool> uploadedPasteIds = {}; Map<String, bool> uploadedPasteIds = new Map();
try { try {
List<File>? files; List<File>? files;
Map<String, String>? additionalFiles; Map<String, String>? additionalFiles;
if (pasteTextController.text.isNotEmpty) { if (pasteTextController.text.isNotEmpty) {
additionalFiles = additionalFiles = Map.from(
Map.from({'paste-${(DateTime.now().millisecondsSinceEpoch / 1000).round()}.txt': pasteTextController.text}); {'paste-${(new DateTime.now().millisecondsSinceEpoch / 1000).round()}.txt': pasteTextController.text});
} }
if (paths != null && paths!.isNotEmpty) { if (paths != null && paths!.length > 0) {
files = paths!.map((e) => File(e.path!)).toList(); files = paths!.map((e) => new File(e.path!)).toList();
} }
UploadedResponse response = await _fileService.uploadPaste(files, additionalFiles); UploadedResponse response = await _fileService.uploadPaste(files, additionalFiles);
for (var element in response.data.ids) { response.data.ids.forEach((element) {
uploadedPasteIds.putIfAbsent(element, () => false); uploadedPasteIds.putIfAbsent(element, () => false);
} });
if (createMulti && response.data.ids.length > 1) { if (createMulti && response.data.ids.length > 1) {
UploadedMultiResponse multiResponse = await _fileService.uploadMultiPaste(response.data.ids); UploadedMultiResponse multiResponse = await _fileService.uploadMultiPaste(response.data.ids);
@ -205,7 +205,7 @@ class UploadModel extends BaseModel {
clearCachedFiles(); clearCachedFiles();
_pasteTextController.clear(); _pasteTextController.clear();
_refreshService.addEvent(RefreshEvent.refreshHistory); _refreshService.addEvent(RefreshEvent.RefreshHistory);
errorMessage = null; errorMessage = null;
return uploadedPasteIds; return uploadedPasteIds;
} catch (e) { } catch (e) {
@ -226,21 +226,21 @@ class UploadModel extends BaseModel {
} else { } else {
errorMessage = translate('api.general_rest_error'); errorMessage = translate('api.general_rest_error');
} }
} else if (e is ServiceException && e.code == ErrorCode.socketError) { } else if (e is ServiceException && e.code == ErrorCode.SOCKET_ERROR) {
errorMessage = translate('api.socket_error'); errorMessage = translate('api.socket_error');
} else if (e is ServiceException && e.code == ErrorCode.socketTimeout) { } else if (e is ServiceException && e.code == ErrorCode.SOCKET_TIMEOUT) {
errorMessage = translate('api.socket_timeout'); errorMessage = translate('api.socket_timeout');
} else { } else {
errorMessage = translate('app.unknown_error'); errorMessage = translate('app.unknown_error');
setStateMessage(null); setStateMessage(null);
setStateView(ViewState.idle); setStateView(ViewState.Idle);
_logger.e('An unknown error occurred', e); _logger.e('An unknown error occurred', e);
rethrow; throw e;
} }
} }
setStateMessage(null); setStateMessage(null);
setStateView(ViewState.idle); setStateView(ViewState.Idle);
return null; return null;
} }

View file

@ -14,15 +14,15 @@ class AppRouter {
static Route<dynamic> generateRoute(RouteSettings settings) { static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) { switch (settings.name) {
case StartUpView.routeName: case StartUpView.routeName:
return MaterialPageRoute(builder: (_) => const StartUpView()); return MaterialPageRoute(builder: (_) => StartUpView());
case AboutView.routeName: case AboutView.routeName:
return MaterialPageRoute(builder: (_) => const AboutView()); return MaterialPageRoute(builder: (_) => AboutView());
case HomeView.routeName: case HomeView.routeName:
return MaterialPageRoute(builder: (_) => const TabBarContainerView()); return MaterialPageRoute(builder: (_) => TabBarContainerView());
case LoginView.routeName: case LoginView.routeName:
return MaterialPageRoute(builder: (_) => LoginView()); return MaterialPageRoute(builder: (_) => LoginView());
case ProfileView.routeName: case ProfileView.routeName:
return MaterialPageRoute(builder: (_) => const ProfileView()); return MaterialPageRoute(builder: (_) => ProfileView());
default: default:
return MaterialPageRoute( return MaterialPageRoute(
builder: (_) => Scaffold( builder: (_) => Scaffold(

View file

@ -1,27 +1,27 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class UIHelper { class UIHelper {
static const double _verticalSpaceSmall = 10.0; static const double _VerticalSpaceSmall = 10.0;
static const double _verticalSpaceMedium = 20.0; static const double _VerticalSpaceMedium = 20.0;
static const double _verticalSpaceLarge = 60.0; static const double _VerticalSpaceLarge = 60.0;
static const double _horizontalSpaceSmall = 10.0; static const double _HorizontalSpaceSmall = 10.0;
static const double _horizontalSpaceMedium = 20.0; static const double _HorizontalSpaceMedium = 20.0;
static const double _horizontalSpaceLarge = 60.0; static const double HorizontalSpaceLarge = 60.0;
/// Returns a vertical space with height set to [_verticalSpaceSmall] /// Returns a vertical space with height set to [_VerticalSpaceSmall]
static Widget verticalSpaceSmall() { static Widget verticalSpaceSmall() {
return verticalSpace(_verticalSpaceSmall); return verticalSpace(_VerticalSpaceSmall);
} }
/// Returns a vertical space with height set to [_verticalSpaceMedium] /// Returns a vertical space with height set to [_VerticalSpaceMedium]
static Widget verticalSpaceMedium() { static Widget verticalSpaceMedium() {
return verticalSpace(_verticalSpaceMedium); return verticalSpace(_VerticalSpaceMedium);
} }
/// Returns a vertical space with height set to [_verticalSpaceLarge] /// Returns a vertical space with height set to [_VerticalSpaceLarge]
static Widget verticalSpaceLarge() { static Widget verticalSpaceLarge() {
return verticalSpace(_verticalSpaceLarge); return verticalSpace(_VerticalSpaceLarge);
} }
/// Returns a vertical space equal to the [height] supplied /// Returns a vertical space equal to the [height] supplied
@ -29,19 +29,19 @@ class UIHelper {
return Container(height: height); return Container(height: height);
} }
/// Returns a vertical space with height set to [_horizontalSpaceSmall] /// Returns a vertical space with height set to [_HorizontalSpaceSmall]
static Widget horizontalSpaceSmall() { static Widget horizontalSpaceSmall() {
return horizontalSpace(_horizontalSpaceSmall); return horizontalSpace(_HorizontalSpaceSmall);
} }
/// Returns a vertical space with height set to [_horizontalSpaceMedium] /// Returns a vertical space with height set to [_HorizontalSpaceMedium]
static Widget horizontalSpaceMedium() { static Widget horizontalSpaceMedium() {
return horizontalSpace(_horizontalSpaceMedium); return horizontalSpace(_HorizontalSpaceMedium);
} }
/// Returns a vertical space with height set to [_horizontalSpaceLarge] /// Returns a vertical space with height set to [HorizontalSpaceLarge]
static Widget horizontalSpaceLarge() { static Widget horizontalSpaceLarge() {
return horizontalSpace(_horizontalSpaceLarge); return horizontalSpace(HorizontalSpaceLarge);
} }
/// Returns a vertical space equal to the [width] supplied /// Returns a vertical space equal to the [width] supplied

View file

@ -12,8 +12,6 @@ import 'base_view.dart';
class AboutView extends StatelessWidget { class AboutView extends StatelessWidget {
static const routeName = '/about'; static const routeName = '/about';
const AboutView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final logo = Hero( final logo = Hero(
@ -32,13 +30,13 @@ class AboutView extends StatelessWidget {
title: Text(translate('titles.about')), title: Text(translate('titles.about')),
enableAbout: false, enableAbout: false,
), ),
body: model.state == ViewState.busy body: model.state == ViewState.Busy
? const Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: Container( : Container(
padding: const EdgeInsets.all(0), padding: EdgeInsets.all(0),
child: ListView( child: ListView(
shrinkWrap: true, shrinkWrap: true,
padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10, top: 10), padding: EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10, top: 10),
children: <Widget>[ children: <Widget>[
UIHelper.verticalSpaceMedium(), UIHelper.verticalSpaceMedium(),
Center(child: logo), Center(child: logo),
@ -77,7 +75,7 @@ class AboutView extends StatelessWidget {
Center( Center(
child: Linkify( child: Linkify(
text: translate('about.website'), text: translate('about.website'),
options: const LinkifyOptions(humanize: false), options: LinkifyOptions(humanize: false),
onOpen: (link) => model.openLink(link.url), onOpen: (link) => model.openLink(link.url),
), ),
) )

View file

@ -10,7 +10,7 @@ class BaseView<T extends BaseModel> extends StatefulWidget {
final Widget Function(BuildContext context, T model, Widget? child)? builder; final Widget Function(BuildContext context, T model, Widget? child)? builder;
final Function(T)? onModelReady; final Function(T)? onModelReady;
const BaseView({super.key, this.builder, this.onModelReady}); BaseView({this.builder, this.onModelReady});
@override @override
_BaseViewState<T> createState() => _BaseViewState<T>(); _BaseViewState<T> createState() => _BaseViewState<T>();

View file

@ -18,8 +18,6 @@ import 'base_view.dart';
class HistoryView extends StatelessWidget { class HistoryView extends StatelessWidget {
static const routeName = '/history'; static const routeName = '/history';
const HistoryView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BaseView<HistoryModel>( return BaseView<HistoryModel>(
@ -35,15 +33,15 @@ class HistoryView extends StatelessWidget {
Widget _render(HistoryModel model, BuildContext context) { Widget _render(HistoryModel model, BuildContext context) {
var url = Provider.of<Session>(context).url; var url = Provider.of<Session>(context).url;
return model.state == ViewState.busy return model.state == ViewState.Busy
? const Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: (model.errorMessage == null : (model.errorMessage == null
? Container( ? Container(
padding: const EdgeInsets.all(0), padding: EdgeInsets.all(0),
child: RefreshIndicator( child: RefreshIndicator(
onRefresh: () async => await model.getHistory(), child: _renderItems(model, url, context))) onRefresh: () async => await model.getHistory(), child: _renderItems(model, url, context)))
: Container( : Container(
padding: const EdgeInsets.all(25), padding: EdgeInsets.all(25),
child: CenteredErrorRow( child: CenteredErrorRow(
model.errorMessage, model.errorMessage,
retryCallback: () => model.getHistory(), retryCallback: () => model.getHistory(),
@ -53,8 +51,8 @@ class HistoryView extends StatelessWidget {
Widget _renderItems(HistoryModel model, String url, BuildContext context) { Widget _renderItems(HistoryModel model, String url, BuildContext context) {
List<Widget> cards = []; List<Widget> cards = [];
if (model.pastes.isNotEmpty) { if (model.pastes.length > 0) {
for (var paste in model.pastes.reversed) { model.pastes.reversed.forEach((paste) {
List<Widget> widgets = []; List<Widget> widgets = [];
var fullPasteUrl = PasteUtil.generateLink(url, paste.id); var fullPasteUrl = PasteUtil.generateLink(url, paste.id);
@ -73,7 +71,7 @@ class HistoryView extends StatelessWidget {
var copyWidget = ListTile( var copyWidget = ListTile(
title: Text(translate('history.copy_link.description')), title: Text(translate('history.copy_link.description')),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.copy, color: blueColor, textDirection: TextDirection.ltr), icon: Icon(Icons.copy, color: blueColor, textDirection: TextDirection.ltr),
onPressed: () { onPressed: () {
FlutterClipboard.copy(fullPasteUrl).then((value) { FlutterClipboard.copy(fullPasteUrl).then((value) {
final snackBar = SnackBar( final snackBar = SnackBar(
@ -85,7 +83,7 @@ class HistoryView extends StatelessWidget {
}, },
), ),
content: Text(translate('history.copy_link.copied')), content: Text(translate('history.copy_link.copied')),
duration: const Duration(seconds: 10), duration: Duration(seconds: 10),
); );
ScaffoldMessenger.of(context).showSnackBar(snackBar); ScaffoldMessenger.of(context).showSnackBar(snackBar);
}); });
@ -94,7 +92,7 @@ class HistoryView extends StatelessWidget {
var deleteWidget = ListTile( var deleteWidget = ListTile(
title: Text(translate('history.delete')), title: Text(translate('history.delete')),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.delete, color: redColor), icon: Icon(Icons.delete, color: redColor),
onPressed: () { onPressed: () {
model.deletePaste(paste.id); model.deletePaste(paste.id);
})); }));
@ -122,13 +120,13 @@ class HistoryView extends StatelessWidget {
widgets.add(fileSizeWidget); widgets.add(fileSizeWidget);
widgets.add(mimeTypeWidget); widgets.add(mimeTypeWidget);
} else { } else {
for (var element in paste.items!) { paste.items!.forEach((element) {
widgets.add(ListTile( widgets.add(ListTile(
title: Text(element!), title: Text(element!),
subtitle: Text(translate('history.multipaste_element')), subtitle: Text(translate('history.multipaste_element')),
trailing: _renderOpenInBrowser(model, '$url/$element'), trailing: _renderOpenInBrowser(model, '$url/$element'),
)); ));
} });
} }
widgets.add(dateWidget); widgets.add(dateWidget);
@ -137,7 +135,7 @@ class HistoryView extends StatelessWidget {
widgets.add(deleteWidget); widgets.add(deleteWidget);
var expandable = ExpandableTheme( var expandable = ExpandableTheme(
data: const ExpandableThemeData( data: ExpandableThemeData(
iconPlacement: ExpandablePanelIconPlacement.right, iconPlacement: ExpandablePanelIconPlacement.right,
headerAlignment: ExpandablePanelHeaderAlignment.center, headerAlignment: ExpandablePanelHeaderAlignment.center,
hasIcon: true, hasIcon: true,
@ -148,7 +146,7 @@ class HistoryView extends StatelessWidget {
header: InkWell( header: InkWell(
child: Text( child: Text(
paste.id, paste.id,
style: const TextStyle(color: blueColor), style: TextStyle(color: blueColor),
textAlign: TextAlign.left, textAlign: TextAlign.left,
)), )),
expanded: Column( expanded: Column(
@ -164,15 +162,15 @@ class HistoryView extends StatelessWidget {
trailing: Wrap(children: [ trailing: Wrap(children: [
openInBrowserButton, openInBrowserButton,
IconButton( IconButton(
icon: const Icon(Icons.share, color: blueColor, textDirection: TextDirection.ltr), icon: Icon(Icons.share, color: blueColor, textDirection: TextDirection.ltr),
onPressed: () async { onPressed: () async {
await Share.share(fullPasteUrl); await Share.share(fullPasteUrl);
}) })
]), ]),
subtitle: Text(!paste.isMulti! ? paste.filename! : '', style: const TextStyle(fontStyle: FontStyle.italic)), subtitle: Text(!paste.isMulti! ? paste.filename! : '', style: TextStyle(fontStyle: FontStyle.italic)),
), ),
)); ));
} });
} else { } else {
cards.add(Card( cards.add(Card(
child: ListTile( child: ListTile(
@ -183,14 +181,14 @@ class HistoryView extends StatelessWidget {
return ListView( return ListView(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
physics: const AlwaysScrollableScrollPhysics(),
children: cards, children: cards,
physics: AlwaysScrollableScrollPhysics(),
); );
} }
Widget _renderOpenInBrowser(HistoryModel model, String url) { Widget _renderOpenInBrowser(HistoryModel model, String url) {
return IconButton( return IconButton(
icon: const Icon(Icons.open_in_new, color: blueColor, textDirection: TextDirection.ltr), icon: Icon(Icons.open_in_new, color: blueColor, textDirection: TextDirection.ltr),
onPressed: () { onPressed: () {
return model.openLink(url); return model.openLink(url);
}); });

View file

@ -9,14 +9,12 @@ import 'base_view.dart';
class HomeView extends StatelessWidget { class HomeView extends StatelessWidget {
static const routeName = '/home'; static const routeName = '/home';
const HomeView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BaseView<HomeModel>( return BaseView<HomeModel>(
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
appBar: MyAppBar(title: Text(translate('app.title'))), appBar: MyAppBar(title: Text(translate('app.title'))),
body: model.state == ViewState.busy ? const Center(child: CircularProgressIndicator()) : Container()), body: model.state == ViewState.Busy ? Center(child: CircularProgressIndicator()) : Container()),
); );
} }
} }

View file

@ -20,8 +20,6 @@ class LoginView extends StatelessWidget {
final NavigationService _navigationService = locator<NavigationService>(); final NavigationService _navigationService = locator<NavigationService>();
final DialogService _dialogService = locator<DialogService>(); final DialogService _dialogService = locator<DialogService>();
LoginView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final logo = Hero( final logo = Hero(
@ -37,11 +35,11 @@ class LoginView extends StatelessWidget {
onModelReady: (model) => model.init(), onModelReady: (model) => model.init(),
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
appBar: MyAppBar(title: Text(translate('titles.login'))), appBar: MyAppBar(title: Text(translate('titles.login'))),
body: model.state == ViewState.busy body: model.state == ViewState.Busy
? const Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: ListView( : ListView(
shrinkWrap: true, shrinkWrap: true,
padding: const EdgeInsets.only(left: 10.0, right: 10.0), padding: EdgeInsets.only(left: 10.0, right: 10.0),
children: <Widget>[ children: <Widget>[
UIHelper.verticalSpaceMedium(), UIHelper.verticalSpaceMedium(),
Center(child: logo), Center(child: logo),
@ -52,7 +50,7 @@ class LoginView extends StatelessWidget {
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
children: <Widget>[ children: <Widget>[
InkWell( InkWell(
child: const Icon(Icons.help), child: Icon(Icons.help),
onTap: () { onTap: () {
_dialogService.showDialog( _dialogService.showDialog(
title: translate('login.compatibility_dialog.title'), title: translate('login.compatibility_dialog.title'),
@ -81,7 +79,7 @@ class LoginView extends StatelessWidget {
apiKeyController: model.apiKeyController), apiKeyController: model.apiKeyController),
UIHelper.verticalSpaceMedium(), UIHelper.verticalSpaceMedium(),
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.login, color: blueColor), icon: Icon(Icons.login, color: blueColor),
label: Text(translate('login.button')), label: Text(translate('login.button')),
onPressed: () async { onPressed: () async {
var loginSuccess = await model.login(); var loginSuccess = await model.login();

View file

@ -8,8 +8,6 @@ import '../shared/app_colors.dart';
import 'history_view.dart'; import 'history_view.dart';
class AuthenticatedNavBarView extends StatefulWidget { class AuthenticatedNavBarView extends StatefulWidget {
const AuthenticatedNavBarView({super.key});
@override @override
AuthenticatedNavBarState createState() => AuthenticatedNavBarState(); AuthenticatedNavBarState createState() => AuthenticatedNavBarState();
} }
@ -54,17 +52,17 @@ class AuthenticatedNavBarState extends State<AuthenticatedNavBarView> with Singl
Container( Container(
color: myColor, color: myColor,
alignment: Alignment.center, alignment: Alignment.center,
child: const UploadView(), child: UploadView(),
), ),
Container( Container(
color: myColor, color: myColor,
alignment: Alignment.center, alignment: Alignment.center,
child: const HistoryView(), child: HistoryView(),
), ),
Container( Container(
color: myColor, color: myColor,
alignment: Alignment.center, alignment: Alignment.center,
child: const ProfileView(), child: ProfileView(),
), ),
][_currentTabIndex], ][_currentTabIndex],
); );

View file

@ -15,8 +15,6 @@ import 'base_view.dart';
class ProfileView extends StatelessWidget { class ProfileView extends StatelessWidget {
static const routeName = '/profile'; static const routeName = '/profile';
const ProfileView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BaseView<ProfileModel>( return BaseView<ProfileModel>(
@ -28,8 +26,8 @@ class ProfileView extends StatelessWidget {
var url = Provider.of<Session>(context).url; var url = Provider.of<Session>(context).url;
var apiKey = Provider.of<Session>(context).apiKey; var apiKey = Provider.of<Session>(context).apiKey;
return model.state == ViewState.busy return model.state == ViewState.Busy
? const Center(child: CircularProgressIndicator()) ? Center(child: CircularProgressIndicator())
: ListView( : ListView(
children: <Widget>[ children: <Widget>[
UIHelper.verticalSpaceMedium(), UIHelper.verticalSpaceMedium(),
@ -47,7 +45,7 @@ class ProfileView extends StatelessWidget {
child: Linkify( child: Linkify(
onOpen: (link) => model.openLink(link.url), onOpen: (link) => model.openLink(link.url),
text: translate('profile.connection', args: {'url': url}), text: translate('profile.connection', args: {'url': url}),
options: const LinkifyOptions(humanize: false), options: LinkifyOptions(humanize: false),
))), ))),
UIHelper.verticalSpaceMedium(), UIHelper.verticalSpaceMedium(),
Padding( Padding(
@ -58,12 +56,12 @@ class ProfileView extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
const CircularProgressIndicator(), CircularProgressIndicator(),
Text(translate('profile.show_config_loading')), Text(translate('profile.show_config_loading')),
], ],
)) ))
: ElevatedButton.icon( : ElevatedButton.icon(
icon: const Icon(Icons.settings, color: blueColor), icon: Icon(Icons.settings, color: blueColor),
label: Text( label: Text(
translate('profile.show_config'), translate('profile.show_config'),
), ),
@ -74,7 +72,7 @@ class ProfileView extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.only(left: 25.0, right: 25.0), padding: const EdgeInsets.only(left: 25.0, right: 25.0),
child: ElevatedButton.icon( child: ElevatedButton.icon(
icon: const Icon(Icons.lock, color: orangeColor), icon: Icon(Icons.lock, color: orangeColor),
label: Text( label: Text(
translate('profile.reveal_api_key'), translate('profile.reveal_api_key'),
), ),
@ -85,7 +83,7 @@ class ProfileView extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.only(left: 25.0, right: 25.0), padding: const EdgeInsets.only(left: 25.0, right: 25.0),
child: ElevatedButton.icon( child: ElevatedButton.icon(
icon: const Icon(Icons.exit_to_app, color: redColor), icon: Icon(Icons.exit_to_app, color: redColor),
label: Text( label: Text(
translate('profile.logout'), translate('profile.logout'),
), ),

View file

@ -7,21 +7,19 @@ import '../../core/viewmodels/startup_model.dart';
class StartUpView extends StatelessWidget { class StartUpView extends StatelessWidget {
static const routeName = '/'; static const routeName = '/';
const StartUpView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ViewModelBuilder<StartUpViewModel>.reactive( return ViewModelBuilder<StartUpViewModel>.reactive(
viewModelBuilder: () => StartUpViewModel(), viewModelBuilder: () => StartUpViewModel(),
onModelReady: (model) => model.handleStartUpLogic(), onModelReady: (model) => model.handleStartUpLogic(),
builder: (context, model, child) => Scaffold( builder: (context, model, child) => Scaffold(
body: model.state == ViewState.busy body: model.state == ViewState.Busy
? Center( ? Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
const CircularProgressIndicator(), CircularProgressIndicator(),
(model.stateMessage!.isNotEmpty ? Text(model.stateMessage!) : Container()) (model.stateMessage!.isNotEmpty ? Text(model.stateMessage!) : Container())
])) ]))
: Container())); : Container()));

View file

@ -6,17 +6,17 @@ import 'package:provider/provider.dart';
import '../../core/models/session.dart'; import '../../core/models/session.dart';
class TabBarContainerView extends StatelessWidget { class TabBarContainerView extends StatelessWidget {
const TabBarContainerView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Session? currentSession = Provider.of<Session?>(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) { if (isAuthenticated) {
return const AuthenticatedNavBarView(); return AuthenticatedNavBarView();
} }
return LoginView(); return Container(
child: LoginView(),
);
} }
} }

View file

@ -14,8 +14,6 @@ import 'base_view.dart';
class UploadView extends StatelessWidget { class UploadView extends StatelessWidget {
static const routeName = '/upload'; static const routeName = '/upload';
const UploadView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BaseView<UploadModel>( return BaseView<UploadModel>(
@ -25,19 +23,19 @@ class UploadView extends StatelessWidget {
} }
bool _isUploadButtonEnabled(UploadModel model) { bool _isUploadButtonEnabled(UploadModel model) {
return model.pasteTextTouched || (model.paths != null && model.paths!.isNotEmpty); return model.pasteTextTouched || (model.paths != null && model.paths!.length > 0);
} }
Widget _render(UploadModel model, BuildContext context) { Widget _render(UploadModel model, BuildContext context) {
var url = Provider.of<Session>(context).url; var url = Provider.of<Session>(context).url;
return model.state == ViewState.busy return model.state == ViewState.Busy
? Center( ? Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
const CircularProgressIndicator(), CircularProgressIndicator(),
(model.stateMessage != null && model.stateMessage!.isNotEmpty ? Text(model.stateMessage!) : Container()) (model.stateMessage != null && model.stateMessage!.isNotEmpty ? Text(model.stateMessage!) : Container())
])) ]))
: ListView(children: <Widget>[ : ListView(children: <Widget>[
@ -52,15 +50,15 @@ class UploadView extends StatelessWidget {
minLines: 1, minLines: 1,
maxLines: 7, maxLines: 7,
decoration: InputDecoration( decoration: InputDecoration(
prefixIcon: const Icon( prefixIcon: Icon(
Icons.text_snippet, Icons.text_snippet,
), ),
suffixIcon: IconButton( suffixIcon: IconButton(
onPressed: () => model.pasteTextController.clear(), onPressed: () => model.pasteTextController.clear(),
icon: const Icon(Icons.clear), icon: Icon(Icons.clear),
), ),
hintText: translate('upload.text_to_be_pasted'), hintText: translate('upload.text_to_be_pasted'),
contentPadding: const EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), contentPadding: 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: model.pasteTextController)), controller: model.pasteTextController)),
@ -77,14 +75,14 @@ class UploadView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.file_copy_sharp, color: blueColor), icon: Icon(Icons.file_copy_sharp, color: blueColor),
onPressed: () => model.openFileExplorer(), onPressed: () => model.openFileExplorer(),
label: Text( label: Text(
translate('upload.open_file_explorer'), translate('upload.open_file_explorer'),
)), )),
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.cancel, color: orangeColor), icon: Icon(Icons.cancel, color: orangeColor),
onPressed: model.paths != null && model.paths!.isNotEmpty onPressed: model.paths != null && model.paths!.length > 0
? () => model.clearCachedFiles() ? () => model.clearCachedFiles()
: null, : null,
label: Text( label: Text(
@ -128,13 +126,13 @@ class UploadView extends StatelessWidget {
}, },
), ),
content: Text(translate('upload.uploaded')), content: Text(translate('upload.uploaded')),
duration: const Duration(seconds: 10), duration: Duration(seconds: 10),
); );
ScaffoldMessenger.of(context).showSnackBar(snackBar); ScaffoldMessenger.of(context).showSnackBar(snackBar);
}); });
} }
}, },
icon: const Icon(Icons.upload_rounded, color: greenColor), icon: Icon(Icons.upload_rounded, color: greenColor),
label: Text( label: Text(
translate('upload.upload'), translate('upload.upload'),
)), )),
@ -146,9 +144,9 @@ class UploadView extends StatelessWidget {
: Container(), : Container(),
Builder( Builder(
builder: (BuildContext context) => model.loadingPath builder: (BuildContext context) => model.loadingPath
? const Padding( ? Padding(
padding: EdgeInsets.only(bottom: 10.0), padding: const EdgeInsets.only(bottom: 10.0),
child: CircularProgressIndicator(), child: const CircularProgressIndicator(),
) )
: model.paths != null : model.paths != null
? Container( ? Container(
@ -161,7 +159,7 @@ class UploadView extends StatelessWidget {
final String name = (isMultiPath final String name = (isMultiPath
? model.paths!.map((e) => e.name).toList()[index] ? model.paths!.map((e) => e.name).toList()[index]
: model.fileName ?? '...'); : model.fileName ?? '...');
final path = model.paths!.isNotEmpty final path = model.paths!.length > 0
? model.paths!.map((e) => e.path).toList()[index].toString() ? model.paths!.map((e) => e.path).toList()[index].toString()
: ''; : '';

View file

@ -5,14 +5,14 @@ import '../../locator.dart';
import '../../ui/views/about_view.dart'; import '../../ui/views/about_view.dart';
class AboutIconButton extends StatelessWidget { class AboutIconButton extends StatelessWidget {
AboutIconButton({super.key}); AboutIconButton();
final NavigationService _navigationService = locator<NavigationService>(); final NavigationService _navigationService = locator<NavigationService>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return IconButton( return IconButton(
icon: const Icon(Icons.help), icon: Icon(Icons.help),
onPressed: () { onPressed: () {
_navigationService.navigateTo(AboutView.routeName); _navigationService.navigateTo(AboutView.routeName);
}); });

View file

@ -6,7 +6,7 @@ class CenteredErrorRow extends StatelessWidget {
final Function? retryCallback; final Function? retryCallback;
final String? message; final String? message;
const CenteredErrorRow(this.message, {super.key, this.retryCallback}); CenteredErrorRow(this.message, {this.retryCallback});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -20,7 +20,7 @@ class CenteredErrorRow extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Expanded(child: Center(child: Text(message!, style: const TextStyle(color: redColor)))), Expanded(child: Center(child: Text(message!, style: TextStyle(color: redColor)))),
], ],
), ),
(retryCallback != null (retryCallback != null
@ -30,7 +30,7 @@ class CenteredErrorRow extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Center( Center(
child: IconButton( child: IconButton(
icon: const Icon(Icons.refresh), icon: Icon(Icons.refresh),
color: primaryAccentColor, color: primaryAccentColor,
onPressed: () { onPressed: () {
retryCallback!(); retryCallback!();

View file

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

View file

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

View file

@ -9,14 +9,14 @@ class LoginTextField extends StatelessWidget {
final bool obscureText; final bool obscureText;
final Widget prefixIcon; final Widget prefixIcon;
const LoginTextField(this.controller, this.placeHolder, this.prefixIcon, LoginTextField(this.controller, this.placeHolder, this.prefixIcon,
{super.key, this.keyboardType = TextInputType.text, this.obscureText = false}); {this.keyboardType = TextInputType.text, this.obscureText = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 10.0), padding: EdgeInsets.symmetric(horizontal: 10.0),
margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0), margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
height: 50.0, height: 50.0,
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
decoration: BoxDecoration(color: whiteColor, borderRadius: BorderRadius.circular(10.0)), decoration: BoxDecoration(color: whiteColor, borderRadius: BorderRadius.circular(10.0)),
@ -26,11 +26,11 @@ class LoginTextField extends StatelessWidget {
decoration: InputDecoration( decoration: InputDecoration(
suffixIcon: IconButton( suffixIcon: IconButton(
onPressed: () => controller.clear(), onPressed: () => controller.clear(),
icon: const Icon(Icons.clear), icon: Icon(Icons.clear),
), ),
prefixIcon: prefixIcon, prefixIcon: prefixIcon,
hintText: placeHolder, hintText: placeHolder,
contentPadding: const EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), contentPadding: 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), controller: controller),

View file

@ -10,7 +10,9 @@ class MyAppBar extends AppBar {
: super(key: key, title: Row(children: <Widget>[title]), actions: _renderIconButtons(actionWidgets, enableAbout)); : 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 ??= []; if (actionWidgets == null) {
actionWidgets = [];
}
List<Widget> widgets = [...actionWidgets]; List<Widget> widgets = [...actionWidgets];

View file

@ -70,7 +70,7 @@ packages:
name: build_runner name: build_runner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.3.3" version: "2.3.2"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
@ -237,13 +237,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.0.2" version: "5.0.2"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
flutter_localizations: flutter_localizations:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -323,7 +316,7 @@ packages:
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
intl: intl:
dependency: "direct main" dependency: transitive
description: description:
name: intl name: intl
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
@ -364,13 +357,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.1.0" version: "4.1.0"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
logger: logger:
dependency: "direct main" dependency: "direct main"
description: description:
@ -442,7 +428,7 @@ packages:
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
path: path:
dependency: "direct main" dependency: transitive
description: description:
name: path name: path
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"

View file

@ -41,16 +41,13 @@ dependencies:
package_info_plus: 3.0.2 package_info_plus: 3.0.2
json_annotation: 4.7.0 json_annotation: 4.7.0
dynamic_color: 1.5.4 dynamic_color: 1.5.4
intl: 0.17.0
path: 1.8.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
build_runner: 2.3.3 build_runner: 2.3.2
built_value_generator: 8.4.2 built_value_generator: 8.4.2
json_serializable: 6.5.4 json_serializable: 6.5.4
flutter_lints: 2.0.1
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec # following page: https://www.dartlang.org/tools/pub/pubspec