feat: Add a notification if the app doesn't support the server API version

This commit is contained in:
Inex Code 2024-07-26 17:43:57 +03:00
parent 8c5bdf9cd8
commit 58bfa6db93
9 changed files with 186 additions and 0 deletions

View file

@ -39,6 +39,7 @@
"please_connect": "Please connect your server, domain and DNS provider to dive in!",
"network_error": "Network error",
"feature_unsupported_on_api_version": "This feature is only supported on server version {versionConstraint}. Your server is on version {currentVersion}.",
"server_is_outdated": "This SelfPrivacy version requires API version {versionConstraint}, but your server is on version {currentVersion}. Some features won't work and errors might occur. Please upgrade your server.",
"error": "Error"
},
"more_page": {

View file

@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/bloc/connection_status_bloc.dart';
import 'package:selfprivacy/logic/bloc/devices/devices_bloc.dart';
import 'package:selfprivacy/logic/bloc/outdated_server_checker/outdated_server_checker_bloc.dart';
import 'package:selfprivacy/logic/bloc/recovery_key/recovery_key_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_logs/server_logs_bloc.dart';
@ -38,6 +39,7 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
late final ServerDetailsCubit serverDetailsCubit;
late final VolumesBloc volumesBloc;
late final ServerLogsBloc serverLogsBloc;
late final OutdatedServerCheckerBloc outdatedServerCheckerBloc;
@override
void initState() {
@ -55,6 +57,7 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
serverDetailsCubit = ServerDetailsCubit();
volumesBloc = VolumesBloc();
serverLogsBloc = ServerLogsBloc();
outdatedServerCheckerBloc = OutdatedServerCheckerBloc();
}
@override
@ -100,6 +103,9 @@ class BlocAndProviderConfigState extends State<BlocAndProviderConfig> {
BlocProvider(
create: (final _) => serverLogsBloc,
),
BlocProvider(
create: (final _) => outdatedServerCheckerBloc,
),
],
child: widget.child,
);

View file

@ -0,0 +1,49 @@
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:selfprivacy/config/get_it_config.dart';
part 'outdated_server_checker_event.dart';
part 'outdated_server_checker_state.dart';
const String requiredServerVersion = '>=3.3.0';
class OutdatedServerCheckerBloc
extends Bloc<OutdatedServerCheckerEvent, OutdatedServerCheckerState> {
OutdatedServerCheckerBloc() : super(OutdatedServerCheckerInitial()) {
on<ServerApiVersionChanged>((final event, final emit) {
if (event.newVersion == null) {
emit(OutdatedServerCheckerInitial());
return;
}
final requiredVersion = VersionConstraint.parse(requiredServerVersion);
final currentVersion = Version.parse(event.newVersion!);
if (requiredVersion.allows(currentVersion)) {
emit(OutdatedServerCheckerUpToDate(currentVersion));
} else {
emit(OutdatedServerCheckerOutdated(currentVersion));
}
});
_apiDataSubscription = getIt<ApiConnectionRepository>().dataStream.listen(
(final ApiData apiData) {
add(ServerApiVersionChanged(apiData.apiVersion.data));
},
);
}
@override
Future<void> close() {
_apiDataSubscription.cancel();
return super.close();
}
@override
void onChange(final Change<OutdatedServerCheckerState> change) {
super.onChange(change);
}
late StreamSubscription _apiDataSubscription;
}

View file

@ -0,0 +1,14 @@
part of 'outdated_server_checker_bloc.dart';
sealed class OutdatedServerCheckerEvent extends Equatable {
const OutdatedServerCheckerEvent();
}
class ServerApiVersionChanged extends OutdatedServerCheckerEvent {
const ServerApiVersionChanged(this.newVersion);
final String? newVersion;
@override
List<Object?> get props => [newVersion];
}

View file

@ -0,0 +1,31 @@
part of 'outdated_server_checker_bloc.dart';
sealed class OutdatedServerCheckerState extends Equatable {
const OutdatedServerCheckerState();
VersionConstraint get requiredVersion =>
VersionConstraint.parse(requiredServerVersion);
}
final class OutdatedServerCheckerInitial extends OutdatedServerCheckerState {
@override
List<Object> get props => [];
}
final class OutdatedServerCheckerOutdated extends OutdatedServerCheckerState {
const OutdatedServerCheckerOutdated(this.currentVersion);
final Version currentVersion;
@override
List<Object> get props => [currentVersion];
}
final class OutdatedServerCheckerUpToDate extends OutdatedServerCheckerState {
const OutdatedServerCheckerUpToDate(this.currentVersion);
final Version currentVersion;
@override
List<Object> get props => [currentVersion];
}

View file

