[ui] UI theme should match repertory blue #61
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good

This commit is contained in:
2025-08-17 17:22:35 -05:00
parent 31dbed7a1f
commit 1ccfae7552
7 changed files with 185 additions and 187 deletions

View File

@@ -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<String?> 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<String>(
hint: const Text("Select drive"),
value: currentLocation,
onChanged: (value) => setState(() => currentLocation = value),
items: available.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
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<String>(
hint: const Text("Select drive"),
value: currentLocation,
onChanged: (value) =>
setState(() => currentLocation = value),
items: available.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
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,
);
},
);

View File

@@ -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;

View File

@@ -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<AddMountScreen> {
),
),
),
const AuroraSweep(
enabled: true,
duration: Duration(seconds: 28),
primaryAlphaA: 0.04,
primaryAlphaB: 0.03,
Consumer<Settings>(
builder: (_, settings, _) {
return AuroraSweep(enabled: settings.enableAnimations);
},
),
Positioned.fill(
child: BackdropFilter(
@@ -214,7 +214,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
),
const SizedBox(width: constants.padding),
DropdownButton<String>(
value: _mountType,
value: _mountType.isEmpty ? null : _mountType,
autofocus: true,
underline: const SizedBox.shrink(),
onChanged: (mountType) {
@@ -298,99 +298,108 @@ class _AddMountScreenState extends State<AddMountScreen> {
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<MountList>(
context,
listen: false,
);
List<String> 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<MountList>(
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<String> 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);
},
),
),
],
),

View File

@@ -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<AuthScreen> {
),
),
),
const AuroraSweep(),
Consumer<Settings>(
builder: (_, settings, _) {
return AuroraSweep(enabled: settings.enableAnimations);
},
),
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),

View File

@@ -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<EditMountScreen> {
),
),
),
const AuroraSweep(
enabled: true,
duration: Duration(seconds: 28),
primaryAlphaA: 0.04,
primaryAlphaB: 0.03,
Consumer<Settings>(
builder: (_, settings, _) {
return AuroraSweep(enabled: settings.enableAnimations);
},
),
Positioned.fill(
child: BackdropFilter(

View File

@@ -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<EditSettingsScreen> {
),
),
),
const AuroraSweep(
enabled: true,
duration: Duration(seconds: 28),
primaryAlphaA: 0.04,
primaryAlphaB: 0.03,
Consumer<Settings>(
builder: (_, settings, _) {
return AuroraSweep(enabled: settings.enableAnimations);
},
),
Positioned.fill(
child: BackdropFilter(

View File

@@ -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<HomeScreen> {
),
),
),
const AuroraSweep(
enabled: true,
duration: Duration(seconds: 28),
primaryAlphaA: 0.04,
primaryAlphaB: 0.03,
Consumer<Settings>(
builder: (_, settings, _) {
return AuroraSweep(enabled: settings.enableAnimations);
},
),
Positioned.fill(
child: BackdropFilter(