contact invitation algorithm

This commit is contained in:
Christien Rioux 2023-08-02 21:09:28 -04:00
parent c35056f687
commit f52094c105
43 changed files with 1319 additions and 451 deletions

View file

@ -0,0 +1,67 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class BottomSheetActionButton extends ConsumerStatefulWidget {
const BottomSheetActionButton(
{required this.bottomSheetBuilder,
required this.builder,
this.foregroundColor,
this.backgroundColor,
this.shape,
super.key});
final Color? foregroundColor;
final Color? backgroundColor;
final ShapeBorder? shape;
final Widget Function(BuildContext) builder;
final Widget Function(BuildContext) bottomSheetBuilder;
@override
BottomSheetActionButtonState createState() => BottomSheetActionButtonState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(ObjectFlagProperty<Widget Function(BuildContext p1)>.has(
'bottomSheetBuilder', bottomSheetBuilder))
..add(ColorProperty('foregroundColor', foregroundColor))
..add(ColorProperty('backgroundColor', backgroundColor))
..add(DiagnosticsProperty<ShapeBorder?>('shape', shape))
..add(ObjectFlagProperty<Widget? Function(BuildContext p1)>.has(
'builder', builder));
}
}
class BottomSheetActionButtonState
extends ConsumerState<BottomSheetActionButton> {
bool _showFab = true;
@override
void initState() {
super.initState();
}
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
//
return _showFab
? FloatingActionButton(
shape: widget.shape,
foregroundColor: widget.foregroundColor,
backgroundColor: widget.backgroundColor,
child: widget.builder(context),
onPressed: () async {
await showModalBottomSheet<void>(
context: context, builder: widget.bottomSheetBuilder);
},
)
: Container();
}
void showFloatingActionButton(bool value) {
setState(() {
_showFab = value;
});
}
}

View file

@ -5,6 +5,8 @@ import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
import 'package:intl/date_symbol_data_local.dart';
import 'package:uuid/uuid.dart';
import '../tools/theme_service.dart';
class ChatComponent extends ConsumerStatefulWidget {
const ChatComponent({super.key});
@ -63,56 +65,68 @@ class ChatComponentState extends ConsumerState<ChatComponent> {
_addMessage(textMessage);
}
void _handleAttachmentPressed() {
//
}
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final chatTheme = scale.toChatTheme();
final textTheme = Theme.of(context).textTheme;
//
return Align(
alignment: AlignmentDirectional.centerEnd,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
),
child: Stack(
children: [
Column(
return DefaultTextStyle(
style: textTheme.bodySmall!,
child: Align(
alignment: AlignmentDirectional.centerEnd,
child: Container(
decoration: BoxDecoration(
color: scale.primaryScale.appBackground,
),
child: Stack(
children: [
Container(
height: 48,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
),
child: Align(
alignment: AlignmentDirectional.centerStart,
child: Padding(
padding:
const EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0),
child: Text("current contact",
textAlign: TextAlign.start,
style: Theme.of(context).textTheme.titleMedium),
Column(
children: [
Container(
height: 48,
decoration: BoxDecoration(
color: scale.primaryScale.subtleBackground,
),
child: Align(
alignment: AlignmentDirectional.centerStart,
child: Padding(
padding: const EdgeInsetsDirectional.fromSTEB(
16, 0, 16, 0),
child: Text("current contact",
textAlign: TextAlign.start,
style: textTheme.titleMedium),
),
),
),
),
),
Expanded(
child: Container(
decoration: const BoxDecoration(),
child: Chat(
//theme: _chatTheme,
messages: _messages,
//onAttachmentPressed: _handleAttachmentPressed,
//onMessageTap: _handleMessageTap,
//onPreviewDataFetched: _handlePreviewDataFetched,
onSendPressed: _handleSendPressed,
showUserAvatars: true,
showUserNames: true,
user: _user,
Expanded(
child: Container(
decoration: const BoxDecoration(),
child: Chat(
theme: chatTheme,
messages: _messages,
//onAttachmentPressed: _handleAttachmentPressed,
//onMessageTap: _handleMessageTap,
//onPreviewDataFetched: _handlePreviewDataFetched,
onSendPressed: _handleSendPressed,
showUserAvatars: true,
showUserNames: true,
user: _user,
),
),
),
),
],
),
],
),
],
),
));
)));
}
}

View file

@ -1,25 +1,74 @@
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_animate/flutter_animate.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_translate/flutter_translate.dart';
class ContactInvitationDisplay extends ConsumerWidget {
const ContactInvitationDisplay({super.key});
//final LocalAccount account;
import '../tools/tools.dart';
class ContactInvitationDisplayDialog extends ConsumerStatefulWidget {
const ContactInvitationDisplayDialog({
super.key,
});
// EncryptionKeyType _encryptionKeyType = EncryptionKeyType.none;
// _encryptionKey = '';
@override
Widget build(BuildContext context, WidgetRef ref) {
//final logins = ref.watch(loginsProvider);
ContactInvitationDisplayDialogState createState() =>
ContactInvitationDisplayDialogState();
}
return ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 300),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [const Expanded(child: Text('Contact Invitation'))]));
class ContactInvitationDisplayDialogState
extends ConsumerState<ContactInvitationDisplayDialog> {
final focusNode = FocusNode();
final formKey = GlobalKey<FormState>();
Future<void>? _generateFuture;
@override
void initState() {
super.initState();
if (_generateFuture == null) {
_generateFuture = _generate();
}
}
Future<void> _generate() async {
// Generate invitation
setState(() {
_generateFuture = null;
});
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
//properties.add(DiagnosticsProperty<LocalAccount>('account', account));
void dispose() {
focusNode.dispose();
super.dispose();
}
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final cardsize = MediaQuery.of(context).size.shortestSide - 24;
//
return Dialog(
backgroundColor: Colors.white,
child: SizedBox(
width: cardsize,
height: cardsize,
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [Text("Contact Invitation")]))
.withModalHUD(context, _generateFuture != null)));
}
}

View file

@ -0,0 +1,162 @@
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 'package:pinput/pinput.dart';
import '../tools/tools.dart';
class EnterPinDialog extends ConsumerStatefulWidget {
const EnterPinDialog({
this.matchPin,
this.description,
super.key,
});
final String? matchPin;
final String? description;
@override
EnterPinDialogState createState() => EnterPinDialogState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(StringProperty('matchPin', matchPin))
..add(StringProperty('description', description));
}
}
class EnterPinDialogState extends ConsumerState<EnterPinDialog> {
final pinController = TextEditingController();
final focusNode = FocusNode();
final formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
}
@override
void dispose() {
pinController.dispose();
focusNode.dispose();
super.dispose();
}
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final focusedBorderColor = scale.primaryScale.hoverBorder;
final fillColor = scale.primaryScale.elementBackground;
final borderColor = scale.primaryScale.border;
final defaultPinTheme = PinTheme(
width: 56,
height: 60,
textStyle: TextStyle(fontSize: 22, color: scale.primaryScale.text),
decoration: BoxDecoration(
color: fillColor,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: borderColor),
),
);
/// Optionally you can use form to validate the Pinput
return Dialog(
backgroundColor: scale.grayScale.subtleBackground,
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
widget.matchPin == null
? translate('enter_pin_dialog.enter_pin')
: translate('enter_pin_dialog.reenter_pin'),
style: theme.textTheme.titleLarge,
).paddingAll(16),
Directionality(
// Specify direction if desired
textDirection: TextDirection.ltr,
child: Pinput(
controller: pinController,
focusNode: focusNode,
autofocus: true,
defaultPinTheme: defaultPinTheme,
enableSuggestions: false,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
// validator: (widget.matchPin != null)
// ? (value) => value == widget.matchPin
// ? null
// : translate('enter_pin_dialog.pin_does_not_match')
// : null,
// onClipboardFound: (value) {
// debugPrint('onClipboardFound: $value');
// pinController.setText(value);
// },
hapticFeedbackType: HapticFeedbackType.lightImpact,
onCompleted: (pin) {
debugPrint('onCompleted: $pin');
Navigator.pop(context, pin);
},
onChanged: (value) {
debugPrint('onChanged: $value');
},
cursor: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
margin: const EdgeInsets.only(bottom: 9),
width: 22,
height: 1,
color: focusedBorderColor,
),
],
),
focusedPinTheme: defaultPinTheme.copyWith(
height: 68,
width: 64,
decoration: defaultPinTheme.decoration!.copyWith(
border: Border.all(color: borderColor),
),
),
errorText: '',
errorPinTheme: defaultPinTheme.copyWith(
decoration: BoxDecoration(
color: scale.errorScale.border,
borderRadius: BorderRadius.circular(8),
),
),
).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<TextEditingController>(
'pinController', pinController))
..add(DiagnosticsProperty<FocusNode>('focusNode', focusNode))
..add(DiagnosticsProperty<GlobalKey<FormState>>('formKey', formKey));
}
}

