Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Security and Privacy section improved with UI #2639

Merged
merged 6 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changes/2639-security-privacy-section-improvement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- [improvement] : Improved security and privacy UI on the activity page.
2 changes: 1 addition & 1 deletion app/lib/common/themes/acter_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class ActerTheme {
dialogTheme: dialogTheme,
bottomSheetTheme: bottomSheetTheme,
elevatedButtonTheme: elevatedButtonTheme(colorScheme),
outlinedButtonTheme: outlinedButtonTheme,
outlinedButtonTheme: outlinedButtonTheme(colorScheme, textTheme),
textButtonTheme: textButtonTheme(colorScheme),
iconButtonTheme: iconButtonTheme,
inputDecorationTheme: inputDecorationTheme,
Expand Down
2 changes: 2 additions & 0 deletions app/lib/common/themes/colors/color_scheme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ Color backgroundColor = const Color(0xff0d0f11);

//On colors
Color whiteColor = Colors.white;
Color warningColor = Colors.amber;

Color pinFeatureColor = const Color(0xff7c4a4a);
Color eventFeatureColor = const Color(0xff206a9a);
Color taskFeatureColor = const Color(0xff406c6e);
Color boastFeatureColor = Colors.blueGrey;


var colorScheme = ColorScheme.dark(
brightness: Brightness.dark,
//Primary
Expand Down
35 changes: 24 additions & 11 deletions app/lib/common/themes/components/button_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import 'package:flutter/material.dart';
ElevatedButtonThemeData elevatedButtonTheme(ColorScheme colors) =>
ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(18),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
elevation: 0,
backgroundColor: colors.primary,
foregroundColor: Colors.white,
Expand Down Expand Up @@ -38,16 +41,26 @@ TextButtonThemeData inlineTextButtonThemeMaker(ColorScheme colors) =>
),
);

final outlinedButtonTheme = OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.all(18),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
foregroundColor: Colors.white,
backgroundColor: Colors.transparent,
),
);
OutlinedButtonThemeData outlinedButtonTheme(ColorScheme colors, TextTheme textTheme) =>
OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: colors.primary,
side: BorderSide(color: colors.primary),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
textStyle: textTheme.titleMedium?.copyWith(
color: colors.primary,
fontSize: 15,
),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
);

TextButtonThemeData textButtonTheme(ColorScheme colors) => TextButtonThemeData(
style: TextButton.styleFrom(
Expand Down
2 changes: 1 addition & 1 deletion app/lib/features/activities/pages/activities_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import 'package:acter/common/providers/common_providers.dart';
import 'package:acter/common/utils/routes.dart';
import 'package:acter/common/widgets/empty_state_widget.dart';
import 'package:acter/features/activities/providers/activities_providers.dart';
import 'package:acter/features/backups/widgets/backup_state_widget.dart';
import 'package:acter/features/invitations/providers/invitations_providers.dart';
import 'package:acter/features/invitations/widgets/invitation_card.dart';
import 'package:acter/features/backups/widgets/backup_state_widget.dart';
import 'package:acter/features/home/providers/client_providers.dart';
import 'package:acter/features/invitations/widgets/has_invites_tile.dart';
import 'package:acter/features/labs/model/labs_features.dart';
Expand Down
74 changes: 74 additions & 0 deletions app/lib/features/activities/widgets/security_privacy_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class SecurityPrivacyWidget extends ConsumerWidget {
final IconData icon;
final Color? iconColor;
final String title;
final String subtitle;
final List<Widget> actions;

const SecurityPrivacyWidget({
super.key,
required this.icon,
this.iconColor,
required this.title,
required this.subtitle,
required this.actions,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 3,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildLeadingIconUI(context),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildTitleSubtitleUI(context),
const SizedBox(height: 12),
if (actions.isNotEmpty) Row(children: actions),
],
),
),
],
),
),
);
}

Widget buildLeadingIconUI(BuildContext context) {
return Icon(
icon,
size: 24,
color: iconColor ?? Theme.of(context).colorScheme.error,
);
}

Widget buildTitleSubtitleUI(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
ConstrainedBox(
key: Key('subtitle-key'),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width - 100,
),
child: Text(subtitle, style: Theme.of(context).textTheme.bodyMedium),
),
],
);
}
}
107 changes: 59 additions & 48 deletions app/lib/features/backups/widgets/backup_state_widget.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:acter/common/themes/colors/color_scheme.dart';
import 'package:acter/features/activities/widgets/security_privacy_widget.dart';
import 'package:acter/features/backups/dialogs/provide_recovery_key_dialog.dart';
import 'package:acter/features/backups/dialogs/show_confirm_disabling.dart';
import 'package:acter/features/backups/dialogs/show_recovery_key.dart';
Expand Down Expand Up @@ -25,7 +27,8 @@
@override
Widget build(BuildContext context, WidgetRef ref) {
return switch (ref.watch(backupStateProvider)) {
RecoveryState.enabled => allowDisabling
RecoveryState.enabled =>
allowDisabling
? renderCanResetAction(context, ref)
: const SizedBox.shrink(), // nothing to see here. all good.
RecoveryState.incomplete => renderRecoverAction(context, ref),
Expand All @@ -37,72 +40,82 @@
Widget renderUnknown(BuildContext context, WidgetRef ref) {
final lang = L10n.of(context);
return Skeletonizer(
child: Card(
child: ListTile(
leading: const Icon(Icons.warning),
title: Text(lang.encryptionBackupMissing),
subtitle: Text(lang.encryptionBackupMissingExplainer),
trailing: OutlinedButton(
onPressed: () {},
child: SecurityPrivacyWidget(
icon: Icons.warning_amber_rounded,
iconColor: warningColor,
title: lang.encryptionBackupMissing,
subtitle: lang.encryptionBackupMissingExplainer,
actions: [
OutlinedButton(
onPressed: null,
child: Text(lang.loading),
),
),
],

),
);
}

Widget renderCanResetAction(BuildContext context, WidgetRef ref) {
final lang = L10n.of(context);
return Card(
child: ListTile(
leading: const Icon(Atlas.check_website_thin),
title: Text(lang.encryptionBackupEnabled),
subtitle: Text(lang.encryptionBackupEnabledExplainer),
trailing: OutlinedButton.icon(
icon: const Icon(Icons.toggle_on_outlined),
return SecurityPrivacyWidget(
icon: Atlas.check_website_thin,
iconColor: Theme
.of(context)
.colorScheme
.primary,
title: lang.encryptionBackupEnabled,
subtitle: lang.encryptionBackupEnabledExplainer,
actions: [
OutlinedButton(
onPressed: () => showConfirmResetDialog(context, ref),
label: Text(lang.reset),
child: Text(lang.reset),
),
),
],

);
}

Widget renderRecoverAction(BuildContext context, WidgetRef ref) {
final lang = L10n.of(context);
return Card(
child: ListTile(
leading: const Icon(Icons.warning),
title: Text(lang.encryptionBackupProvideKey),
subtitle: Text(lang.encryptionBackupProvideKeyExplainer),
trailing: Wrap(
children: [
OutlinedButton(
onPressed: () => showProviderRecoveryKeyDialog(context, ref),
child: Text(lang.encryptionBackupProvideKeyAction),
),
if (allowDisabling)
OutlinedButton(
onPressed: () => showConfirmResetDialog(context, ref),
child: Text(lang.reset),
),
],
// Since SecurityPrivacyWidget only supports one action button,
// we'll use the primary action (provide key) and handle reset differently if needed
return SecurityPrivacyWidget(
icon: Icons.warning_amber_rounded,
iconColor: warningColor,
title: lang.encryptionBackupProvideKey,
subtitle: lang.encryptionBackupProvideKeyExplainer,

actions: [
OutlinedButton(
onPressed: () => showProviderRecoveryKeyDialog(context, ref),

Check warning on line 91 in app/lib/features/backups/widgets/backup_state_widget.dart

View check run for this annotation

Codecov / codecov/patch

app/lib/features/backups/widgets/backup_state_widget.dart#L91

Added line #L91 was not covered by tests
child: Text(lang.encryptionBackupProvideKeyAction),
),
),
if (allowDisabling) ...[
const SizedBox(width: 8),
OutlinedButton(
onPressed: () => showConfirmResetDialog(context, ref),

Check warning on line 97 in app/lib/features/backups/widgets/backup_state_widget.dart

View check run for this annotation

Codecov / codecov/patch

app/lib/features/backups/widgets/backup_state_widget.dart#L97

Added line #L97 was not covered by tests
child: Text(lang.reset),
),
],
],
);
}

Widget renderStartAction(BuildContext context, WidgetRef ref) {
final lang = L10n.of(context);
return Card(
child: ListTile(
leading: const Icon(Icons.warning),
title: Text(lang.encryptionBackupNoBackup),
subtitle: Text(lang.encryptionBackupNoBackupExplainer),
trailing: OutlinedButton(
return SecurityPrivacyWidget(
icon: Icons.warning_amber_rounded,
iconColor: warningColor,
title: lang.encryptionBackupNoBackup,
subtitle: lang.encryptionBackupNoBackupExplainer,
actions: [
OutlinedButton(
onPressed: () => startAction(context, ref),
child: Text(lang.encryptionBackupNoBackupAction),
),
),
],

);
}

Expand Down Expand Up @@ -131,11 +144,9 @@
}
}

Widget renderInProgress(
BuildContext context,
WidgetRef ref,
RecoveryState currentState,
) {
Widget renderInProgress(BuildContext context,

Check warning on line 147 in app/lib/features/backups/widgets/backup_state_widget.dart

View check run for this annotation

Codecov / codecov/patch

app/lib/features/backups/widgets/backup_state_widget.dart#L147

Added line #L147 was not covered by tests
WidgetRef ref,
RecoveryState currentState,) {
return Card(
child: Column(
children: [
Expand Down
6 changes: 5 additions & 1 deletion app/lib/features/space/widgets/space_toolbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,16 @@
horizontal: 12,
vertical: 6,
),
side: BorderSide(color: colorScheme.outline),

Check warning on line 110 in app/lib/features/space/widgets/space_toolbar.dart

View check run for this annotation

Codecov / codecov/patch

app/lib/features/space/widgets/space_toolbar.dart#L110

Added line #L110 was not covered by tests
),
onPressed: () => context.pushNamed(
Routes.spaceInvite.name,
pathParameters: {'spaceId': spaceId},
),
child: Text(lang.invite),
child: Text(
lang.invite,
style: TextStyle(color: colorScheme.outline),

Check warning on line 118 in app/lib/features/space/widgets/space_toolbar.dart

View check run for this annotation

Codecov / codecov/patch

app/lib/features/space/widgets/space_toolbar.dart#L116-L118

Added lines #L116 - L118 were not covered by tests
),
),
IconButton(
icon: Icon(isBookmarked ? Icons.bookmark : Icons.bookmark_border),
Expand Down
12 changes: 11 additions & 1 deletion app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1113,7 +1113,7 @@
"@sentAnImage": {},
"server": "Server",
"@server": {},
"sessions": "Sessions",
"sessions": "Session",
"@sessions": {},
"sessionTokenName": "Session Token Name",
"@sessionTokenName": {},
Expand Down Expand Up @@ -1309,6 +1309,16 @@
"@unverifiedSessions": {},
"unverifiedSessionsDescription": "You have devices logged in your account that aren’t verified. This can be a security risk. Please ensure this is okay.",
"@unverifiedSessionsDescription": {},
"unverifiedSessionsCount": "There are {count} unverified sessions logged in",
"@unverifiedSessionsCount": {
"description": "Text shown for number of unverified sessions",
"placeholders": {
"count": {
"type": "int",
"example": "6"
}
}
},
"upcoming": "Upcoming",
"@upcoming": {},
"updatePowerLevel": "Update Permission level",
Expand Down
Loading
Loading