Compare commits
No commits in common. "a65c7d92531e070b6c57339ab2fafd9102cb7105" and "f9a2bb0df723dc13e65cb7b8c39657997c486005" have entirely different histories.
a65c7d9253
...
f9a2bb0df7
47 changed files with 256 additions and 322 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
enum RefreshEvent { refreshHistory }
|
enum RefreshEvent { RefreshHistory }
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
enum ViewState { idle, busy }
|
enum ViewState { Idle, Busy }
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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>();
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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'),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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()));
|
||||||
|
|
|
@ -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(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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!();
|
||||||
|
|
|
@ -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,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -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),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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];
|
||||||
|
|
||||||
|
|
20
pubspec.lock
20
pubspec.lock
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue