mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-08 01:43:13 +00:00
Merge branch 'master' into backblaze-refactor
This commit is contained in:
commit
08988ed10b
68
.gitea/ISSUE_TEMPLATE/bug.yaml
Normal file
68
.gitea/ISSUE_TEMPLATE/bug.yaml
Normal file
|
@ -0,0 +1,68 @@
|
|||
name: Bug report
|
||||
about: File a bug report
|
||||
labels:
|
||||
- Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Please provide a short but a descriptive title for your issue.
|
||||
- type: textarea
|
||||
id: expected-behaviour
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: What did you expect to happen?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: actual-behaviour
|
||||
attributes:
|
||||
label: Actual Behavior
|
||||
description: What actually happened?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps-to-reproduce
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: What steps can we follow to reproduce this issue?
|
||||
placeholder: |
|
||||
1. First step
|
||||
2. Second step
|
||||
3. and so on...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Context and notes
|
||||
description: Additional information about environment or what were you trying to do. If you have an idea how to fix this issue, please describe it here too.
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output, if you have any. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: input
|
||||
id: app-version
|
||||
attributes:
|
||||
label: App Version
|
||||
description: What version of SelfPrivacy app are you running? You can find it in the "About" section of the app.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: api-version
|
||||
attributes:
|
||||
label: Server API Version
|
||||
description: What version of SelfPrivacy API are you running? You can find it in the "About" section of the app. Leave it empty if your app is not connected to the server yet.
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: What operating system are you using?
|
||||
options:
|
||||
- Android
|
||||
- iOS
|
||||
- Linux
|
||||
- macOS
|
||||
- Windows
|
23
.gitea/ISSUE_TEMPLATE/feature.yaml
Normal file
23
.gitea/ISSUE_TEMPLATE/feature.yaml
Normal file
|
@ -0,0 +1,23 @@
|
|||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
label:
|
||||
- Feature request
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this feature request! Please provide a short but a descriptive title for your issue.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Describe the feature you'd like to see.
|
||||
placeholder: |
|
||||
As a user, I want to be able to...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Context and notes
|
||||
description: Additional information about environment and what were you trying to do. If you have an idea how to implement this feature, please describe it here too.
|
29
.gitea/ISSUE_TEMPLATE/translation.yaml
Normal file
29
.gitea/ISSUE_TEMPLATE/translation.yaml
Normal file
|
@ -0,0 +1,29 @@
|
|||
name: Translation issue
|
||||
about: File a translation (localization) issue
|
||||
labels:
|
||||
- Translations
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Translations can be modified and discussed on [Weblate](https://weblate.selfprivacy.org/projects/selfprivacy/). You can fix the mistranslation issue yourself there. Using the search, you can also find the string ID of the mistranslated string. If your issue is more complex, please file it here
|
||||
|
||||
If you are a member of SelfPrivacy core team, you **must** fix the issue yourself on Weblate.
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Language
|
||||
description: What language is affected?
|
||||
placeholder: |
|
||||
English
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Describe the issue in detail. If you have an idea how to fix this issue, please describe it here too. Include the string ID of the mistranslated string, if possible.
|
||||
placeholder: |
|
||||
The string `string.id` is translated as "foo", but it should be "bar".
|
||||
validations:
|
||||
required: true
|
|
@ -35,7 +35,8 @@
|
|||
"done": "Done",
|
||||
"continue": "Continue",
|
||||
"alert": "Alert",
|
||||
"copied_to_clipboard": "Copied to clipboard!"
|
||||
"copied_to_clipboard": "Copied to clipboard!",
|
||||
"please_connect": "Please connect your server, domain and DNS provider to dive in!"
|
||||
},
|
||||
"more_page": {
|
||||
"configuration_wizard": "Setup wizard",
|
||||
|
@ -315,6 +316,7 @@
|
|||
"in_menu": "Server is not set up yet. Please finish setup using setup wizard for further work."
|
||||
},
|
||||
"service_page": {
|
||||
"nothing_here": "Nothing here",
|
||||
"open_in_browser": "Open in browser",
|
||||
"restart": "Restart service",
|
||||
"disable": "Disable service",
|
||||
|
@ -371,7 +373,6 @@
|
|||
"add_new_user": "Add a first user",
|
||||
"new_user": "New user",
|
||||
"delete_user": "Delete user",
|
||||
"not_ready": "Please connect server, domain and DNS in the Providers tab, to be able to add a first user",
|
||||
"nobody_here": "Nobody here",
|
||||
"login": "Login",
|
||||
"new_user_info_note": "New user will automatically be granted an access to all of the services",
|
||||
|
|
|
@ -75,7 +75,7 @@ class DnsRecordsCubit
|
|||
|
||||
@override
|
||||
Future<void> clear() async {
|
||||
emit(const DnsRecordsState(dnsState: DnsRecordsStatus.error));
|
||||
emit(const DnsRecordsState(dnsState: DnsRecordsStatus.uninitialized));
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
|
|
67
lib/ui/helpers/empty_page_placeholder.dart
Normal file
67
lib/ui/helpers/empty_page_placeholder.dart
Normal file
|
@ -0,0 +1,67 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
|
||||
|
||||
class EmptyPagePlaceholder extends StatelessWidget {
|
||||
const EmptyPagePlaceholder({
|
||||
required this.title,
|
||||
required this.iconData,
|
||||
required this.description,
|
||||
this.showReadyCard = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String description;
|
||||
final IconData iconData;
|
||||
final bool showReadyCard;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => !showReadyCard
|
||||
? _expandedContent(context)
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: NotReadyCard(),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Center(
|
||||
child: _expandedContent(context),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _expandedContent(final BuildContext context) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
iconData,
|
||||
size: 50,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
description,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
|
@ -49,6 +49,8 @@ class _ProvidersPageState extends State<ProvidersPage> {
|
|||
return StateType.stable;
|
||||
}
|
||||
|
||||
bool isClickable() => getServerStatus() != StateType.uninitialized;
|
||||
|
||||
StateType getDnsStatus() {
|
||||
if (dnsStatus == DnsRecordsStatus.uninitialized ||
|
||||
dnsStatus == DnsRecordsStatus.refreshing) {
|
||||
|
@ -83,7 +85,9 @@ class _ProvidersPageState extends State<ProvidersPage> {
|
|||
subtitle: diskStatus.isDiskOkay
|
||||
? 'storage.status_ok'.tr()
|
||||
: 'storage.status_error'.tr(),
|
||||
onTap: () => context.pushRoute(const ServerDetailsRoute()),
|
||||
onTap: isClickable()
|
||||
? () => context.pushRoute(const ServerDetailsRoute())
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_Card(
|
||||
|
@ -93,7 +97,9 @@ class _ProvidersPageState extends State<ProvidersPage> {
|
|||
subtitle: appConfig.isDomainSelected
|
||||
? appConfig.serverDomain!.domainName
|
||||
: '',
|
||||
onTap: () => context.pushRoute(const DnsDetailsRoute()),
|
||||
onTap: isClickable()
|
||||
? () => context.pushRoute(const DnsDetailsRoute())
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_Card(
|
||||
|
@ -103,7 +109,9 @@ class _ProvidersPageState extends State<ProvidersPage> {
|
|||
icon: BrandIcons.save,
|
||||
title: 'backup.card_title'.tr(),
|
||||
subtitle: isBackupInitialized ? 'backup.card_subtitle'.tr() : '',
|
||||
onTap: () => context.pushRoute(const BackupDetailsRoute()),
|
||||
onTap: isClickable()
|
||||
? () => context.pushRoute(const BackupDetailsRoute())
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -7,9 +7,10 @@ import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
|||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
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/not_ready_card/not_ready_card.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:selfprivacy/ui/helpers/empty_page_placeholder.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
import 'package:selfprivacy/utils/breakpoints.dart';
|
||||
import 'package:selfprivacy/utils/launch_url.dart';
|
||||
|
@ -42,7 +43,14 @@ class _ServicesPageState extends State<ServicesPage> {
|
|||
),
|
||||
)
|
||||
: null,
|
||||
body: RefreshIndicator(
|
||||
body: !isReady
|
||||
? EmptyPagePlaceholder(
|
||||
showReadyCard: true,
|
||||
title: 'service_page.nothing_here'.tr(),
|
||||
description: 'basis.please_connect'.tr(),
|
||||
iconData: BrandIcons.box,
|
||||
)
|
||||
: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await context.read<ServicesCubit>().reload();
|
||||
},
|
||||
|
@ -54,7 +62,6 @@ class _ServicesPageState extends State<ServicesPage> {
|
|||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
if (!isReady) ...[const NotReadyCard(), const SizedBox(height: 24)],
|
||||
...services.map(
|
||||
(final service) => Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
|
@ -129,16 +136,16 @@ class _Card extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
service.displayName,
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
service.displayName,
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (service.url != '' && service.url != null)
|
||||
Column(
|
||||
|
@ -146,7 +153,7 @@ class _Card extends StatelessWidget {
|
|||
_ServiceLink(
|
||||
url: service.url ?? '',
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
if (service.id == 'mailserver')
|
||||
|
@ -156,21 +163,21 @@ class _Card extends StatelessWidget {
|
|||
url: domainName,
|
||||
isActive: false,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
service.description,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
service.loginInfo,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
part of 'users.dart';
|
||||
|
||||
class _NoUsers extends StatelessWidget {
|
||||
const _NoUsers({required this.text});
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
BrandIcons.users,
|
||||
size: 50,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'users.nobody_here'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
text,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _CouldNotLoadUsers extends StatelessWidget {
|
||||
const _CouldNotLoadUsers({required this.text});
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
BrandIcons.users,
|
||||
size: 50,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'users.could_not_fetch_users'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
text,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
|
@ -16,17 +16,16 @@ import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
|||
import 'package:selfprivacy/ui/components/buttons/outlined_button.dart';
|
||||
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
import 'package:selfprivacy/ui/helpers/empty_page_placeholder.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.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/not_ready_card/not_ready_card.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
import 'package:selfprivacy/utils/breakpoints.dart';
|
||||
import 'package:selfprivacy/utils/platform_adapter.dart';
|
||||
import 'package:selfprivacy/utils/ui_helpers.dart';
|
||||
|
||||
part 'empty.dart';
|
||||
part 'new_user.dart';
|
||||
part 'user.dart';
|
||||
part 'user_details.dart';
|
||||
|
@ -43,7 +42,12 @@ class UsersPage extends StatelessWidget {
|
|||
Widget child;
|
||||
|
||||
if (!isReady) {
|
||||
child = isNotReady();
|
||||
child = EmptyPagePlaceholder(
|
||||
showReadyCard: true,
|
||||
title: 'users.nobody_here'.tr(),
|
||||
description: 'basis.please_connect'.tr(),
|
||||
iconData: BrandIcons.users,
|
||||
);
|
||||
} else {
|
||||
child = BlocBuilder<UsersCubit, UsersState>(
|
||||
builder: (final BuildContext context, final UsersState state) {
|
||||
|
@ -64,8 +68,10 @@ class UsersPage extends StatelessWidget {
|
|||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_CouldNotLoadUsers(
|
||||
text: 'users.could_not_fetch_description'.tr(),
|
||||
EmptyPagePlaceholder(
|
||||
title: 'users.could_not_fetch_users'.tr(),
|
||||
description: 'users.could_not_fetch_description'.tr(),
|
||||
iconData: BrandIcons.users,
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
BrandOutlinedButton(
|
||||
|
@ -132,24 +138,4 @@ class UsersPage extends StatelessWidget {
|
|||
body: child,
|
||||
);
|
||||
}
|
||||
|
||||
Widget isNotReady() => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: NotReadyCard(),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Center(
|
||||
child: _NoUsers(
|
||||
text: 'users.not_ready'.tr(),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue