Compare commits

...

6 Commits

Author SHA1 Message Date
Inex Code 4930fc2387 feat: Show the error screen when libsecret fails
continuous-integration/drone/push Build is passing Details
2024-05-02 15:05:38 +03:00
Inex Code 11d0e58334 fix: Flatpak builds didn't work 2024-04-26 18:08:04 +03:00
NaiJi ✨ a6b846cc78 feat(backups): Show how much space a service uses on backup (#500)
continuous-integration/drone/push Build is passing Details
Fixes #434

![image](/attachments/351cc025-8dae-44f2-9bca-18f8950e0780)

Co-authored-by: Inex Code <inex.code@selfprivacy.org>
Reviewed-on: #500
Reviewed-by: Inex Code <inex.code@selfprivacy.org>
Co-authored-by: NaiJi  <naiji@noreply.git.selfprivacy.org>
Co-committed-by: NaiJi  <naiji@noreply.git.selfprivacy.org>
2024-04-24 13:18:02 +03:00
NaiJi ✨ 6819192219 feat: Add country names to installation process (#501)
continuous-integration/drone/push Build is passing Details
Fixes #494

Reviewed-on: #501
Reviewed-by: Inex Code <inex.code@selfprivacy.org>
Co-authored-by: NaiJi  <naiji@noreply.git.selfprivacy.org>
Co-committed-by: NaiJi  <naiji@noreply.git.selfprivacy.org>
2024-04-24 12:54:32 +03:00
NaiJi ✨ ffdb9d92fb Merge pull request 'fix(backups): Implement filtering for enabled services for backups' (#499) from filter-enabled-backup-services into master
continuous-integration/drone/push Build is passing Details
Reviewed-on: #499
Reviewed-by: Inex Code <inex.code@selfprivacy.org>
2024-04-17 18:48:56 +03:00
NaiJi ✨ 1c42598787 fix(backups): Implement filtering for enabled services for backups
- Resolve: #433
2024-04-16 23:03:11 +04:00
15 changed files with 253 additions and 30 deletions

6
.gitignore vendored
View File

@ -40,3 +40,9 @@ app.*.symbols
# Obfuscation related
app.*.map.json
# Flatpak
.flatpak-builder/
flatpak-build/
flatpak-repo/
*.flatpak

View File

@ -606,5 +606,16 @@
"reset_onboarding": "Reset onboarding switch",
"reset_onboarding_description": "Reset onboarding switch to show onboarding screen again",
"cubit_statuses": "Cubit loading statuses"
},
"countries": {
"germany": "Germany",
"netherlands": "Netherlands",
"singapore": "Singapore",
"united_kingdom": "United Kingdom",
"canada": "Canada",
"india": "India",
"australia": "Australia",
"united_states": "United States",
"finland": "Finland"
}
}
}

View File

@ -1,6 +1,6 @@
app-id: org.selfprivacy.app
runtime: org.freedesktop.Platform
runtime-version: '22.08'
runtime-version: '23.08'
sdk: org.freedesktop.Sdk
command: selfprivacy
finish-args:
@ -11,6 +11,7 @@ finish-args:
- "--share=network"
- "--own-name=org.selfprivacy.app"
- "--device=dri"
- "--talk-name=org.freedesktop.secrets"
modules:
- name: selfprivacy
buildsystem: simple
@ -35,7 +36,7 @@ modules:
sources:
- type: git
url: https://gitlab.gnome.org/GNOME/libsecret.git
tag: 0.20.5
tag: 0.21.4
- name: libjsoncpp
buildsystem: meson
config-opts:

View File

@ -1,6 +1,6 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
@ -28,33 +28,47 @@ class HiveConfig {
await Hive.openBox(BNames.appSettingsBox);
final HiveAesCipher cipher = HiveAesCipher(
await getEncryptedKey(BNames.serverInstallationEncryptionKey),
);
try {
final HiveAesCipher cipher = HiveAesCipher(
await getEncryptedKey(BNames.serverInstallationEncryptionKey),
);
await Hive.openBox<User>(BNames.usersDeprecated);
await Hive.openBox<User>(BNames.usersBox, encryptionCipher: cipher);
await Hive.openBox<User>(BNames.usersDeprecated);
await Hive.openBox<User>(BNames.usersBox, encryptionCipher: cipher);
final Box<User> deprecatedUsers = Hive.box<User>(BNames.usersDeprecated);
if (deprecatedUsers.isNotEmpty) {
final Box<User> users = Hive.box<User>(BNames.usersBox);
await users.addAll(deprecatedUsers.values.toList());
await deprecatedUsers.clear();
final Box<User> deprecatedUsers = Hive.box<User>(BNames.usersDeprecated);
if (deprecatedUsers.isNotEmpty) {
final Box<User> users = Hive.box<User>(BNames.usersBox);
await users.addAll(deprecatedUsers.values.toList());
await deprecatedUsers.clear();
}
await Hive.openBox(
BNames.serverInstallationBox,
encryptionCipher: cipher,
);
} on PlatformException catch (e) {
print('HiveConfig: Error while opening boxes: $e');
rethrow;
}
await Hive.openBox(BNames.serverInstallationBox, encryptionCipher: cipher);
}
static Future<Uint8List> getEncryptedKey(final String encKey) async {
const FlutterSecureStorage secureStorage = FlutterSecureStorage();
final bool hasEncryptionKey = await secureStorage.containsKey(key: encKey);
if (!hasEncryptionKey) {
final List<int> key = Hive.generateSecureKey();
await secureStorage.write(key: encKey, value: base64UrlEncode(key));
}
try {
final bool hasEncryptionKey =
await secureStorage.containsKey(key: encKey);
if (!hasEncryptionKey) {
final List<int> key = Hive.generateSecureKey();
await secureStorage.write(key: encKey, value: base64UrlEncode(key));
}
final String? string = await secureStorage.read(key: encKey);
return base64Url.decode(string!);
final String? string = await secureStorage.read(key: encKey);
return base64Url.decode(string!);
} on PlatformException catch (e) {
print('HiveConfig: Error while getting encryption key: $e');
rethrow;
}
}
}

View File

@ -65,14 +65,59 @@ class DigitalOceanLocation {
emoji = '🇮🇳';
break;
case 'syd':
emoji = '🇦🇺';
break;
case 'nyc':
case 'sfo':
emoji = '🇺🇸';
break;
}
return emoji;
}
String get countryDisplayKey {
String displayKey = 'countries.';
switch (slug.substring(0, 3)) {
case 'fra':
displayKey += 'germany';
break;
case 'ams':
displayKey += 'netherlands';
break;
case 'sgp':
displayKey += 'singapore';
break;
case 'lon':
displayKey += 'united_kingdom';
break;
case 'tor':
displayKey += 'canada';
break;
case 'blr':
displayKey += 'india';
break;
case 'syd':
displayKey += 'australia';
break;
case 'nyc':
case 'sfo':
displayKey += 'united_states';
break;
default:
displayKey = slug;
}
return displayKey;
}
}
@JsonSerializable()

View File

@ -155,6 +155,27 @@ class HetznerLocation {
}
return emoji;
}
String get countryDisplayKey {
String displayKey = 'countries.';
switch (country.substring(0, 2)) {
case 'DE':
displayKey += 'germany';
break;
case 'FI':
displayKey += 'finland';
break;
case 'US':
displayKey += 'united_states';
break;
default:
displayKey = country;
}
return displayKey;
}
}
/// A Volume is a highly-available, scalable, and SSD-based block storage for Servers.

View File

@ -2,12 +2,14 @@ class ServerProviderLocation {
ServerProviderLocation({
required this.title,
required this.identifier,
required this.countryDisplayKey,
this.description,
this.flag = '',
});
final String title;
final String identifier;
final String countryDisplayKey;
final String? description;
final String flag;
}

View File

