mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-10 19:03:12 +00:00
feat: Add a notification if the app doesn't support the server API version
This commit is contained in:
parent
8c5bdf9cd8
commit
58bfa6db93
|
@ -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": {
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in a new issue