mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-13 20:23:16 +00:00
feat: Allow refreshing device token for Server API
This commit is contained in:
parent
0fb898873c
commit
8f42d89457
|
@ -569,6 +569,14 @@
|
|||
"description": "The device {} will no longer have access to the server.",
|
||||
"yes": "Revoke",
|
||||
"no": "Cancel"
|
||||
},
|
||||
"refresh_token_alert": {
|
||||
"header": "Refresh access token?",
|
||||
"description": "A new token will be generated for this device to connect to the server.",
|
||||
"yes": "Confirm",
|
||||
"no": "Cancel",
|
||||
"failed_to_refresh_token": "Couldn't refresh the token. Please try again.",
|
||||
"success_refresh_token": "Token refreshed successfully."
|
||||
}
|
||||
},
|
||||
"recovery_key": {
|
||||
|
|
|
@ -550,6 +550,41 @@ class ServerApi extends GraphQLApiMap
|
|||
return token;
|
||||
}
|
||||
|
||||
Future<GenericResult<String>> refreshDeviceApiToken() async {
|
||||
GenericResult<String> token;
|
||||
QueryResult<Mutation$RefreshDeviceApiToken> response;
|
||||
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
|
||||
final mutation = Options$Mutation$RefreshDeviceApiToken();
|
||||
response = await client.mutate$RefreshDeviceApiToken(
|
||||
mutation,
|
||||
);
|
||||
if (response.hasException) {
|
||||
print(response.exception.toString());
|
||||
token = GenericResult<String>(
|
||||
success: false,
|
||||
data: '',
|
||||
message: response.exception.toString(),
|
||||
);
|
||||
}
|
||||
token = GenericResult<String>(
|
||||
success: true,
|
||||
data: response.parsedData!.api.refreshDeviceApiToken.token!,
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
token = GenericResult<String>(
|
||||
success: false,
|
||||
data: '',
|
||||
message: e.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
Future<bool> isHttpServerWorking() async => (await getApiVersion()) != null;
|
||||
|
||||
Future<GenericResult<String>> authorizeDevice(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:bloc_concurrency/bloc_concurrency.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
|
@ -32,6 +33,10 @@ class TokensBloc extends Bloc<TokensEvent, TokensState> {
|
|||
on<ServerSelectedForProviderToken>(
|
||||
connectServerToProviderToken,
|
||||
);
|
||||
on<RefreshServerApiTokenEvent>(
|
||||
refreshServerApiToken,
|
||||
transformer: droppable(),
|
||||
);
|
||||
|
||||
add(const RevalidateTokens());
|
||||
|
||||
|
@ -208,6 +213,33 @@ class TokensBloc extends Bloc<TokensEvent, TokensState> {
|
|||
await getIt<ResourcesModel>().updateServerByDomain(newServerData);
|
||||
}
|
||||
|
||||
Future<void> refreshServerApiToken(
|
||||
final RefreshServerApiTokenEvent event,
|
||||
final Emitter<TokensState> emit,
|
||||
) async {
|
||||
final (success, newToken) =
|
||||
await getIt<ApiConnectionRepository>().refreshDeviceToken();
|
||||
if (!success) {
|
||||
getIt<NavigationService>().showSnackBar(
|
||||
'devices.refresh_token_alert.failed_to_refresh_token'.tr(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
final Server serverDetails = state.servers.first;
|
||||
final hostingDetails = serverDetails.hostingDetails.copyWith(
|
||||
apiToken: newToken,
|
||||
);
|
||||
await getIt<ResourcesModel>().updateServerByDomain(
|
||||
Server(
|
||||
hostingDetails: hostingDetails,
|
||||
domain: serverDetails.domain,
|
||||
),
|
||||
);
|
||||
getIt<NavigationService>().showSnackBar(
|
||||
'devices.refresh_token_alert.success_refresh_token'.tr(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_resourcesModelSubscription.cancel();
|
||||
|
|
|
@ -34,3 +34,10 @@ class ServerSelectedForProviderToken extends TokensEvent {
|
|||
@override
|
||||
List<Object> get props => [providerServer, server, serverProviderCredential];
|
||||
}
|
||||
|
||||
class RefreshServerApiTokenEvent extends TokensEvent {
|
||||
const RefreshServerApiTokenEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
|
|
@ -257,6 +257,16 @@ class ApiConnectionRepository {
|
|||
}
|
||||
}
|
||||
|
||||
Future<(bool, String)> refreshDeviceToken() async {
|
||||
final GenericResult<String> result = await api.refreshDeviceApiToken();
|
||||
_apiData.devices.invalidate();
|
||||
if (result.success) {
|
||||
return (true, result.data);
|
||||
} else {
|
||||
return (false, result.message ?? 'jobs.generic_error'.tr());
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_dataStream.close();
|
||||
_connectionStatusStream.close();
|
||||
|
|
|
@ -50,16 +50,17 @@ class ServerHostingDetails {
|
|||
final DateTime? startTime,
|
||||
final String? serverLocation,
|
||||
final String? serverType,
|
||||
final String? apiToken,
|
||||
}) =>
|
||||
ServerHostingDetails(
|
||||
startTime: startTime ?? this.startTime,
|
||||
serverLocation: serverLocation ?? this.serverLocation,
|
||||
serverType: serverType ?? this.serverType,
|
||||
apiToken: apiToken ?? this.apiToken,
|
||||
createTime: createTime,
|
||||
id: id,
|
||||
ip4: ip4,
|
||||
volume: volume,
|
||||
apiToken: apiToken,
|
||||
provider: provider,
|
||||
);
|
||||
|
||||
|
|
|
@ -52,7 +52,8 @@ abstract class AppThemeFactory {
|
|||
}
|
||||
|
||||
static Future<ColorScheme?> _getDynamicColors(
|
||||
final Brightness brightness) async {
|
||||
final Brightness brightness,
|
||||
) async {
|
||||
List<Color> extractAdditionalColours(final ColorScheme scheme) => [
|
||||
scheme.surface,
|
||||
scheme.surfaceDim,
|
||||
|
@ -65,7 +66,9 @@ abstract class AppThemeFactory {
|
|||
];
|
||||
|
||||
ColorScheme insertAdditionalColours(
|
||||
final ColorScheme scheme, final List<Color> additionalColours) =>
|
||||
final ColorScheme scheme,
|
||||
final List<Color> additionalColours,
|
||||
) =>
|
||||
scheme.copyWith(
|
||||
surface: additionalColours[0],
|
||||
surfaceDim: additionalColours[1],
|
||||
|
@ -88,7 +91,9 @@ abstract class AppThemeFactory {
|
|||
}
|
||||
// Workaround as dynamic_color doesn't add new color roles
|
||||
final fromSeed = ColorScheme.fromSeed(
|
||||
seedColor: colorScheme.primary, brightness: brightness);
|
||||
seedColor: colorScheme.primary,
|
||||
brightness: brightness,
|
||||
);
|
||||
final additionalColours = extractAdditionalColours(fromSeed);
|
||||
final updatedColorScheme =
|
||||
insertAdditionalColours(colorScheme, additionalColours);
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:cubit_form/cubit_form.dart';
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/bloc/devices/devices_bloc.dart';
|
||||
import 'package:selfprivacy/logic/bloc/tokens/tokens_bloc.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/json/api_token.dart';
|
||||
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
|
||||
|
@ -121,7 +122,7 @@ class _DeviceTile extends StatelessWidget {
|
|||
.tr(args: [DateFormat.yMMMMd().format(device.date)]),
|
||||
),
|
||||
onTap: device.isCaller
|
||||
? null
|
||||
? () => _showTokenRefreshDialog(context, device)
|
||||
: () => _showConfirmationDialog(context, device),
|
||||
);
|
||||
|
||||
|
@ -172,4 +173,54 @@ class _DeviceTile extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
);
|
||||
|
||||
Future _showTokenRefreshDialog(
|
||||
final BuildContext context,
|
||||
final ApiToken device,
|
||||
) =>
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (final context) => AlertDialog(
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.update_outlined),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'devices.refresh_token_alert.header'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'devices.refresh_token_alert.description'
|
||||
.tr(args: [device.name]),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text('devices.refresh_token_alert.no'.tr()),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text('devices.refresh_token_alert.yes'.tr()),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<TokensBloc>()
|
||||
.add(const RefreshServerApiTokenEvent());
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue