diff --git a/lib/components/enter_password.dart b/lib/components/enter_password.dart new file mode 100644 index 0000000..a1b06ab --- /dev/null +++ b/lib/components/enter_password.dart @@ -0,0 +1,128 @@ +import 'package:awesome_extensions/awesome_extensions.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_translate/flutter_translate.dart'; + +import '../tools/tools.dart'; + +class EnterPasswordDialog extends ConsumerStatefulWidget { + const EnterPasswordDialog({ + this.matchPass, + this.description, + super.key, + }); + + final String? matchPass; + final String? description; + + @override + EnterPasswordDialogState createState() => EnterPasswordDialogState(); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(StringProperty('reenter', matchPass)) + ..add(StringProperty('description', description)); + } +} + +class EnterPasswordDialogState extends ConsumerState { + final passwordController = TextEditingController(); + final focusNode = FocusNode(); + final formKey = GlobalKey(); + bool _passwordVisible = false; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + passwordController.dispose(); + focusNode.dispose(); + super.dispose(); + } + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + final theme = Theme.of(context); + final scale = theme.extension()!; + + return Dialog( + backgroundColor: scale.grayScale.subtleBackground, + child: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.matchPass == null + ? translate('enter_password_dialog.enter_password') + : translate('enter_password_dialog.reenter_password'), + style: theme.textTheme.titleLarge, + ).paddingAll(16), + TextField( + controller: passwordController, + focusNode: focusNode, + autofocus: true, + enableSuggestions: false, + obscureText: + !_passwordVisible, //This will obscure text dynamically + inputFormatters: [ + FilteringTextInputFormatter.singleLineFormatter + ], + onSubmitted: (password) { + Navigator.pop(context, password); + }, + onChanged: (_) { + setState(() {}); + }, + decoration: InputDecoration( + prefixIcon: widget.matchPass == null + ? null + : Icon(Icons.check_circle, + color: passwordController.text == widget.matchPass + ? scale.primaryScale.background + : scale.grayScale.subtleBackground), + suffixIcon: IconButton( + icon: Icon( + _passwordVisible + ? Icons.visibility + : Icons.visibility_off, + color: scale.primaryScale.text, + ), + onPressed: () { + setState(() { + _passwordVisible = !_passwordVisible; + }); + }, + ), + )).paddingAll(16), + if (widget.description != null) + SizedBox( + width: 400, + child: Text( + widget.description!, + textAlign: TextAlign.center, + ).paddingAll(16)) + ], + ), + )); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty( + 'passwordController', passwordController)) + ..add(DiagnosticsProperty('focusNode', focusNode)) + ..add(DiagnosticsProperty>('formKey', formKey)); + } +} diff --git a/lib/tools/loggy.dart b/lib/tools/loggy.dart new file mode 100644 index 0000000..b1f7018 --- /dev/null +++ b/lib/tools/loggy.dart @@ -0,0 +1,104 @@ +import 'dart:io' show Platform; + +import 'package:ansicolor/ansicolor.dart'; +import 'package:flutter/foundation.dart'; +import 'package:loggy/loggy.dart'; + +import '../veilid_support/veilid_support.dart'; + +String wrapWithLogColor(LogLevel? level, String text) { + // XXX: https://github.com/flutter/flutter/issues/64491 + if (!kIsWeb && Platform.isIOS) { + return text; + } + + if (level == null) { + return text; + } + final pen = AnsiPen(); + ansiColorDisabled = false; + switch (level) { + case LogLevel.error: + pen + ..reset() + ..red(bold: true); + return pen(text); + case LogLevel.warning: + pen + ..reset() + ..yellow(bold: true); + return pen(text); + case LogLevel.info: + pen + ..reset() + ..white(bold: true); + return pen(text); + case LogLevel.debug: + pen + ..reset() + ..green(bold: true); + return pen(text); + case traceLevel: + pen + ..reset() + ..blue(bold: true); + return pen(text); + } + return text; +} + +extension PrettyPrintLogRecord on LogRecord { + String pretty() { + final lstr = + wrapWithLogColor(level, '[${level.toString().substring(0, 1)}]'); + return '$lstr $message'; + } +} + +class CallbackPrinter extends LoggyPrinter { + CallbackPrinter() : super(); + + void Function(LogRecord)? callback; + + @override + void onLog(LogRecord record) { + debugPrint(record.pretty()); + callback?.call(record); + } + + void setCallback(void Function(LogRecord)? cb) { + callback = cb; + } +} + +CallbackPrinter globalTerminalPrinter = CallbackPrinter(); + +LogOptions getLogOptions(LogLevel? level) => LogOptions( + level ?? LogLevel.all, + stackTraceLevel: LogLevel.error, + ); + +class RootLoggy implements LoggyType { + @override + Loggy get loggy => Loggy(''); +} + +Loggy get log => Loggy('veilidchat'); + +void initLoggy() { + Loggy.initLoggy( + logPrinter: globalTerminalPrinter, + logOptions: getLogOptions(null), + ); + + // ignore: do_not_use_environment + const isTrace = String.fromEnvironment('LOG_TRACE') != ''; + LogLevel logLevel; + if (isTrace) { + logLevel = traceLevel; + } else { + logLevel = kDebugMode ? LogLevel.debug : LogLevel.info; + } + + Loggy('').level = getLogOptions(logLevel); +} diff --git a/lib/tools/state_logger.dart b/lib/tools/state_logger.dart new file mode 100644 index 0000000..973b5f9 --- /dev/null +++ b/lib/tools/state_logger.dart @@ -0,0 +1,22 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'loggy.dart'; + +class StateLogger extends ProviderObserver { + const StateLogger(); + @override + void didUpdateProvider( + ProviderBase provider, + Object? previousValue, + Object? newValue, + ProviderContainer container, + ) { + log.debug(''' +{ + provider: ${provider.name ?? provider.runtimeType}, + oldValue: $previousValue, + newValue: $newValue +} +'''); + super.didUpdateProvider(provider, previousValue, newValue, container); + } +}