View file

@ -0,0 +1,162 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:quickalert/quickalert.dart';
import '../entities/local_account.dart';
import '../tools/tools.dart';
import 'contact_invitation_display.dart';
import 'enter_pin.dart';
class SendInviteDialog extends ConsumerStatefulWidget {
const SendInviteDialog({super.key});
@override
SendInviteDialogState createState() => SendInviteDialogState();
}
class SendInviteDialogState extends ConsumerState<SendInviteDialog> {
final messageTextController = TextEditingController(
text: translate('send_invite_dialog.connect_with_me'));
EncryptionKeyType _encryptionKeyType = EncryptionKeyType.none;
String _encryptionKey = '';
@override
void initState() {
super.initState();
}
Future<void> _onNoneEncryptionSelected(bool selected) async {
setState(() {
if (selected) {
_encryptionKeyType = EncryptionKeyType.none;
}
});
}
Future<void> _onPinEncryptionSelected(bool selected) async {
final description = translate('send_invite_dialog.pin_description');
final pin = await showDialog<String>(
context: context,
builder: (context) => EnterPinDialog(description: description));
if (pin == null) {
return;
}
// ignore: use_build_context_synchronously
if (!context.mounted) {
return;
}
final matchpin = await showDialog<String>(
context: context,
builder: (context) => EnterPinDialog(
matchPin: pin,
description: description,
));
if (matchpin == null) {
return;
} else if (pin == matchpin) {
setState(() {
_encryptionKeyType = EncryptionKeyType.pin;
_encryptionKey = pin;
});
} else {
// ignore: use_build_context_synchronously
if (!context.mounted) {
return;
}
showErrorToast(
context, translate('send_invite_dialog.pin_does_not_match'));
setState(() {
_encryptionKeyType = EncryptionKeyType.none;
_encryptionKey = '';
});
}
}
Future<void> _onPasswordEncryptionSelected(bool selected) async {
setState(() {
if (selected) {
_encryptionKeyType = EncryptionKeyType.password;
}
});
}
Future<void> _onGenerateButtonPressed() async {
await showDialog<void>(
context: context,
builder: (context) => ContactInvitationDisplayDialog());
// if (ret == null) {
// return;
// }
Navigator.of(context).pop();
}
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
final theme = Theme.of(context);
//final scale = theme.extension<ScaleScheme>()!;
final textTheme = theme.textTheme;
return SizedBox(
height: 400,
child: SingleChildScrollView(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
translate('send_invite_dialog.message_to_contact'),
).paddingAll(8),
TextField(
controller: messageTextController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: translate('send_invite_dialog.enter_message_hint'),
labelText: translate('send_invite_dialog.message')),
).paddingAll(8),
const SizedBox(height: 10),
Text(translate('send_invite_dialog.protect_this_invitation'),
style: textTheme.labelLarge)
.paddingAll(8),
Wrap(spacing: 5, children: [
ChoiceChip(
label: Text(translate('send_invite_dialog.unlocked')),
selected: _encryptionKeyType == EncryptionKeyType.none,
onSelected: _onNoneEncryptionSelected,
),
ChoiceChip(
label: Text(translate('send_invite_dialog.numeric_pin')),
selected: _encryptionKeyType == EncryptionKeyType.pin,
onSelected: _onPinEncryptionSelected,
),
ChoiceChip(
label: Text(translate('send_invite_dialog.password')),
selected: _encryptionKeyType == EncryptionKeyType.password,
onSelected: _onPasswordEncryptionSelected,
)
]).paddingAll(8),
Container(
width: double.infinity,
height: 60,
padding: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: _onGenerateButtonPressed,
child: Text(
translate('send_invite_dialog.generate'),
),
),
),
Text(translate('send_invite_dialog.note')).paddingAll(8),
Text(
translate('send_invite_dialog.note_text'),
style: Theme.of(context).textTheme.bodySmall,
).paddingAll(8),
],
),
),
);
}
}