@ -438,6 +438,7 @@ class DigitalOceanServerProvider extends ServerProvider {
description: rawLocation.name,
flag: rawLocation.flag,
identifier: rawLocation.slug,
countryDisplayKey: rawLocation.countryDisplayKey,
);
} catch (e) {
continue;

View File

@ -156,6 +156,7 @@ class HetznerServerProvider extends ServerProvider {
description: server.location.description,
flag: server.location.flag,
identifier: server.location.name,
countryDisplayKey: server.location.countryDisplayKey,
),
),
);
@ -456,6 +457,7 @@ class HetznerServerProvider extends ServerProvider {
description: rawLocation.description,
flag: rawLocation.flag,
identifier: rawLocation.name,
countryDisplayKey: rawLocation.countryDisplayKey,
);
} catch (e) {
continue;

View File

@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/bloc_config.dart';
import 'package:selfprivacy/config/bloc_observer.dart';
@ -9,13 +10,20 @@ import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/config/localization.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/theming/factory/app_theme_factory.dart';
import 'package:selfprivacy/ui/pages/errors/failed_to_init_secure_storage.dart';
import 'package:selfprivacy/ui/router/router.dart';
// import 'package:wakelock/wakelock.dart';
import 'package:timezone/data/latest.dart' as tz;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await HiveConfig.init();
try {
await HiveConfig.init();
} on PlatformException catch (e) {
runApp(
FailedToInitSecureStorageScreen(e: e),
);
}
// await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
// try {

View File

@ -127,7 +127,9 @@ class _HeroSliverAppBarState extends State<HeroSliverAppBar> {
Widget build(final BuildContext context) {
final isMobile =
widget.ignoreBreakpoints ? true : Breakpoints.small.isActive(context);
final isJobsListEmpty = context.watch<JobsCubit>().state is JobsStateEmpty;
final isJobsListEmpty = widget.hasFlashButton
? context.watch<JobsCubit>().state is JobsStateEmpty
: true;
return SliverAppBar(
expandedHeight:
widget.hasHeroIcon ? 148.0 + _size.height : 72.0 + _size.height,

View File

@ -38,8 +38,14 @@ class BackupDetailsPage extends StatelessWidget {
: StateType.uninitialized;
final bool preventActions = backupsState.preventActions;
final List<Backup> backups = backupsState.backups;
final List<Service> services =
context.watch<ServicesBloc>().state.servicesThatCanBeBackedUp;
final List<Service> services = context
.watch<ServicesBloc>()
.state
.servicesThatCanBeBackedUp
.where(
(final service) => service.isEnabled,
)
.toList();
final Duration? autobackupPeriod = backupsState.autobackupPeriod;
final List<ServerJob> backupJobs = context
.watch<ServerJobsBloc>()

View File

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/logic/models/service.dart';
@ -103,6 +104,29 @@ class _CreateBackupsModalState extends State<CreateBackupsModal> {
...widget.services.map(
(final Service service) {
final bool busy = busyServices.contains(service.id);
final List<Widget> descriptionWidgets = [];
if (busy) {
descriptionWidgets.add(Text('backup.service_busy'.tr()));
} else {
descriptionWidgets.add(
Text(
'service_page.uses'.tr(
namedArgs: {
'usage': service.storageUsage.used.toString(),
'volume': context
.read<VolumesBloc>()
.state
.getVolume(service.storageUsage.volume ?? '')
.displayName,
},
),
style: Theme.of(context).textTheme.labelMedium,
),
);
descriptionWidgets.add(
Text(service.backupDescription),
);
}
return CheckboxListTile.adaptive(
onChanged: !busy
? (final bool? value) {
@ -122,8 +146,9 @@ class _CreateBackupsModalState extends State<CreateBackupsModal> {
title: Text(
service.displayName,
),
subtitle: Text(
busy ? 'backup.service_busy'.tr() : service.backupDescription,
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: descriptionWidgets,
),
secondary: SvgPicture.string(
service.svgIcon,

View File

@ -0,0 +1,71 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gap/gap.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/pages/more/about_application.dart';
class FailedToInitSecureStorageScreen extends StatelessWidget {
const FailedToInitSecureStorageScreen({
required this.e,
super.key,
});
final PlatformException e;
@override
Widget build(final BuildContext context) => MaterialApp(
home: BrandHeroScreen(
heroIcon: Icons.error_outline,
heroTitle: 'Failed to initialize secure storage',
hasBackButton: false,
children: [
const Text(
'SelfPrivacy requires a secure storage provided by your operating system to encrypt sensitive data, but it failed to initialize.',
),
if (Platform.isLinux)
const Text(
'Please make sure that the libsecret library is installed.',
),
const Gap(16),
Text('Error: ${e.message}'),
const Gap(16),
const Divider(),
const Gap(16),
const LinkListTile(
title: 'Our website',
subtitle: 'selfprivacy.org',
uri: 'https://selfprivacy.org/',
icon: Icons.language_outlined,
),
const LinkListTile(
title: 'Documentation',
subtitle: 'selfprivacy.org/docs',
uri: 'https://selfprivacy.org/docs/',
icon: Icons.library_books_outlined,
),
const LinkListTile(
title: 'Privacy Policy',
subtitle: 'selfprivacy.org/privacy-policy',
uri: 'https://selfprivacy.org/privacy-policy/',
icon: Icons.policy_outlined,
),
const LinkListTile(
title: 'Matrix support chat',
subtitle: '#chat:selfprivacy.org',
uri: 'https://matrix.to/#/#chat:selfprivacy.org',
icon: Icons.question_answer_outlined,
longPressText: '#chat:selfprivacy.org',
),
const LinkListTile(
title: 'Telegram support chat',
subtitle: '@selfprivacy_chat',
uri: 'https://t.me/selfprivacy_chat',
icon: Icons.question_answer_outlined,
longPressText: '@selfprivacy_chat',
),
],
),
);
}

View File

@ -120,6 +120,14 @@ class SelectLocationPage extends StatelessWidget {
.titleMedium,
),
const SizedBox(height: 8),
Text(
location.countryDisplayKey.tr(),
style: Theme.of(context)
.textTheme
.bodyMedium,
),
if (location.description != null)
const SizedBox(height: 4),
if (location.description != null)
Text(
location.description!,