From 1ccfae75524a810512497507dbd168dc7d84d1ca Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 17 Aug 2025 17:22:35 -0500 Subject: [PATCH] [ui] UI theme should match repertory blue #61 --- web/repertory/lib/helpers.dart | 160 +++++++---------- web/repertory/lib/models/settings.dart | 6 + .../lib/screens/add_mount_screen.dart | 169 +++++++++--------- web/repertory/lib/screens/auth_screen.dart | 7 +- .../lib/screens/edit_mount_screen.dart | 10 +- .../lib/screens/edit_settings_screen.dart | 10 +- web/repertory/lib/screens/home_screen.dart | 10 +- 7 files changed, 185 insertions(+), 187 deletions(-) diff --git a/web/repertory/lib/helpers.dart b/web/repertory/lib/helpers.dart index 2b2f5bf5..6304abc4 100644 --- a/web/repertory/lib/helpers.dart +++ b/web/repertory/lib/helpers.dart @@ -8,6 +8,30 @@ import 'package:repertory/constants.dart' as constants; import 'package:repertory/models/auth.dart'; import 'package:sodium_libs/sodium_libs.dart' show SecureKey, StringX; +Future doShowDialog(BuildContext context, Widget child) => showDialog( + context: context, + builder: (context) { + final theme = Theme.of(context); + final scheme = theme.colorScheme; + return Theme( + data: theme.copyWith( + dialogTheme: DialogThemeData( + backgroundColor: scheme.primary.withValues(alpha: 0.15), + surfaceTintColor: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(constants.borderRadius), + side: BorderSide( + color: scheme.outlineVariant.withValues(alpha: 0.08), + width: 1, + ), + ), + ), + ), + child: child, + ); + }, +); + typedef Validator = bool Function(String); class NullPasswordException implements Exception { @@ -347,98 +371,52 @@ Future editMountLocation( }) async { String? currentLocation = location; final controller = TextEditingController(text: currentLocation); - return await showDialog( - context: context, - builder: (context) { - var theme = Theme.of(context); - var scheme = theme.colorScheme; - return Theme( - data: theme.copyWith( - dialogTheme: DialogThemeData( - backgroundColor: scheme.surface.withValues(alpha: 0.40), - surfaceTintColor: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(constants.borderRadius), - side: BorderSide( - color: scheme.outlineVariant.withValues(alpha: 0.08), - width: 1, - ), + return await doShowDialog( + context, + StatefulBuilder( + builder: (context, setState) { + return AlertDialog( + actions: [ + TextButton( + child: const Text('Cancel'), + onPressed: () => Navigator.of(context).pop(null), ), - ), - ), - child: StatefulBuilder( - builder: (context, setState) { - return AlertDialog( - actions: [ - TextButton( - child: const Text('Cancel'), - onPressed: () => Navigator.of(context).pop(null), + TextButton( + child: const Text('OK'), + onPressed: () { + final result = getSettingValidators('Path').firstWhereOrNull( + (validator) => !validator(currentLocation ?? ''), + ); + if (result != null) { + return displayErrorMessage( + context, + "Mount location is not valid", + ); + } + Navigator.of(context).pop(currentLocation); + }, + ), + ], + content: available.isEmpty + ? TextField( + autofocus: true, + controller: controller, + onChanged: (value) => setState(() => currentLocation = value), + ) + : DropdownButton( + hint: const Text("Select drive"), + value: currentLocation, + onChanged: (value) => setState(() => currentLocation = value), + items: available.map>((item) { + return DropdownMenuItem( + value: item, + child: Text(item), + ); + }).toList(), ), - TextButton( - child: const Text('OK'), - onPressed: () { - final result = getSettingValidators('Path') - .firstWhereOrNull( - (validator) => !validator(currentLocation ?? ''), - ); - if (result != null) { - return displayErrorMessage( - context, - "Mount location is not valid", - ); - } - Navigator.of(context).pop(currentLocation); - }, - ), - ], - content: available.isEmpty - ? TextField( - autofocus: true, - controller: controller, - onChanged: (value) => - setState(() => currentLocation = value), - ) - : DropdownButton( - hint: const Text("Select drive"), - value: currentLocation, - onChanged: (value) => - setState(() => currentLocation = value), - items: available.map>((item) { - return DropdownMenuItem( - value: item, - child: Text(item), - ); - }).toList(), - ), - title: const Text('Mount Location', textAlign: TextAlign.center), - ); - }, - ), - ); - }, + title: const Text('Mount Location', textAlign: TextAlign.center), + ); + }, + ), ); } - -Future doShowDialog(BuildContext context, Widget child) => showDialog( - context: context, - builder: (context) { - final theme = Theme.of(context); - final scheme = theme.colorScheme; - return Theme( - data: theme.copyWith( - dialogTheme: DialogThemeData( - backgroundColor: scheme.surface.withValues(alpha: 0.40), - surfaceTintColor: Colors.transparent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(constants.borderRadius), - side: BorderSide( - color: scheme.outlineVariant.withValues(alpha: 0.08), - width: 1, - ), - ), - ), - ), - child: child, - ); - }, -); diff --git a/web/repertory/lib/models/settings.dart b/web/repertory/lib/models/settings.dart index 8a8decd2..1f3946d1 100644 --- a/web/repertory/lib/models/settings.dart +++ b/web/repertory/lib/models/settings.dart @@ -8,6 +8,7 @@ import 'package:repertory/models/auth.dart'; class Settings with ChangeNotifier { final Auth _auth; bool _autoStart = false; + bool _enableAnimations = true; Settings(this._auth) { _auth.addListener(() { @@ -18,6 +19,11 @@ class Settings with ChangeNotifier { } bool get autoStart => _autoStart; + bool get enableAnimations => _enableAnimations; + set enableAnimations(bool enable) { + _enableAnimations = enable; + notifyListeners(); + } void _reset() { _autoStart = false; diff --git a/web/repertory/lib/screens/add_mount_screen.dart b/web/repertory/lib/screens/add_mount_screen.dart index 0f626e11..d8356b6a 100644 --- a/web/repertory/lib/screens/add_mount_screen.dart +++ b/web/repertory/lib/screens/add_mount_screen.dart @@ -9,6 +9,7 @@ import 'package:repertory/helpers.dart'; import 'package:repertory/models/auth.dart'; import 'package:repertory/models/mount.dart'; import 'package:repertory/models/mount_list.dart'; +import 'package:repertory/models/settings.dart'; import 'package:repertory/types/mount_config.dart'; import 'package:repertory/widgets/aurora_sweep.dart'; import 'package:repertory/widgets/mount_settings.dart'; @@ -92,11 +93,10 @@ class _AddMountScreenState extends State { ), ), ), - const AuroraSweep( - enabled: true, - duration: Duration(seconds: 28), - primaryAlphaA: 0.04, - primaryAlphaB: 0.03, + Consumer( + builder: (_, settings, _) { + return AuroraSweep(enabled: settings.enableAnimations); + }, ), Positioned.fill( child: BackdropFilter( @@ -214,7 +214,7 @@ class _AddMountScreenState extends State { ), const SizedBox(width: constants.padding), DropdownButton( - value: _mountType, + value: _mountType.isEmpty ? null : _mountType, autofocus: true, underline: const SizedBox.shrink(), onChanged: (mountType) { @@ -298,99 +298,108 @@ class _AddMountScreenState extends State { const SizedBox(height: constants.padding), Row( children: [ - ElevatedButton.icon( - label: const Text('Test'), - icon: const Icon(Icons.check), - style: ElevatedButton.styleFrom( - backgroundColor: scheme.primary.withValues( - alpha: 0.18, - ), - foregroundColor: scheme.primary, - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - constants.borderRadius, + IntrinsicWidth( + child: ElevatedButton.icon( + label: const Text('Test'), + icon: const Icon(Icons.check), + style: ElevatedButton.styleFrom( + backgroundColor: scheme.primary.withValues( + alpha: 0.18, ), - side: BorderSide( - color: scheme.outlineVariant.withValues( - alpha: 0.15, + foregroundColor: scheme.primary, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + constants.borderRadius, + ), + side: BorderSide( + color: scheme.outlineVariant.withValues( + alpha: 0.15, + ), + width: 1, ), - width: 1, ), ), + onPressed: _handleProviderTest, ), - onPressed: _handleProviderTest, ), const SizedBox(width: constants.padding), - ElevatedButton.icon( - label: const Text('Add'), - icon: const Icon(Icons.add), - style: ElevatedButton.styleFrom( - backgroundColor: scheme.primary, - foregroundColor: scheme.onPrimary, - elevation: 8, - shadowColor: scheme.primary.withValues(alpha: 0.45), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - constants.borderRadius, + IntrinsicWidth( + child: ElevatedButton.icon( + label: const Text('Add'), + icon: const Icon(Icons.add), + style: ElevatedButton.styleFrom( + backgroundColor: scheme.primary, + foregroundColor: scheme.onPrimary, + elevation: 8, + shadowColor: scheme.primary.withValues( + alpha: 0.45, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + constants.borderRadius, + ), ), ), - ), - onPressed: () async { - final mountList = Provider.of( - context, - listen: false, - ); - - List failed = []; - if (!validateSettings( - _settings[_mountType]!, - failed, - )) { - for (var key in failed) { - displayErrorMessage( - context, - "Setting '$key' is not valid", - ); - } - return; - } - - if (mountList.hasConfigName( - _mountNameController.text, - )) { - return displayErrorMessage( + onPressed: () async { + final mountList = Provider.of( context, - "Configuration name '${_mountNameController.text}' already exists", + listen: false, ); - } - if (_mountType == "Sia" || _mountType == "S3") { - final bucket = - _settings[_mountType]!["${_mountType}Config"]["Bucket"] - as String; - if (mountList.hasBucketName(_mountType, bucket)) { + List failed = []; + if (!validateSettings( + _settings[_mountType]!, + failed, + )) { + for (var key in failed) { + displayErrorMessage( + context, + "Setting '$key' is not valid", + ); + } + return; + } + + if (mountList.hasConfigName( + _mountNameController.text, + )) { return displayErrorMessage( context, - "Bucket '$bucket' already exists", + "Configuration name '${_mountNameController.text}' already exists", ); } - } - final success = await mountList.add( - _mountType, - _mountType == 'Remote' - ? '${_settings[_mountType]!['RemoteConfig']['HostNameOrIp']}_${_settings[_mountType]!['RemoteConfig']['ApiPort']}' - : _mountNameController.text, - _settings[_mountType]!, - ); + if (_mountType == "Sia" || _mountType == "S3") { + final bucket = + _settings[_mountType]!["${_mountType}Config"]["Bucket"] + as String; + if (mountList.hasBucketName( + _mountType, + bucket, + )) { + return displayErrorMessage( + context, + "Bucket '$bucket' already exists", + ); + } + } - if (!success || !context.mounted) { - return; - } + final success = await mountList.add( + _mountType, + _mountType == 'Remote' + ? '${_settings[_mountType]!['RemoteConfig']['HostNameOrIp']}_${_settings[_mountType]!['RemoteConfig']['ApiPort']}' + : _mountNameController.text, + _settings[_mountType]!, + ); - Navigator.pop(context); - }, + if (!success || !context.mounted) { + return; + } + + Navigator.pop(context); + }, + ), ), ], ), diff --git a/web/repertory/lib/screens/auth_screen.dart b/web/repertory/lib/screens/auth_screen.dart index 9ce13c4e..1b01a6b2 100644 --- a/web/repertory/lib/screens/auth_screen.dart +++ b/web/repertory/lib/screens/auth_screen.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:repertory/constants.dart' as constants; import 'package:repertory/models/auth.dart'; +import 'package:repertory/models/settings.dart'; import 'package:repertory/widgets/aurora_sweep.dart'; class AuthScreen extends StatefulWidget { @@ -97,7 +98,11 @@ class _AuthScreenState extends State { ), ), ), - const AuroraSweep(), + Consumer( + builder: (_, settings, _) { + return AuroraSweep(enabled: settings.enableAnimations); + }, + ), Positioned.fill( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), diff --git a/web/repertory/lib/screens/edit_mount_screen.dart b/web/repertory/lib/screens/edit_mount_screen.dart index d405a96f..552c20f3 100644 --- a/web/repertory/lib/screens/edit_mount_screen.dart +++ b/web/repertory/lib/screens/edit_mount_screen.dart @@ -8,6 +8,7 @@ import 'package:provider/provider.dart'; import 'package:repertory/constants.dart' as constants; import 'package:repertory/models/auth.dart'; import 'package:repertory/models/mount.dart'; +import 'package:repertory/models/settings.dart'; import 'package:repertory/widgets/aurora_sweep.dart'; import 'package:repertory/widgets/mount_settings.dart'; @@ -43,11 +44,10 @@ class _EditMountScreenState extends State { ), ), ), - const AuroraSweep( - enabled: true, - duration: Duration(seconds: 28), - primaryAlphaA: 0.04, - primaryAlphaB: 0.03, + Consumer( + builder: (_, settings, _) { + return AuroraSweep(enabled: settings.enableAnimations); + }, ), Positioned.fill( child: BackdropFilter( diff --git a/web/repertory/lib/screens/edit_settings_screen.dart b/web/repertory/lib/screens/edit_settings_screen.dart index 5122abf2..15845eef 100644 --- a/web/repertory/lib/screens/edit_settings_screen.dart +++ b/web/repertory/lib/screens/edit_settings_screen.dart @@ -9,6 +9,7 @@ import 'package:provider/provider.dart'; import 'package:repertory/constants.dart' as constants; import 'package:repertory/helpers.dart'; import 'package:repertory/models/auth.dart'; +import 'package:repertory/models/settings.dart'; import 'package:repertory/widgets/aurora_sweep.dart'; import 'package:repertory/widgets/ui_settings.dart'; @@ -41,11 +42,10 @@ class _EditSettingsScreenState extends State { ), ), ), - const AuroraSweep( - enabled: true, - duration: Duration(seconds: 28), - primaryAlphaA: 0.04, - primaryAlphaB: 0.03, + Consumer( + builder: (_, settings, _) { + return AuroraSweep(enabled: settings.enableAnimations); + }, ), Positioned.fill( child: BackdropFilter( diff --git a/web/repertory/lib/screens/home_screen.dart b/web/repertory/lib/screens/home_screen.dart index ddfc4fba..c23fd534 100644 --- a/web/repertory/lib/screens/home_screen.dart +++ b/web/repertory/lib/screens/home_screen.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:repertory/constants.dart' as constants; import 'package:repertory/models/auth.dart'; +import 'package:repertory/models/settings.dart'; import 'package:repertory/widgets/mount_list_widget.dart'; import 'package:repertory/widgets/aurora_sweep.dart'; @@ -37,11 +38,10 @@ class _HomeScreeState extends State { ), ), ), - const AuroraSweep( - enabled: true, - duration: Duration(seconds: 28), - primaryAlphaA: 0.04, - primaryAlphaB: 0.03, + Consumer( + builder: (_, settings, _) { + return AuroraSweep(enabled: settings.enableAnimations); + }, ), Positioned.fill( child: BackdropFilter(