@ -0,0 +1,39 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
class ServerOutdatedCard extends StatelessWidget {
const ServerOutdatedCard({
required this.requiredVersion,
required this.currentVersion,
super.key,
});
final String requiredVersion;
final String currentVersion;
@override
Widget build(final BuildContext context) => FilledCard(
error: true,
child: ListTile(
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
title: Text(
'basis.server_is_outdated'.tr(
namedArgs: {
'versionConstraint': requiredVersion,
'currentVersion': currentVersion,
},
),
style: Theme.of(context).textTheme.titleSmall?.copyWith(
color: Theme.of(context).colorScheme.onErrorContainer,
),
),
trailing: Icon(
Icons.error_outline,
size: 16,
color: Theme.of(context).colorScheme.onTertiaryContainer,
),
),
);
}

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/bloc/outdated_server_checker/outdated_server_checker_bloc.dart';
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
@ -12,6 +13,7 @@ import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
import 'package:selfprivacy/ui/components/server_outdated_card/server_outdated_card.dart';
import 'package:selfprivacy/ui/router/router.dart';
import 'package:selfprivacy/utils/breakpoints.dart';
@ -40,6 +42,9 @@ class _ProvidersPageState extends State<ProvidersPage> {
final ServerInstallationState appConfig =
context.watch<ServerInstallationCubit>().state;
final OutdatedServerCheckerState outdatedServerCheckerState =
context.watch<OutdatedServerCheckerBloc>().state;
StateType getServerStatus() {
if (!isReady) {
return StateType.uninitialized;
@ -76,6 +81,15 @@ class _ProvidersPageState extends State<ProvidersPage> {
const NotReadyCard(),
const SizedBox(height: 16),
],
if (outdatedServerCheckerState is OutdatedServerCheckerOutdated) ...[
ServerOutdatedCard(
requiredVersion:
outdatedServerCheckerState.requiredVersion.toString(),
currentVersion:
outdatedServerCheckerState.currentVersion.toString(),
),
const SizedBox(height: 16),
],
_Card(
state: getServerStatus(),
icon: BrandIcons.server,

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:gap/gap.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/bloc/outdated_server_checker/outdated_server_checker_bloc.dart';
import 'package:selfprivacy/logic/bloc/services/services_bloc.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/models/service.dart';
@ -11,6 +12,7 @@ import 'package:selfprivacy/logic/models/state_types.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
import 'package:selfprivacy/ui/components/server_outdated_card/server_outdated_card.dart';
import 'package:selfprivacy/ui/helpers/empty_page_placeholder.dart';
import 'package:selfprivacy/ui/router/router.dart';
import 'package:selfprivacy/utils/breakpoints.dart';
@ -31,6 +33,9 @@ class _ServicesPageState extends State<ServicesPage> {
final isReady = context.watch<ServerInstallationCubit>().state
is ServerInstallationFinished;
final OutdatedServerCheckerState outdatedServerCheckerState =
context.watch<OutdatedServerCheckerBloc>().state;
final services = [...context.watch<ServicesBloc>().state.services];
services
.sort((final a, final b) => a.status.index.compareTo(b.status.index));
@ -53,6 +58,16 @@ class _ServicesPageState extends State<ServicesPage> {
child: ListView(
padding: paddingH15V0,
children: [
if (outdatedServerCheckerState
is OutdatedServerCheckerOutdated) ...[
ServerOutdatedCard(
requiredVersion:
outdatedServerCheckerState.requiredVersion.toString(),
currentVersion:
outdatedServerCheckerState.currentVersion.toString(),
),
const SizedBox(height: 16),
],
Text(
'basis.services_title'.tr(),
style: Theme.of(context).textTheme.bodyLarge,

View file

@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/bloc/outdated_server_checker/outdated_server_checker_bloc.dart';
import 'package:selfprivacy/logic/bloc/users/users_bloc.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
@ -20,6 +21,7 @@ import 'package:selfprivacy/ui/components/buttons/outlined_button.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
import 'package:selfprivacy/ui/components/list_tiles/list_tile_on_surface_variant.dart';
import 'package:selfprivacy/ui/components/server_outdated_card/server_outdated_card.dart';
import 'package:selfprivacy/ui/helpers/empty_page_placeholder.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/router/router.dart';
@ -42,6 +44,9 @@ class UsersPage extends StatelessWidget {
is ServerInstallationFinished;
Widget child;
final OutdatedServerCheckerState outdatedServerCheckerState =
context.watch<OutdatedServerCheckerBloc>().state;
if (!isReady) {
child = EmptyPagePlaceholder(
showReadyCard: true,
@ -93,6 +98,18 @@ class UsersPage extends StatelessWidget {
},
child: Column(
children: [
if (outdatedServerCheckerState
is OutdatedServerCheckerOutdated) ...[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ServerOutdatedCard(
requiredVersion:
outdatedServerCheckerState.requiredVersion.toString(),
currentVersion:
outdatedServerCheckerState.currentVersion.toString(),
),
),
],
Padding(
padding: const EdgeInsets.all(8.0),
child: FilledButton.tonal(