Compare commits
6 Commits
inex/april
...
master
Author | SHA1 | Date |
---|---|---|
Inex Code | 4930fc2387 | |
Inex Code | 11d0e58334 | |
NaiJi ✨ | a6b846cc78 | |
NaiJi ✨ | 6819192219 | |
NaiJi ✨ | ffdb9d92fb | |
NaiJi ✨ | 1c42598787 |
|
@ -40,3 +40,9 @@ app.*.symbols
|
|||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Flatpak
|
||||
.flatpak-builder/
|
||||
flatpak-build/
|
||||
flatpak-repo/
|
||||
*.flatpak
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -438,6 +438,7 @@ class DigitalOceanServerProvider extends ServerProvider {
|
|||
description: rawLocation.name,
|
||||
flag: rawLocation.flag,
|
||||
identifier: rawLocation.slug,
|
||||
countryDisplayKey: rawLocation.countryDisplayKey,
|
||||
);
|
||||
} catch (e) {
|
||||
continue;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
|
@ -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!,
|
||||
|
|
Loading…
Reference in New Issue