Output login errors to user
This commit is contained in:
14
lib/api.dart
14
lib/api.dart
@@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:path_provider/path_provider.dart'; // For cache storage
|
import 'package:path_provider/path_provider.dart'; // For cache storage
|
||||||
|
import 'package:sheetless/utility.dart';
|
||||||
|
|
||||||
import 'sheet.dart';
|
import 'sheet.dart';
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ class ApiClient {
|
|||||||
|
|
||||||
ApiClient({required this.baseUrl, this.token});
|
ApiClient({required this.baseUrl, this.token});
|
||||||
|
|
||||||
Future<bool> login(String username, String password) async {
|
Future<Result<void>> login(String username, String password) async {
|
||||||
log.info("Logging in...");
|
log.info("Logging in...");
|
||||||
try {
|
try {
|
||||||
final url = '$baseUrl/login';
|
final url = '$baseUrl/login';
|
||||||
@@ -28,14 +29,19 @@ class ApiClient {
|
|||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
token = jsonDecode(response.body);
|
token = jsonDecode(response.body);
|
||||||
log.info('Login successful');
|
log.info('Login successful');
|
||||||
return true;
|
return Result.ok(null);
|
||||||
} else {
|
} else {
|
||||||
log.warning('Login failed: ${response.statusCode}, ${response.body}');
|
log.warning('Login failed: ${response.statusCode}, ${response.body}');
|
||||||
|
return Result.error(
|
||||||
|
Exception(
|
||||||
|
"Response code ${response.statusCode}\nResponse: ${response.body}",
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} on Exception catch (e) {
|
||||||
log.warning('Error during login', e);
|
log.warning('Error during login', e);
|
||||||
|
return Result.error(e);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void logout() {
|
void logout() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:jwt_decoder/jwt_decoder.dart';
|
|||||||
import 'package:sheetless/api.dart';
|
import 'package:sheetless/api.dart';
|
||||||
import 'package:sheetless/home_page.dart';
|
import 'package:sheetless/home_page.dart';
|
||||||
import 'package:sheetless/storage_helper.dart';
|
import 'package:sheetless/storage_helper.dart';
|
||||||
|
import 'package:sheetless/utility.dart';
|
||||||
|
|
||||||
class LoginPage extends StatefulWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
const LoginPage({super.key});
|
const LoginPage({super.key});
|
||||||
@@ -62,17 +63,16 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
});
|
});
|
||||||
serverUrl = "$serverUrl/api";
|
serverUrl = "$serverUrl/api";
|
||||||
final apiClient = ApiClient(baseUrl: serverUrl);
|
final apiClient = ApiClient(baseUrl: serverUrl);
|
||||||
final loginSuccessful = await apiClient.login(email, password);
|
final loginResult = await apiClient.login(email, password);
|
||||||
if (loginSuccessful) {
|
if (loginResult.isOk()) {
|
||||||
await _storageHelper.writeSecure(SecureStorageKey.url, serverUrl);
|
await _storageHelper.writeSecure(SecureStorageKey.url, serverUrl);
|
||||||
await _storageHelper.writeSecure(SecureStorageKey.jwt, apiClient.token!);
|
await _storageHelper.writeSecure(SecureStorageKey.jwt, apiClient.token!);
|
||||||
await _storageHelper.writeSecure(SecureStorageKey.email, email);
|
await _storageHelper.writeSecure(SecureStorageKey.email, email);
|
||||||
await _storageHelper.writeSecure(SecureStorageKey.password, password);
|
await _storageHelper.writeSecure(SecureStorageKey.password, password);
|
||||||
_navigateToMainPage();
|
_navigateToMainPage();
|
||||||
} else {
|
} else {
|
||||||
// TODO: give more varied error messages
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_error = "Login failed.";
|
_error = "Login failed.\n${loginResult.error()}";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
70
lib/utility.dart
Normal file
70
lib/utility.dart
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/// Utility class that simplifies handling errors.
|
||||||
|
///
|
||||||
|
/// Return a [Result] from a function to indicate success or failure.
|
||||||
|
///
|
||||||
|
/// A [Result] is either an [Ok] with a value of type [T]
|
||||||
|
/// or an [Err] with an [Exception].
|
||||||
|
///
|
||||||
|
/// Use [Result.ok] to create a successful result with a value of type [T].
|
||||||
|
/// Use [Result.error] to create an error result with an [Exception].
|
||||||
|
///
|
||||||
|
/// Evaluate the result using a switch statement:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (result) {
|
||||||
|
/// case Ok(): {
|
||||||
|
/// print(result.value);
|
||||||
|
/// }
|
||||||
|
/// case Error(): {
|
||||||
|
/// print(result.error);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
sealed class Result<T> {
|
||||||
|
const Result();
|
||||||
|
|
||||||
|
/// Creates a successful [Result], completed with the specified [value].
|
||||||
|
const factory Result.ok(T value) = Ok._;
|
||||||
|
|
||||||
|
/// Creates an error [Result], completed with the specified [error].
|
||||||
|
const factory Result.error(Exception error) = Err._;
|
||||||
|
|
||||||
|
bool isOk() {
|
||||||
|
return this is Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
T value() {
|
||||||
|
Ok<T> ok = this as Ok<T>;
|
||||||
|
return ok._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isErr() {
|
||||||
|
return this is Err;
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception error() {
|
||||||
|
Err<T> err = this as Err<T>;
|
||||||
|
return err._error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A successful [Result] with a returned [value].
|
||||||
|
final class Ok<T> extends Result<T> {
|
||||||
|
const Ok._(this._value);
|
||||||
|
|
||||||
|
/// The returned value of this result.
|
||||||
|
final T _value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'Result<$T>.ok($_value)';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error [Result] with a resulting [error].
|
||||||
|
final class Err<T> extends Result<T> {
|
||||||
|
const Err._(this._error);
|
||||||
|
|
||||||
|
/// The resulting error of this result.
|
||||||
|
final Exception _error;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'Result<$T>.error($_error)';
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user