mirror of
https://gitlab.com/mysocialportal/relatica
synced 2024-10-18 11:13:31 +00:00
Refactor bootstrapping process to have more statusing, timeouts and go to sign in screen if non logged in
This commit is contained in:
parent
b59f26b9ef
commit
aa2f102518
10 changed files with 124 additions and 22 deletions
|
@ -32,6 +32,7 @@ import 'services/persistent_info_service.dart';
|
|||
import 'services/reshared_via_service.dart';
|
||||
import 'services/secrets_service.dart';
|
||||
import 'services/setting_service.dart';
|
||||
import 'services/status_service.dart';
|
||||
import 'services/timeline_entry_filter_service.dart';
|
||||
import 'services/timeline_manager.dart';
|
||||
import 'update_timer_initialization.dart';
|
||||
|
@ -40,6 +41,8 @@ import 'utils/active_profile_selector.dart';
|
|||
final _logger = Logger('DI_Init');
|
||||
|
||||
Future<void> dependencyInjectionInitialization() async {
|
||||
getIt.registerSingleton<StatusService>(StatusService());
|
||||
|
||||
final appSupportdir = await getApplicationSupportDirectory();
|
||||
getIt.registerSingleton<ActiveProfileSelector<PersistentInfoService>>(
|
||||
ActiveProfileSelector(
|
||||
|
|
|
@ -781,7 +781,7 @@ class ProfileClient extends FriendicaClient {
|
|||
_logger.finest(() => 'Getting logged in user profile');
|
||||
final request =
|
||||
Uri.parse('https://$serverName/api/v1/accounts/verify_credentials');
|
||||
return (await _getApiRequest(request))
|
||||
return (await _getApiRequest(request, timeout: oauthTimeout))
|
||||
.mapValue((json) => ConnectionMastodonExtensions.fromJson(
|
||||
json,
|
||||
defaultServerName: serverName,
|
||||
|
@ -1083,25 +1083,40 @@ abstract class FriendicaClient {
|
|||
}
|
||||
|
||||
FutureResult<PagedResponse<List<dynamic>>, ExecError> _getApiListRequest(
|
||||
Uri url) async {
|
||||
return await getUrl(url, headers: _headers).transformAsync(
|
||||
Uri url, {
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
return await getUrl(
|
||||
url,
|
||||
headers: _headers,
|
||||
timeout: timeout,
|
||||
).transformAsync(
|
||||
(response) async {
|
||||
return response.map((data) => jsonDecode(data) as List<dynamic>);
|
||||
},
|
||||
).execErrorCastAsync();
|
||||
}
|
||||
|
||||
FutureResult<PagedResponse<dynamic>, ExecError> _getApiPagedRequest(
|
||||
Uri url) async {
|
||||
return await getUrl(url, headers: _headers).transformAsync(
|
||||
FutureResult<PagedResponse<dynamic>, ExecError> _getApiPagedRequest(Uri url,
|
||||
{Duration? timeout}) async {
|
||||
return await getUrl(
|
||||
url,
|
||||
headers: _headers,
|
||||
timeout: timeout,
|
||||
).transformAsync(
|
||||
(response) async {
|
||||
return response.map((data) => jsonDecode(data));
|
||||
},
|
||||
).execErrorCastAsync();
|
||||
}
|
||||
|
||||
FutureResult<dynamic, ExecError> _getApiRequest(Uri url) async {
|
||||
return await getUrl(url, headers: _headers).transformAsync(
|
||||
FutureResult<dynamic, ExecError> _getApiRequest(Uri url,
|
||||
{Duration? timeout}) async {
|
||||
return await getUrl(
|
||||
url,
|
||||
headers: _headers,
|
||||
timeout: timeout,
|
||||
).transformAsync(
|
||||
(response) async {
|
||||
return jsonDecode(response.data);
|
||||
},
|
||||
|
@ -1111,6 +1126,5 @@ abstract class FriendicaClient {
|
|||
Map<String, String> get _headers => {
|
||||
'Authorization': _profile.credentials.authHeaderValue,
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
if (usePhpDebugging) 'Cookie': 'XDEBUG_SESSION=PHPSTORM;path=/',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ const maxViewPortalWidth = 750.0;
|
|||
const maxProcessingMillis = 3;
|
||||
const processingSleep = Duration(milliseconds: 1);
|
||||
const apiCallTimeout = Duration(seconds: 60);
|
||||
const oauthTimeout = Duration(seconds: 15);
|
||||
|
||||
Future<bool?> showConfirmDialog(BuildContext context, String caption) {
|
||||
return showDialog<bool>(
|
||||
|
|
|
@ -21,6 +21,7 @@ import 'services/hashtag_service.dart';
|
|||
import 'services/interactions_manager.dart';
|
||||
import 'services/notifications_manager.dart';
|
||||
import 'services/setting_service.dart';
|
||||
import 'services/status_service.dart';
|
||||
import 'services/timeline_entry_filter_service.dart';
|
||||
import 'services/timeline_manager.dart';
|
||||
import 'utils/active_profile_selector.dart';
|
||||
|
@ -62,6 +63,10 @@ class App extends StatelessWidget {
|
|||
return Portal(
|
||||
child: MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider<StatusService>(
|
||||
create: (_) => getIt<StatusService>(),
|
||||
lazy: true,
|
||||
),
|
||||
ChangeNotifierProvider<SettingsService>(
|
||||
create: (_) => getIt<SettingsService>(),
|
||||
lazy: true,
|
||||
|
|
|
@ -6,6 +6,8 @@ import 'package:logging/logging.dart';
|
|||
import 'package:result_monad/result_monad.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../../globals.dart';
|
||||
import '../../services/status_service.dart';
|
||||
import '../../utils/network_utils.dart';
|
||||
import '../exec_error.dart';
|
||||
import 'credentials_intf.dart';
|
||||
|
@ -91,20 +93,28 @@ class OAuthCredentials implements ICredentials {
|
|||
'Client ID and Client Secret are already set, skipping ID fetching');
|
||||
return Result.ok(true);
|
||||
}
|
||||
getIt<StatusService>().setStatus('Attempting to contact $serverName...');
|
||||
final idEndpoint = Uri.parse('https://$serverName/api/v1/apps');
|
||||
final response = await postUrl(idEndpoint, {
|
||||
final response = await postUrl(
|
||||
idEndpoint,
|
||||
{
|
||||
'client_name': 'Relatica',
|
||||
'redirect_uris': redirectUrl,
|
||||
'scopes': 'read write follow push',
|
||||
'website': 'https://myportal.social',
|
||||
});
|
||||
},
|
||||
timeout: oauthTimeout,
|
||||
);
|
||||
|
||||
response.match(onSuccess: (body) {
|
||||
getIt<StatusService>().setStatus('Connected to $serverName...');
|
||||
final json = jsonDecode(body);
|
||||
clientId = json['client_id'];
|
||||
clientSecret = json['client_secret'];
|
||||
}, onError: (error) {
|
||||
_logger.severe('Error logging in: $error');
|
||||
getIt<StatusService>()
|
||||
.setStatus('Error contacting $serverName...: $error');
|
||||
_logger.severe('Error contacting $serverName...: $error');
|
||||
});
|
||||
|
||||
return response.mapValue((_) => true);
|
||||
|
@ -112,6 +122,7 @@ class OAuthCredentials implements ICredentials {
|
|||
|
||||
FutureResult<bool, ExecError> _login() async {
|
||||
if (accessToken.isNotEmpty) {
|
||||
getIt<StatusService>().setStatus('Logged into $serverName');
|
||||
_logger.info('Already have access token, skipping');
|
||||
return Result.ok(true);
|
||||
}
|
||||
|
@ -124,12 +135,20 @@ class OAuthCredentials implements ICredentials {
|
|||
});
|
||||
|
||||
try {
|
||||
getIt<StatusService>()
|
||||
.setStatus('Attempting getting authorization to $serverName');
|
||||
|
||||
final result = await FlutterWebAuth2.authenticate(
|
||||
url: url.toString(), callbackUrlScheme: redirectScheme);
|
||||
url: url.toString(),
|
||||
callbackUrlScheme: redirectScheme,
|
||||
);
|
||||
final code = Uri.parse(result).queryParameters['code'];
|
||||
if (code == null) {
|
||||
_logger.severe(
|
||||
'Error code was not returned with the query parameters: $result');
|
||||
getIt<StatusService>().setStatus(
|
||||
'Error getting the response code during authentication to $serverName');
|
||||
|
||||
return buildErrorResult(
|
||||
type: ErrorType.serverError,
|
||||
message: 'Error getting the response code during authentication',
|
||||
|
@ -145,11 +164,16 @@ class OAuthCredentials implements ICredentials {
|
|||
};
|
||||
final response = await postUrl(url2, body);
|
||||
response.match(onSuccess: (body) {
|
||||
getIt<StatusService>().setStatus('Logged into $serverName');
|
||||
accessToken = jsonDecode(body)['access_token'];
|
||||
}, onError: (error) {
|
||||
getIt<StatusService>()
|
||||
.setStatus('Error getting authorization to $serverName');
|
||||
_logger.severe('Error doing OAUth processing: $error');
|
||||
});
|
||||
} catch (e) {
|
||||
getIt<StatusService>()
|
||||
.setStatus('Error getting authorization to $serverName');
|
||||
_logger.severe('Exception while Doing OAuth Process: $e');
|
||||
return buildErrorResult(
|
||||
type: ErrorType.serverError,
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../controls/padding.dart';
|
||||
import '../globals.dart';
|
||||
import '../routes.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../services/status_service.dart';
|
||||
|
||||
class SplashScreen extends StatelessWidget {
|
||||
const SplashScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final statusService = context.watch<StatusService>();
|
||||
final accountsService = context.watch<AccountsService>();
|
||||
if (!accountsService.initializing && !accountsService.loggedIn) {
|
||||
context.pushNamed(ScreenPaths.signin);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Column(
|
||||
|
@ -27,7 +37,13 @@ class SplashScreen extends StatelessWidget {
|
|||
const CircularProgressIndicator(),
|
||||
const VerticalPadding(),
|
||||
const Text('Logging in accounts...'),
|
||||
const VerticalPadding(),
|
||||
],
|
||||
Text(
|
||||
statusService.status,
|
||||
softWrap: true,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
)),
|
||||
);
|
||||
|
|
|
@ -7,11 +7,13 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||
|
||||
import '../di_initialization.dart';
|
||||
import '../friendica_client/friendica_client.dart';
|
||||
import '../globals.dart';
|
||||
import '../models/auth/credentials_intf.dart';
|
||||
import '../models/auth/profile.dart';
|
||||
import '../models/exec_error.dart';
|
||||
import '../update_timer_initialization.dart';
|
||||
import 'secrets_service.dart';
|
||||
import 'status_service.dart';
|
||||
|
||||
class AccountsService extends ChangeNotifier {
|
||||
static final _logger = Logger('$AccountsService');
|
||||
|
@ -80,6 +82,8 @@ class AccountsService extends ChangeNotifier {
|
|||
final client =
|
||||
ProfileClient(Profile.credentialsOnly(signedInCredentials));
|
||||
credentialsCache = signedInCredentials;
|
||||
getIt<StatusService>().setStatus(
|
||||
'Getting user profile from ${signedInCredentials.serverName}');
|
||||
return await client.getMyProfile();
|
||||
}).andThenAsync((profileData) async {
|
||||
final loginProfile = Profile(
|
||||
|
@ -91,6 +95,8 @@ class AccountsService extends ChangeNotifier {
|
|||
loggedIn: true,
|
||||
);
|
||||
|
||||
getIt<StatusService>()
|
||||
.setStatus('Loaded user profile ${profileData.handle}');
|
||||
if (_loggedInProfiles.isEmpty) {
|
||||
await setActiveProfile(loginProfile,
|
||||
withNotification: withNotification);
|
||||
|
@ -106,6 +112,7 @@ class AccountsService extends ChangeNotifier {
|
|||
});
|
||||
|
||||
if (result.isFailure) {
|
||||
getIt<StatusService>().setStatus('Error signing in: ${result.error}');
|
||||
_logger.severe('Error signing in: ${result.error}');
|
||||
}
|
||||
|
||||
|
|
16
lib/services/status_service.dart
Normal file
16
lib/services/status_service.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class StatusService extends ChangeNotifier {
|
||||
String _lastStatus = 'None';
|
||||
DateTime _lastStatusTime = DateTime.now();
|
||||
|
||||
String get status => _lastStatus;
|
||||
|
||||
DateTime get statusTime => _lastStatusTime;
|
||||
|
||||
void setStatus(String status) {
|
||||
_lastStatus = status;
|
||||
_lastStatusTime = DateTime.now();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
|
@ -18,6 +18,12 @@ final _logger = Logger('UpdateTimer');
|
|||
|
||||
void setupUpdateTimers() {
|
||||
Timer.periodic(_timerRefresh, (_) async {
|
||||
final service = getIt<AccountsService>();
|
||||
if (!service.loggedIn) {
|
||||
_logger
|
||||
.info('Trying to do update when no logged in accounts. Skipping...');
|
||||
return;
|
||||
}
|
||||
executeUpdatesForProfile(getIt<AccountsService>().currentProfile);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ import 'dart:convert';
|
|||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:logging/logging.dart';
|
||||
import '../globals.dart';
|
||||
import 'package:result_monad/result_monad.dart';
|
||||
|
||||
import '../friendica_client/paged_response.dart';
|
||||
import '../globals.dart';
|
||||
import '../models/exec_error.dart';
|
||||
|
||||
final _logger = Logger('NetworkUtils');
|
||||
|
@ -15,16 +15,21 @@ http.Response requestTimeout() => http.Response('Client side timeout', 408);
|
|||
FutureResult<PagedResponse<String>, ExecError> getUrl(
|
||||
Uri url, {
|
||||
Map<String, String>? headers,
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
_logger.finer('GET: $url');
|
||||
final requestHeaders = headers ?? {};
|
||||
if (usePhpDebugging) {
|
||||
requestHeaders['Cookie'] = 'XDEBUG_SESSION=PHPSTORM;path=/';
|
||||
}
|
||||
try {
|
||||
final request = http.get(
|
||||
url,
|
||||
headers: headers,
|
||||
headers: requestHeaders,
|
||||
);
|
||||
|
||||
final response = await request.timeout(
|
||||
apiCallTimeout,
|
||||
timeout ?? apiCallTimeout,
|
||||
onTimeout: requestTimeout,
|
||||
);
|
||||
|
||||
|
@ -47,17 +52,22 @@ FutureResult<String, ExecError> postUrl(
|
|||
Uri url,
|
||||
Map<String, dynamic> body, {
|
||||
Map<String, String>? headers,
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
_logger.finer('POST: $url \n Body: $body');
|
||||
final requestHeaders = headers ?? {};
|
||||
if (usePhpDebugging) {
|
||||
requestHeaders['Cookie'] = 'XDEBUG_SESSION=PHPSTORM;path=/';
|
||||
}
|
||||
try {
|
||||
final request = http.post(
|
||||
url,
|
||||
headers: headers,
|
||||
headers: requestHeaders,
|
||||
body: jsonEncode(body),
|
||||
);
|
||||
|
||||
final response = await request.timeout(
|
||||
apiCallTimeout,
|
||||
timeout ?? apiCallTimeout,
|
||||
onTimeout: requestTimeout,
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue