layout fixes

This commit is contained in:
Christien Rioux 2024-07-08 21:29:52 -04:00
parent 71f4d37efa
commit 216aef8173
56 changed files with 654 additions and 342 deletions

View File

@ -59,7 +59,7 @@
"instructions_details": "This key is the ONLY way to recover your VeilidChat account in the event of a forgotton password or a lost, stolen, or compromised device.", "instructions_details": "This key is the ONLY way to recover your VeilidChat account in the event of a forgotton password or a lost, stolen, or compromised device.",
"instructions_options": "Here are some options for your recovery key:", "instructions_options": "Here are some options for your recovery key:",
"instructions_print": "Print the recovery key and keep it somewhere safe", "instructions_print": "Print the recovery key and keep it somewhere safe",
"instructions_view": "View the recovery key and write it down on paper", "instructions_view": "View the recovery key and take a screenshot",
"instructions_share": "Share the recovery key to another app to save it", "instructions_share": "Share the recovery key to another app to save it",
"print": "Print", "print": "Print",
"view": "View", "view": "View",

22
assets/js/pdf/3.2.146/pdf.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,8 @@
PODS: PODS:
- camera_avfoundation (0.0.1): - camera_avfoundation (0.0.1):
- Flutter - Flutter
- file_saver (0.0.1):
- Flutter
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_native_splash (0.0.1): - flutter_native_splash (0.0.1):
- Flutter - Flutter
@ -63,6 +65,8 @@ PODS:
- path_provider_foundation (0.0.1): - path_provider_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- printing (1.0.0):
- Flutter
- PromisesObjC (2.4.0) - PromisesObjC (2.4.0)
- share_plus (0.0.1): - share_plus (0.0.1):
- Flutter - Flutter
@ -83,12 +87,14 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
- file_saver (from `.symlinks/plugins/file_saver/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`) - mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- pasteboard (from `.symlinks/plugins/pasteboard/ios`) - pasteboard (from `.symlinks/plugins/pasteboard/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- printing (from `.symlinks/plugins/printing/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- smart_auth (from `.symlinks/plugins/smart_auth/ios`) - smart_auth (from `.symlinks/plugins/smart_auth/ios`)
@ -115,6 +121,8 @@ SPEC REPOS:
EXTERNAL SOURCES: EXTERNAL SOURCES:
camera_avfoundation: camera_avfoundation:
:path: ".symlinks/plugins/camera_avfoundation/ios" :path: ".symlinks/plugins/camera_avfoundation/ios"
file_saver:
:path: ".symlinks/plugins/file_saver/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
flutter_native_splash: flutter_native_splash:
@ -127,6 +135,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/pasteboard/ios" :path: ".symlinks/plugins/pasteboard/ios"
path_provider_foundation: path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin" :path: ".symlinks/plugins/path_provider_foundation/darwin"
printing:
:path: ".symlinks/plugins/printing/ios"
share_plus: share_plus:
:path: ".symlinks/plugins/share_plus/ios" :path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation: shared_preferences_foundation:
@ -144,6 +154,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
camera_avfoundation: 759172d1a77ae7be0de08fc104cfb79738b8a59e camera_avfoundation: 759172d1a77ae7be0de08fc104cfb79738b8a59e
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778 flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
@ -161,6 +172,7 @@ SPEC CHECKSUMS:
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0 pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
printing: 233e1b73bd1f4a05615548e9b5a324c98588640b
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78

View File

@ -132,7 +132,8 @@ class AccountRepository {
/// Creates a new super identity, an identity instance, an account associated /// Creates a new super identity, an identity instance, an account associated
/// with the identity instance, stores the account in the identity key and /// with the identity instance, stores the account in the identity key and
/// then logs into that account with no password set at this time /// then logs into that account with no password set at this time
Future<SecretKey> createWithNewSuperIdentity(proto.Profile newProfile) async { Future<WritableSuperIdentity> createWithNewSuperIdentity(
proto.Profile newProfile) async {
log.debug('Creating super identity'); log.debug('Creating super identity');
final wsi = await WritableSuperIdentity.create(); final wsi = await WritableSuperIdentity.create();
try { try {
@ -146,7 +147,7 @@ class AccountRepository {
localAccount.superIdentity.recordKey, EncryptionKeyType.none, ''); localAccount.superIdentity.recordKey, EncryptionKeyType.none, '');
assert(ok, 'login with none should never fail'); assert(ok, 'login with none should never fail');
return wsi.superSecret; return wsi;
} on Exception catch (_) { } on Exception catch (_) {
await wsi.delete(); await wsi.delete();
rethrow; rethrow;

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -55,6 +57,13 @@ class _EditAccountPageState extends State<EditAccountPage> {
}); });
} }
@override
void dispose() {
unawaited(
changeWindowSetup(TitleBarStyle.normal, OrientationCapability.normal));
super.dispose();
}
Widget _editAccountForm(BuildContext context, Widget _editAccountForm(BuildContext context,
{required Future<void> Function(GlobalKey<FormBuilderState>) {required Future<void> Function(GlobalKey<FormBuilderState>)
onSubmit}) => onSubmit}) =>
@ -282,30 +291,27 @@ class _EditAccountPageState extends State<EditAccountPage> {
await GoRouterHelper(context).push('/settings'); await GoRouterHelper(context).push('/settings');
}) })
]), ]),
body: Column(children: [ body: SingleChildScrollView(
child: Column(children: [
_editAccountForm( _editAccountForm(
context, context,
onSubmit: _onSubmit, onSubmit: _onSubmit,
).expanded(), ).paddingLTRB(0, 0, 0, 32),
Text(translate('edit_account_page.remove_account_description')), OptionBox(
ElevatedButton( instructions:
onPressed: _onRemoveAccount, translate('edit_account_page.remove_account_description'),
child: Row(mainAxisSize: MainAxisSize.min, children: [ buttonIcon: Icons.person_remove_alt_1,
const Icon(Icons.person_remove_alt_1, size: 16) buttonText: translate('edit_account_page.remove_account'),
.paddingLTRB(0, 0, 4, 0), onClick: _onRemoveAccount,
Text(translate('edit_account_page.remove_account')) ),
.paddingLTRB(0, 0, 4, 0) OptionBox(
])).paddingLTRB(0, 8, 0, 24), instructions:
Text(translate('edit_account_page.destroy_account_description')), translate('edit_account_page.destroy_account_description'),
ElevatedButton( buttonIcon: Icons.person_off,
onPressed: _onDestroyAccount, buttonText: translate('edit_account_page.destroy_account'),
child: Row(mainAxisSize: MainAxisSize.min, children: [ onClick: _onDestroyAccount,
const Icon(Icons.person_off, size: 16) )
.paddingLTRB(0, 0, 4, 0), ]).paddingSymmetric(horizontal: 24, vertical: 8)))
Text(translate('edit_account_page.destroy_account'))
.paddingLTRB(0, 0, 4, 0)
])).paddingLTRB(0, 8, 0, 24)
]).paddingSymmetric(horizontal: 24, vertical: 8))
.withModalHUD(context, displayModalHUD); .withModalHUD(context, displayModalHUD);
} }
} }

View File

@ -70,10 +70,10 @@ class _NewAccountPageState extends State<NewAccountPage> {
_isInAsyncCall = true; _isInAsyncCall = true;
}); });
try { try {
final superSecret = await AccountRepository.instance final writableSuperIdentity = await AccountRepository.instance
.createWithNewSuperIdentity(newProfile); .createWithNewSuperIdentity(newProfile);
GoRouterHelper(context) GoRouterHelper(context).pushReplacement('/new_account/recovery_key',
.pushReplacement('/new_account/recovery_key', extra: superSecret); extra: [writableSuperIdentity, newProfile.name]);
} finally { } finally {
if (mounted) { if (mounted) {
setState(() { setState(() {
@ -94,7 +94,6 @@ class _NewAccountPageState extends State<NewAccountPage> {
final displayModalHUD = _isInAsyncCall; final displayModalHUD = _isInAsyncCall;
return StyledScaffold( return StyledScaffold(
// resizeToAvoidBottomInset: false,
appBar: DefaultAppBar( appBar: DefaultAppBar(
title: Text(translate('new_account_page.titlebar')), title: Text(translate('new_account_page.titlebar')),
leading: Navigator.canPop(context) leading: Navigator.canPop(context)
@ -114,10 +113,11 @@ class _NewAccountPageState extends State<NewAccountPage> {
await GoRouterHelper(context).push('/settings'); await GoRouterHelper(context).push('/settings');
}) })
]), ]),
body: _newAccountForm( body: SingleChildScrollView(
child: _newAccountForm(
context, context,
onSubmit: _onSubmit, onSubmit: _onSubmit,
).paddingSymmetric(horizontal: 24, vertical: 8), )).paddingSymmetric(horizontal: 24, vertical: 8),
).withModalHUD(context, displayModalHUD); ).withModalHUD(context, displayModalHUD);
} }
} }

View File

@ -58,7 +58,7 @@ class _EditProfileFormState extends State<EditProfileForm> {
) => ) =>
FormBuilder( FormBuilder(
key: _formKey, key: _formKey,
child: ListView( child: Column(
children: [ children: [
Text(widget.header) Text(widget.header)
.textStyle(context.headlineSmall) .textStyle(context.headlineSmall)

View File

@ -7,12 +7,15 @@ import '../../theme/theme.dart';
class ProfileWidget extends StatelessWidget { class ProfileWidget extends StatelessWidget {
const ProfileWidget({ const ProfileWidget({
required proto.Profile profile, required proto.Profile profile,
required bool showPronouns,
super.key, super.key,
}) : _profile = profile; }) : _profile = profile,
_showPronouns = showPronouns;
// //
final proto.Profile _profile; final proto.Profile _profile;
final bool _showPronouns;
// //
@ -42,22 +45,25 @@ class ProfileWidget extends StatelessWidget {
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(16 * scaleConfig.borderRadiusScale))), Radius.circular(16 * scaleConfig.borderRadiusScale))),
), ),
child: Column(children: [ child: Row(children: [
const Spacer(),
Text( Text(
_profile.name, _profile.name,
style: textTheme.headlineSmall!.copyWith( style: textTheme.titleMedium!.copyWith(
color: scaleConfig.preferBorders color: scaleConfig.preferBorders
? scale.primaryScale.border ? scale.primaryScale.border
: scale.primaryScale.borderText), : scale.primaryScale.borderText),
textAlign: TextAlign.left, textAlign: TextAlign.left,
).paddingAll(4), ).paddingAll(12),
if (_profile.pronouns.isNotEmpty) if (_profile.pronouns.isNotEmpty && _showPronouns)
Text(_profile.pronouns, Text('(${_profile.pronouns})',
style: textTheme.bodyMedium!.copyWith( textAlign: TextAlign.right,
style: textTheme.bodySmall!.copyWith(
color: scaleConfig.preferBorders color: scaleConfig.preferBorders
? scale.primaryScale.border ? scale.primaryScale.border
: scale.primaryScale.borderText)) : scale.primaryScale.primary))
.paddingLTRB(4, 0, 4, 4), .paddingAll(12),
const Spacer()
]), ]),
); );
} }

View File

@ -1,10 +1,18 @@
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data';
import 'package:async_tools/async_tools.dart'; import 'package:async_tools/async_tools.dart';
import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:file_saver/file_saver.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart'; import 'package:flutter_translate/flutter_translate.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:screenshot/screenshot.dart';
import 'package:share_plus/share_plus.dart';
import 'package:veilid_support/veilid_support.dart'; import 'package:veilid_support/veilid_support.dart';
import '../../layout/default_app_bar.dart'; import '../../layout/default_app_bar.dart';
@ -13,13 +21,18 @@ import '../../tools/tools.dart';
import '../../veilid_processor/veilid_processor.dart'; import '../../veilid_processor/veilid_processor.dart';
class ShowRecoveryKeyPage extends StatefulWidget { class ShowRecoveryKeyPage extends StatefulWidget {
const ShowRecoveryKeyPage({required SecretKey secretKey, super.key}) const ShowRecoveryKeyPage(
: _secretKey = secretKey; {required WritableSuperIdentity writableSuperIdentity,
required String name,
super.key})
: _writableSuperIdentity = writableSuperIdentity,
_name = name;
@override @override
ShowRecoveryKeyPageState createState() => ShowRecoveryKeyPageState(); ShowRecoveryKeyPageState createState() => ShowRecoveryKeyPageState();
final SecretKey _secretKey; final WritableSuperIdentity _writableSuperIdentity;
final String _name;
} }
class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> { class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
@ -33,18 +46,99 @@ class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
}); });
} }
Widget _recoveryKeyWidget(SecretKey _secretKey) { Future<void> _shareRecoveryKey(
BuildContext context, Uint8List recoveryKey, String name) async {
setState(() {
_isInAsyncCall = true;
});
final screenshotController = ScreenshotController();
final bytes = await screenshotController.captureFromWidget(
Container(
color: Colors.white,
width: 400,
height: 400,
child: _recoveryKeyWidget(context, recoveryKey, name)),
);
setState(() {
_isInAsyncCall = false;
});
if (Platform.isLinux) {
// Share plus doesn't do Linux yet
await FileSaver.instance.saveFile(name: 'recovery_key.png', bytes: bytes);
} else {
final xfile = XFile.fromData(
bytes,
mimeType: 'image/png',
name: 'recovery_key.png',
);
await Share.shareXFiles([xfile]);
}
}
static Future<void> _printRecoveryKey(
BuildContext context, Uint8List recoveryKey, String name) async {
final wrapped = await WidgetWrapper.fromWidget(
context: context,
widget: SizedBox(
width: 400,
height: 400,
child: _recoveryKeyWidget(context, recoveryKey, name)),
constraints: const BoxConstraints(maxWidth: 400, maxHeight: 400),
pixelRatio: 3);
final doc = pw.Document()
..addPage(pw.Page(
build: (context) =>
pw.Center(child: pw.Image(wrapped, width: 400)) // Center
)); // Page
await Printing.layoutPdf(onLayout: (format) async => doc.save());
}
static Widget _recoveryKeyWidget(
BuildContext context, Uint8List recoveryKey, String name) {
final theme = Theme.of(context); final theme = Theme.of(context);
final textTheme = theme.textTheme; final textTheme = theme.textTheme;
//final scaleConfig = theme.extension<ScaleConfig>()!;
return Column(mainAxisSize: MainAxisSize.min, children: [
Text(
style: textTheme.headlineSmall!.copyWith(
color: Colors.black,
fontWeight: FontWeight.bold,
),
translate('show_recovery_key_page.recovery_key'))
.paddingLTRB(16, 16, 16, 0),
FittedBox(
child: QrImageView.withQr(
size: 300,
qr: QrCode.fromUint8List(
data: recoveryKey,
errorCorrectLevel: QrErrorCorrectLevel.L)))
.paddingLTRB(16, 16, 16, 8)
.expanded(),
Text(
style: textTheme.labelMedium!.copyWith(
color: Colors.black,
fontWeight: FontWeight.bold,
),
name)
.paddingLTRB(16, 8, 16, 24),
]);
}
static Widget _recoveryKeyDialog(
BuildContext context, Uint8List recoveryKey, String name) {
final theme = Theme.of(context);
//final textTheme = theme.textTheme;
final scaleConfig = theme.extension<ScaleConfig>()!; final scaleConfig = theme.extension<ScaleConfig>()!;
final cardsize = final cardsize =
min<double>(MediaQuery.of(context).size.shortestSide - 48.0, 400); min<double>(MediaQuery.of(context).size.shortestSide - 48.0, 400);
final phonoString = prettyPhonoString(
encodePhono(_secretKey.decode()),
wordsPerLine: 2,
);
return Dialog( return Dialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
side: const BorderSide(width: 2), side: const BorderSide(width: 2),
@ -55,65 +149,19 @@ class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
constraints: BoxConstraints( constraints: BoxConstraints(
minWidth: cardsize, minWidth: cardsize,
maxWidth: cardsize, maxWidth: cardsize,
minHeight: cardsize, minHeight: cardsize + 16,
maxHeight: cardsize), maxHeight: cardsize + 16),
child: Column(mainAxisSize: MainAxisSize.min, children: [ child: _recoveryKeyWidget(context, recoveryKey, name)));
Text(
style: textTheme.headlineSmall!.copyWith(
color: Colors.black,
fontWeight: FontWeight.bold,
),
translate('show_recovery_key_page.recovery_key'))
.paddingAll(32),
Text(
style: textTheme.headlineSmall!.copyWith(
color: Colors.black, fontFamily: 'Source Code Pro'),
phonoString)
])));
}
Widget _optionBox(
{required String instructions,
required Icon buttonIcon,
required String buttonText,
required void Function() onClick}) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
return Container(
constraints: const BoxConstraints(maxWidth: 400),
decoration: BoxDecoration(
color: scale.primaryScale.subtleBackground,
borderRadius:
BorderRadius.circular(8 * scaleConfig.borderRadiusScale),
border: Border.all(color: scale.primaryScale.border)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
style: theme.textTheme.labelMedium!
.copyWith(color: scale.primaryScale.appText),
softWrap: true,
textAlign: TextAlign.center,
instructions),
ElevatedButton(
onPressed: onClick,
child: Row(mainAxisSize: MainAxisSize.min, children: [
buttonIcon.paddingLTRB(0, 8, 12, 8),
Text(textAlign: TextAlign.center, buttonText)
])).paddingLTRB(0, 12, 0, 0).toCenter()
]).paddingAll(12))
.paddingLTRB(24, 0, 24, 12);
} }
@override @override
// ignore: prefer_expression_function_bodies // ignore: prefer_expression_function_bodies
Widget build(BuildContext context) { Widget build(BuildContext context) {
final secretKey = widget._secretKey;
final theme = Theme.of(context); final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!; // final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!; // final scaleConfig = theme.extension<ScaleConfig>()!;
final displayModalHUD = _isInAsyncCall;
return StyledScaffold( return StyledScaffold(
// resizeToAvoidBottomInset: false, // resizeToAvoidBottomInset: false,
@ -146,42 +194,55 @@ class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
Text( Text(
textAlign: TextAlign.center, textAlign: TextAlign.center,
translate('show_recovery_key_page.instructions_options')) translate('show_recovery_key_page.instructions_options'))
.paddingLTRB(12, 0, 12, 12), .paddingLTRB(12, 0, 12, 24),
_optionBox( OptionBox(
instructions: instructions:
translate('show_recovery_key_page.instructions_print'), translate('show_recovery_key_page.instructions_print'),
buttonIcon: const Icon(Icons.print), buttonIcon: Icons.print,
buttonText: translate('show_recovery_key_page.print'), buttonText: translate('show_recovery_key_page.print'),
onClick: () {
//
setState(() {
_codeHandled = true;
});
}),
_optionBox(
instructions:
translate('show_recovery_key_page.instructions_view'),
buttonIcon: const Icon(Icons.edit_document),
buttonText: translate('show_recovery_key_page.view'),
onClick: () { onClick: () {
// //
singleFuture(this, () async { singleFuture(this, () async {
await showDialog<void>( await _printRecoveryKey(context,
context: context, widget._writableSuperIdentity.recoveryKey, widget._name);
builder: (context) => _recoveryKeyWidget(secretKey));
}); });
setState(() { setState(() {
_codeHandled = true; _codeHandled = true;
}); });
}), }),
_optionBox( OptionBox(
instructions:
translate('show_recovery_key_page.instructions_view'),
buttonIcon: Icons.edit_document,
buttonText: translate('show_recovery_key_page.view'),
onClick: () {
//
singleFuture(this, () async {
await showDialog<void>(
context: context,
builder: (context) => _recoveryKeyDialog(
context,
widget._writableSuperIdentity.recoveryKey,
widget._name));
});
setState(() {
_codeHandled = true;
});
}),
OptionBox(
instructions: instructions:
translate('show_recovery_key_page.instructions_share'), translate('show_recovery_key_page.instructions_share'),
buttonIcon: const Icon(Icons.ios_share), buttonIcon: Icons.ios_share,
buttonText: translate('show_recovery_key_page.share'), buttonText: translate('show_recovery_key_page.share'),
onClick: () { onClick: () {
// //
singleFuture(this, () async {
await _shareRecoveryKey(context,
widget._writableSuperIdentity.recoveryKey, widget._name);
});
setState(() { setState(() {
_codeHandled = true; _codeHandled = true;
}); });
@ -198,8 +259,9 @@ class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
}, },
child: Text(translate('button.finish')).paddingAll(8)) child: Text(translate('button.finish')).paddingAll(8))
.paddingAll(12)) .paddingAll(12))
]))); ]))).withModalHUD(context, displayModalHUD);
} }
bool _codeHandled = false; bool _codeHandled = false;
bool _isInAsyncCall = false;
} }

View File

@ -149,8 +149,8 @@ class VeilidChatApp extends StatelessWidget {
scale.grayScale.subtleBackground, scale.grayScale.subtleBackground,
] ]
: [ : [
scale.tertiaryScale.hoverElementBackground, scale.primaryScale.hoverElementBackground,
scale.tertiaryScale.subtleBackground, scale.primaryScale.subtleBackground,
]); ]);
return DecoratedBox( return DecoratedBox(

View File

@ -14,14 +14,13 @@ class NoConversationWidget extends StatelessWidget {
final theme = Theme.of(context); final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!; final scale = theme.extension<ScaleScheme>()!;
return Container( return DecoratedBox(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor, color: scale.primaryScale.appBackground,
), ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Icon( Icon(
Icons.diversity_3, Icons.diversity_3,
@ -29,6 +28,7 @@ class NoConversationWidget extends StatelessWidget {
size: 48, size: 48,
), ),
Text( Text(
textAlign: TextAlign.center,
translate('chat.start_a_conversation'), translate('chat.start_a_conversation'),
style: Theme.of(context).textTheme.bodyMedium?.copyWith( style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: scale.primaryScale.subtleBorder, color: scale.primaryScale.subtleBorder,

View File

@ -11,7 +11,6 @@ import 'package:qr_flutter/qr_flutter.dart';
import 'package:veilid_support/veilid_support.dart'; import 'package:veilid_support/veilid_support.dart';
import '../../theme/theme.dart'; import '../../theme/theme.dart';
import '../../tools/tools.dart';
import '../contact_invitation.dart'; import '../contact_invitation.dart';
class ContactInvitationDisplayDialog extends StatelessWidget { class ContactInvitationDisplayDialog extends StatelessWidget {

View File

@ -49,7 +49,7 @@ class ContactInvitationListWidgetState
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16 * scaleConfig.borderRadiusScale), borderRadius: BorderRadius.circular(16 * scaleConfig.borderRadiusScale),
)), )),
constraints: const BoxConstraints(maxHeight: 200), constraints: const BoxConstraints(maxHeight: 100),
child: Container( child: Container(
width: double.infinity, width: double.infinity,
decoration: ShapeDecoration( decoration: ShapeDecoration(
@ -59,6 +59,7 @@ class ContactInvitationListWidgetState
BorderRadius.circular(16 * scaleConfig.borderRadiusScale), BorderRadius.circular(16 * scaleConfig.borderRadiusScale),
)), )),
child: ListView.builder( child: ListView.builder(
shrinkWrap: true,
controller: _scrollController, controller: _scrollController,
itemCount: widget.contactInvitationRecordList.length, itemCount: widget.contactInvitationRecordList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {

View File

@ -11,7 +11,6 @@ import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart'; import '../../account_manager/account_manager.dart';
import '../../theme/theme.dart'; import '../../theme/theme.dart';
import '../../tools/tools.dart';
import '../contact_invitation.dart'; import '../contact_invitation.dart';
class CreateInvitationDialog extends StatefulWidget { class CreateInvitationDialog extends StatefulWidget {

View File

@ -270,11 +270,12 @@ class InvitationDialogState extends State<InvitationDialog> {
if (_validInvitation != null && !_isValidating) if (_validInvitation != null && !_isValidating)
Column(children: [ Column(children: [
Container( Container(
constraints: const BoxConstraints(maxHeight: 64), constraints: const BoxConstraints(maxHeight: 64),
width: double.infinity, width: double.infinity,
child: child: ProfileWidget(
ProfileWidget(profile: _validInvitation!.remoteProfile)) profile: _validInvitation!.remoteProfile,
.paddingLTRB(0, 0, 0, 16), showPronouns: true,
)).paddingLTRB(0, 0, 0, 16),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [

View File

@ -8,7 +8,6 @@ import 'package:provider/provider.dart';
import 'package:veilid_support/veilid_support.dart'; import 'package:veilid_support/veilid_support.dart';
import '../../theme/theme.dart'; import '../../theme/theme.dart';
import '../../tools/tools.dart';
import 'invitation_dialog.dart'; import 'invitation_dialog.dart';
class PasteInvitationDialog extends StatefulWidget { class PasteInvitationDialog extends StatefulWidget {

View File

@ -13,7 +13,6 @@ import 'package:provider/provider.dart';
import 'package:zxing2/qrcode.dart'; import 'package:zxing2/qrcode.dart';
import '../../theme/theme.dart'; import '../../theme/theme.dart';
import '../../tools/tools.dart';
import 'invitation_dialog.dart'; import 'invitation_dialog.dart';
// class BarcodeOverlay extends CustomPainter { // class BarcodeOverlay extends CustomPainter {

View File

@ -10,12 +10,15 @@ import '../../theme/theme.dart';
import 'contact_item_widget.dart'; import 'contact_item_widget.dart';
import 'empty_contact_list_widget.dart'; import 'empty_contact_list_widget.dart';
class ContactListWidget extends StatelessWidget { class ContactListWidget extends StatefulWidget {
const ContactListWidget( const ContactListWidget(
{required this.contactList, required this.disabled, super.key}); {required this.contactList, required this.disabled, super.key});
final IList<proto.Contact> contactList; final IList<proto.Contact> contactList;
final bool disabled; final bool disabled;
@override
State<ContactListWidget> createState() => _ContactListWidgetState();
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties); super.debugFillProperties(properties);
@ -23,45 +26,51 @@ class ContactListWidget extends StatelessWidget {
..add(IterableProperty<proto.Contact>('contactList', contactList)) ..add(IterableProperty<proto.Contact>('contactList', contactList))
..add(DiagnosticsProperty<bool>('disabled', disabled)); ..add(DiagnosticsProperty<bool>('disabled', disabled));
} }
}
class _ContactListWidgetState extends State<ContactListWidget> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!; final scale = theme.extension<ScaleScheme>()!;
return SizedBox.expand( return styledTitleContainer(
child: styledTitleContainer( context: context,
context: context, title: translate('contact_list.title'),
title: translate('contact_list.title'), child: SearchableList<proto.Contact>(
child: SizedBox.expand( shrinkWrap: true,
child: (contactList.isEmpty) initialList: widget.contactList.toList(),
? const EmptyContactListWidget() itemBuilder: (c) =>
: SearchableList<proto.Contact>( ContactItemWidget(contact: c, disabled: widget.disabled)
initialList: contactList.toList(), .paddingLTRB(0, 4, 0, 0),
itemBuilder: (c) => filter: (value) {
ContactItemWidget(contact: c, disabled: disabled) final lowerValue = value.toLowerCase();
.paddingLTRB(0, 4, 0, 0), return widget.contactList
filter: (value) { .where((element) =>
final lowerValue = value.toLowerCase(); element.nickname.toLowerCase().contains(lowerValue) ||
return contactList element.profile.name.toLowerCase().contains(lowerValue) ||
.where((element) => element.profile.pronouns.toLowerCase().contains(lowerValue))
element.nickname .toList();
.toLowerCase() },
.contains(lowerValue) || searchFieldHeight: 40,
element.profile.name spaceBetweenSearchAndList: 4,
.toLowerCase() emptyWidget: const EmptyContactListWidget(),
.contains(lowerValue) || defaultSuffixIconColor: scale.primaryScale.border,
element.profile.pronouns closeKeyboardWhenScrolling: true,
.toLowerCase() inputDecoration: InputDecoration(
.contains(lowerValue)) labelText: translate('contact_list.search'),
.toList(); ),
}, ).paddingAll(8),
spaceBetweenSearchAndList: 4, ).paddingLTRB(8, 0, 8, 8);
defaultSuffixIconColor: scale.primaryScale.border,
inputDecoration: InputDecoration(
labelText: translate('contact_list.search'),
),
).paddingAll(8),
))).paddingLTRB(8, 0, 8, 8);
} }
} }

View File

@ -17,6 +17,7 @@ class EmptyContactListWidget extends StatelessWidget {
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon( Icon(
@ -25,6 +26,7 @@ class EmptyContactListWidget extends StatelessWidget {
size: 48, size: 48,
), ),
Text( Text(
textAlign: TextAlign.center,
translate('contact_list.invite_people'), translate('contact_list.invite_people'),
style: textTheme.bodyMedium?.copyWith( style: textTheme.bodyMedium?.copyWith(
color: scale.primaryScale.subtleBorder, color: scale.primaryScale.subtleBorder,

View File

@ -1,7 +1,6 @@
import 'package:async_tools/async_tools.dart'; import 'package:async_tools/async_tools.dart';
import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
@ -20,7 +19,7 @@ class DrawerMenu extends StatefulWidget {
const DrawerMenu({super.key}); const DrawerMenu({super.key});
@override @override
State createState() => _DrawerMenuState(); State<DrawerMenu> createState() => _DrawerMenuState();
} }
class _DrawerMenuState extends State<DrawerMenu> { class _DrawerMenuState extends State<DrawerMenu> {
@ -105,10 +104,12 @@ class _DrawerMenuState extends State<DrawerMenu> {
width: 34, width: 34,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: scaleConfig.preferBorders
color: border, ? Border.all(
width: 2, color: border,
strokeAlign: BorderSide.strokeAlignOutside), width: 2,
strokeAlign: BorderSide.strokeAlignOutside)
: null,
color: Colors.blue, color: Colors.blue,
), ),
child: AvatarImage( child: AvatarImage(
@ -130,9 +131,18 @@ class _DrawerMenuState extends State<DrawerMenu> {
backgroundColor: background, backgroundColor: background,
backgroundHoverColor: hoverBackground, backgroundHoverColor: hoverBackground,
backgroundFocusColor: activeBackground, backgroundFocusColor: activeBackground,
borderColor: border, borderColor:
borderHoverColor: hoverBorder, (scaleConfig.preferBorders || scaleConfig.useVisualIndicators)
borderFocusColor: activeBorder, ? border
: null,
borderHoverColor:
(scaleConfig.preferBorders || scaleConfig.useVisualIndicators)
? hoverBorder
: null,
borderFocusColor:
(scaleConfig.preferBorders || scaleConfig.useVisualIndicators)
? activeBorder
: null,
borderRadius: 16 * scaleConfig.borderRadiusScale, borderRadius: 16 * scaleConfig.borderRadiusScale,
callback: callback, callback: callback,
footerButtonIcon: loggedIn ? Icons.edit_outlined : null, footerButtonIcon: loggedIn ? Icons.edit_outlined : null,
@ -143,7 +153,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
)); ));
} }
Widget _getAccountList( List<Widget> _getAccountList(
{required IList<LocalAccount> localAccounts, {required IList<LocalAccount> localAccounts,
required TypedKey? activeLocalAccount, required TypedKey? activeLocalAccount,
required PerAccountCollectionBlocMapState required PerAccountCollectionBlocMapState
@ -164,7 +174,9 @@ class _DrawerMenuState extends State<DrawerMenu> {
final avAccountRecordState = perAccountState?.avAccountRecordState; final avAccountRecordState = perAccountState?.avAccountRecordState;
if (perAccountState != null && avAccountRecordState != null) { if (perAccountState != null && avAccountRecordState != null) {
// Account is logged in // Account is logged in
final scale = theme.extension<ScaleScheme>()!.primaryScale; final scale = scaleConfig.useVisualIndicators
? theme.extension<ScaleScheme>()!.primaryScale
: theme.extension<ScaleScheme>()!.tertiaryScale;
final loggedInAccount = avAccountRecordState.when( final loggedInAccount = avAccountRecordState.when(
data: (value) => _makeAccountWidget( data: (value) => _makeAccountWidget(
name: value.profile.name, name: value.profile.name,
@ -209,14 +221,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
} }
// Assemble main menu // Assemble main menu
final mainMenu = <Widget>[...loggedInAccounts, ...loggedOutAccounts]; return <Widget>[...loggedInAccounts, ...loggedOutAccounts];
// Return main menu widgets
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[...mainMenu],
);
} }
Widget _getButton( Widget _getButton(
@ -262,18 +267,18 @@ class _DrawerMenuState extends State<DrawerMenu> {
}), shape: WidgetStateProperty.resolveWith((states) { }), shape: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.hovered)) { if (states.contains(WidgetState.hovered)) {
return RoundedRectangleBorder( return RoundedRectangleBorder(
side: BorderSide(color: hoverBorder), side: BorderSide(color: hoverBorder, width: 2),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(16 * scaleConfig.borderRadiusScale))); Radius.circular(16 * scaleConfig.borderRadiusScale)));
} }
if (states.contains(WidgetState.focused)) { if (states.contains(WidgetState.focused)) {
return RoundedRectangleBorder( return RoundedRectangleBorder(
side: BorderSide(color: activeBorder), side: BorderSide(color: activeBorder, width: 2),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(16 * scaleConfig.borderRadiusScale))); Radius.circular(16 * scaleConfig.borderRadiusScale)));
} }
return RoundedRectangleBorder( return RoundedRectangleBorder(
side: BorderSide(color: border), side: BorderSide(color: border, width: 2),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(16 * scaleConfig.borderRadiusScale))); Radius.circular(16 * scaleConfig.borderRadiusScale)));
})), })),
@ -306,7 +311,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [settingsButton, addButton]).paddingLTRB(0, 16, 0, 0); children: [settingsButton, addButton]).paddingLTRB(0, 16, 0, 16);
} }
@override @override
@ -323,8 +328,8 @@ class _DrawerMenuState extends State<DrawerMenu> {
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: [
scale.tertiaryScale.border,
scale.tertiaryScale.subtleBorder, scale.tertiaryScale.subtleBorder,
scale.tertiaryScale.subtleBackground,
]); ]);
return DecoratedBox( return DecoratedBox(
@ -391,35 +396,21 @@ class _DrawerMenuState extends State<DrawerMenu> {
? grayColorFilter ? grayColorFilter
: null), : null),
]))), ]))),
const Spacer(), Text(translate('menu.accounts'),
DecoratedBox( style: theme.textTheme.titleMedium!.copyWith(
decoration: ShapeDecoration( color: scaleConfig.preferBorders
shape: RoundedRectangleBorder( ? scale.tertiaryScale.border
side: !scaleConfig.useVisualIndicators : scale.tertiaryScale.borderText))
? BorderSide.none .paddingLTRB(0, 16, 0, 16),
: scaleConfig.preferBorders ListView(
? BorderSide(color: scale.tertiaryScale.border) shrinkWrap: true,
: BorderSide(color: scale.tertiaryScale.primary), children: _getAccountList(
borderRadius: BorderRadius.circular( localAccounts: localAccounts,
16 * scaleConfig.borderRadiusScale)), activeLocalAccount: activeLocalAccount,
color: scaleConfig.preferBorders perAccountCollectionBlocMapState:
? Colors.transparent perAccountCollectionBlocMapState))
: scale.tertiaryScale.border.withAlpha(0x5F)), .expanded(),
child: Column(children: [
Text(translate('menu.accounts'),
style: theme.textTheme.titleMedium!.copyWith(
color: scaleConfig.preferBorders
? scale.tertiaryScale.border
: scale.tertiaryScale.primary))
.paddingLTRB(0, 0, 0, 16),
_getAccountList(
localAccounts: localAccounts,
activeLocalAccount: activeLocalAccount,
perAccountCollectionBlocMapState:
perAccountCollectionBlocMapState)
]).paddingAll(16)),
_getBottomButtons(), _getBottomButtons(),
const Spacer(),
Row(children: [ Row(children: [
Text('${translate('menu.version')} $packageInfoVersion', Text('${translate('menu.version')} $packageInfoVersion',
style: theme.textTheme.labelMedium! style: theme.textTheme.labelMedium!

View File

@ -72,7 +72,7 @@ class MenuItemWidget extends StatelessWidget {
).paddingAll(8)), ).paddingAll(8)),
), ),
if (footerButtonIcon != null) if (footerButtonIcon != null)
IconButton.outlined( IconButton(
color: footerButtonIconColor, color: footerButtonIconColor,
focusColor: footerButtonIconFocusColor, focusColor: footerButtonIconFocusColor,
hoverColor: footerButtonIconHoverColor, hoverColor: footerButtonIconHoverColor,

View File

@ -34,6 +34,7 @@ class HomeAccountReadyChatState extends State<HomeAccountReadyChat> {
@override @override
Widget build(BuildContext context) => SafeArea( Widget build(BuildContext context) => SafeArea(
bottom: false,
child: buildChatComponent(context), child: buildChatComponent(context),
); );
} }

View File

@ -1,5 +1,4 @@
import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_translate/flutter_translate.dart'; import 'package:flutter_translate/flutter_translate.dart';
@ -9,7 +8,6 @@ import '../../../account_manager/account_manager.dart';
import '../../../chat/chat.dart'; import '../../../chat/chat.dart';
import '../../../proto/proto.dart' as proto; import '../../../proto/proto.dart' as proto;
import '../../../theme/theme.dart'; import '../../../theme/theme.dart';
import '../../../tools/tools.dart';
import 'main_pager/main_pager.dart'; import 'main_pager/main_pager.dart';
class HomeAccountReadyMain extends StatefulWidget { class HomeAccountReadyMain extends StatefulWidget {
@ -44,7 +42,7 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
? scale.primaryScale.border ? scale.primaryScale.border
: scale.primaryScale.borderText, : scale.primaryScale.borderText,
constraints: constraints:
const BoxConstraints.expand(height: 64, width: 64), const BoxConstraints.expand(height: 48, width: 48),
style: ButtonStyle( style: ButtonStyle(
backgroundColor: WidgetStateProperty.all( backgroundColor: WidgetStateProperty.all(
scaleConfig.preferBorders scaleConfig.preferBorders
@ -61,7 +59,7 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
: scale.primaryScale.borderText, : scale.primaryScale.borderText,
width: 2), width: 2),
borderRadius: BorderRadius.all(Radius.circular( borderRadius: BorderRadius.all(Radius.circular(
16 * scaleConfig.borderRadiusScale))), 12 * scaleConfig.borderRadiusScale))),
)), )),
tooltip: translate('menu.settings_tooltip'), tooltip: translate('menu.settings_tooltip'),
onPressed: () async { onPressed: () async {
@ -69,7 +67,10 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
await ctrl.toggle?.call(); await ctrl.toggle?.call();
//await GoRouterHelper(context).push('/settings'); //await GoRouterHelper(context).push('/settings');
}).paddingLTRB(0, 0, 8, 0), }).paddingLTRB(0, 0, 8, 0),
ProfileWidget(profile: profile).expanded(), ProfileWidget(
profile: profile,
showPronouns: false,
).expanded(),
]).paddingAll(8), ]).paddingAll(8),
MainPager(key: _mainPagerKey).expanded() MainPager(key: _mainPagerKey).expanded()
])); ]));

View File

@ -55,6 +55,8 @@ class AccountPageState extends State<AccountPage> {
tilePadding: const EdgeInsets.fromLTRB(8, 0, 8, 0), tilePadding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
backgroundColor: scale.primaryScale.border, backgroundColor: scale.primaryScale.border,
collapsedBackgroundColor: scale.primaryScale.border, collapsedBackgroundColor: scale.primaryScale.border,
dense: true,
minTileHeight: 16,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: borderRadius:
BorderRadius.circular(16 * scaleConfig.borderRadiusScale), BorderRadius.circular(16 * scaleConfig.borderRadiusScale),
@ -66,10 +68,11 @@ class AccountPageState extends State<AccountPage> {
title: Text( title: Text(
translate('account_page.contact_invitations'), translate('account_page.contact_invitations'),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: textTheme.titleMedium! style: textTheme.titleSmall!
.copyWith(color: scale.primaryScale.borderText), .copyWith(color: scale.primaryScale.borderText),
), ),
iconColor: scale.primaryScale.borderText, iconColor: scale.primaryScale.borderText,
collapsedIconColor: scale.primaryScale.borderText,
initiallyExpanded: true, initiallyExpanded: true,
children: [ children: [
ContactInvitationListWidget( ContactInvitationListWidget(

View File

@ -46,6 +46,7 @@ class BottomSheetActionButtonState extends State<BottomSheetActionButton> {
return _showFab return _showFab
? FloatingActionButton( ? FloatingActionButton(
elevation: 0, elevation: 0,
heroTag: this,
hoverElevation: 0, hoverElevation: 0,
shape: widget.shape, shape: widget.shape,
foregroundColor: widget.foregroundColor, foregroundColor: widget.foregroundColor,

View File

@ -113,7 +113,7 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
padding: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.symmetric(horizontal: 4),
child: Text( child: Text(
_bottomLabelList[index], _bottomLabelList[index],
style: theme.textTheme.labelLarge!.copyWith( style: theme.textTheme.labelMedium!.copyWith(
fontWeight: isActive ? FontWeight.bold : FontWeight.normal, fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
color: color), color: color),
), ),

View File

@ -74,7 +74,10 @@ class HomeScreenState extends State<HomeScreen>
tablet: false, tablet: false,
tabletLandscape: false, tabletLandscape: false,
desktop: false)) { desktop: false)) {
final activeChatCubit = context.watch<ActiveChatCubit>();
return BlocConsumer<ActiveChatCubit, TypedKey?>( return BlocConsumer<ActiveChatCubit, TypedKey?>(
bloc: activeChatCubit,
listener: (context, activeChat) { listener: (context, activeChat) {
final hasActiveChat = activeChat != null; final hasActiveChat = activeChat != null;
if (hasActiveChat) { if (hasActiveChat) {
@ -179,6 +182,7 @@ class HomeScreenState extends State<HomeScreen>
final canClose = activeIndex != -1; final canClose = activeIndex != -1;
return SafeArea( return SafeArea(
bottom: false,
child: DefaultTextStyle( child: DefaultTextStyle(
style: theme.textTheme.bodySmall!, style: theme.textTheme.bodySmall!,
child: ZoomDrawer( child: ZoomDrawer(

View File

@ -78,10 +78,14 @@ class RouterCubit extends Cubit<RouterState> {
builder: (context, state) => const NewAccountPage(), builder: (context, state) => const NewAccountPage(),
), ),
GoRoute( GoRoute(
path: '/new_account/recovery_key', path: '/new_account/recovery_key',
builder: (context, state) => builder: (context, state) {
ShowRecoveryKeyPage(secretKey: state.extra! as SecretKey), final extra = state.extra! as List<Object?>;
),
return ShowRecoveryKeyPage(
writableSuperIdentity: extra[0]! as WritableSuperIdentity,
name: extra[1]! as String);
}),
GoRoute( GoRoute(
path: '/settings', path: '/settings',
builder: (context, state) => const SettingsPage(), builder: (context, state) => const SettingsPage(),

View File

@ -31,6 +31,7 @@ ChatTheme makeChatTheme(
), ),
inputBackgroundColor: Colors.blue, inputBackgroundColor: Colors.blue,
inputBorderRadius: BorderRadius.zero, inputBorderRadius: BorderRadius.zero,
inputTextStyle: textTheme.bodyLarge!,
inputTextDecoration: InputDecoration( inputTextDecoration: InputDecoration(
filled: !scaleConfig.preferBorders, filled: !scaleConfig.preferBorders,
fillColor: scale.primaryScale.subtleBackground, fillColor: scale.primaryScale.subtleBackground,
@ -77,13 +78,10 @@ ChatTheme makeChatTheme(
color: Colors.white, color: Colors.white,
fontSize: 64, fontSize: 64,
), ),
receivedMessageBodyTextStyle: TextStyle( receivedMessageBodyTextStyle: textTheme.bodyLarge!.copyWith(
color: scaleConfig.preferBorders color: scaleConfig.preferBorders
? scale.secondaryScale.calloutBackground ? scale.secondaryScale.calloutBackground
: scale.secondaryScale.calloutText, : scale.secondaryScale.calloutText,
fontSize: 16,
fontWeight: FontWeight.w500,
height: 1.5,
), ),
receivedEmojiMessageTextStyle: const TextStyle( receivedEmojiMessageTextStyle: const TextStyle(
color: Colors.white, color: Colors.white,

View File

@ -106,7 +106,7 @@ class SliderTile extends StatelessWidget {
? tileColor.border ? tileColor.border
: tileColor.borderText) : tileColor.borderText)
: scale.scale(a.actionScale).primaryText, : scale.scale(a.actionScale).primaryText,
icon: a.icon, icon: subtitle.isNotEmpty ? a.icon : null,
label: a.label, label: a.label,
padding: const EdgeInsets.all(2)), padding: const EdgeInsets.all(2)),
) )
@ -129,7 +129,7 @@ class SliderTile extends StatelessWidget {
? tileColor.border ? tileColor.border
: tileColor.borderText) : tileColor.borderText)
: scale.scale(a.actionScale).primaryText, : scale.scale(a.actionScale).primaryText,
icon: a.icon, icon: subtitle.isNotEmpty ? a.icon : null,
label: a.label, label: a.label,
padding: const EdgeInsets.all(2)), padding: const EdgeInsets.all(2)),
) )
@ -140,9 +140,12 @@ class SliderTile extends StatelessWidget {
: const EdgeInsets.fromLTRB(0, 2, 0, 2), : const EdgeInsets.fromLTRB(0, 2, 0, 2),
child: ListTile( child: ListTile(
onTap: onTap, onTap: onTap,
dense: true,
visualDensity: const VisualDensity(vertical: -4),
title: Text( title: Text(
title, title,
softWrap: true, overflow: TextOverflow.fade,
softWrap: false,
), ),
subtitle: subtitle.isNotEmpty ? Text(subtitle) : null, subtitle: subtitle.isNotEmpty ? Text(subtitle) : null,
iconColor: textColor, iconColor: textColor,

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_translate/flutter_translate.dart'; import 'package:flutter_translate/flutter_translate.dart';
import '../theme/theme.dart'; import '../theme.dart';
class EnterPasswordDialog extends StatefulWidget { class EnterPasswordDialog extends StatefulWidget {
const EnterPasswordDialog({ const EnterPasswordDialog({

View File

@ -5,7 +5,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_translate/flutter_translate.dart'; import 'package:flutter_translate/flutter_translate.dart';
import 'package:pinput/pinput.dart'; import 'package:pinput/pinput.dart';
import '../theme/theme.dart'; import '../theme.dart';
class EnterPinDialog extends StatefulWidget { class EnterPinDialog extends StatefulWidget {
const EnterPinDialog({ const EnterPinDialog({

View File

@ -0,0 +1,54 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart';
import '../theme.dart';
class OptionBox extends StatelessWidget {
const OptionBox(
{required String instructions,
required IconData buttonIcon,
required String buttonText,
required void Function() onClick,
super.key})
: _instructions = instructions,
_buttonIcon = buttonIcon,
_buttonText = buttonText,
_onClick = onClick;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
return Container(
constraints: const BoxConstraints(maxWidth: 400),
decoration: BoxDecoration(
color: scale.primaryScale.subtleBackground,
borderRadius:
BorderRadius.circular(8 * scaleConfig.borderRadiusScale),
border: Border.all(color: scale.primaryScale.border)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
style: theme.textTheme.labelMedium!
.copyWith(color: scale.primaryScale.appText),
softWrap: true,
textAlign: TextAlign.center,
_instructions),
ElevatedButton(
onPressed: _onClick,
child: Row(mainAxisSize: MainAxisSize.min, children: [
Icon(_buttonIcon, size: 24).paddingLTRB(0, 8, 8, 8),
Text(textAlign: TextAlign.center, _buttonText)
])).paddingLTRB(0, 12, 0, 0).toCenter()
]).paddingAll(12))
.paddingLTRB(24, 0, 24, 12);
}
final String _instructions;
final IconData _buttonIcon;
final String _buttonText;
final void Function() _onClick;
}

View File

View File

@ -9,7 +9,7 @@ bool get isWeb => kIsWeb;
bool get isDesktop => bool get isDesktop =>
!isWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS); !isWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS);
const kMobileWidthCutoff = 479.0; const kMobileWidthCutoff = 500.0;
bool isMobileWidth(BuildContext context) => bool isMobileWidth(BuildContext context) =>
MediaQuery.of(context).size.width < kMobileWidthCutoff; MediaQuery.of(context).size.width < kMobileWidthCutoff;

View File

@ -12,13 +12,15 @@ class StyledScaffold extends StatelessWidget {
final scale = theme.extension<ScaleScheme>()!; final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!; final scaleConfig = theme.extension<ScaleConfig>()!;
return clipBorder( return isDesktop
clipEnabled: true, ? clipBorder(
borderEnabled: scaleConfig.useVisualIndicators, clipEnabled: true,
borderRadius: 16 * scaleConfig.borderRadiusScale, borderEnabled: scaleConfig.useVisualIndicators,
borderColor: scale.primaryScale.border, borderRadius: 16 * scaleConfig.borderRadiusScale,
child: Scaffold(appBar: appBar, body: body, key: key)) borderColor: scale.primaryScale.border,
.paddingAll(32); child: Scaffold(appBar: appBar, body: body, key: key))
.paddingAll(32)
: Scaffold(appBar: appBar, body: body, key: key);
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////

View File

@ -1,5 +1,11 @@
export 'brightness_preferences.dart'; export 'brightness_preferences.dart';
export 'color_preferences.dart'; export 'color_preferences.dart';
export 'enter_password.dart';
export 'enter_pin.dart';
export 'option_box.dart';
export 'pop_control.dart';
export 'recovery_key_widget.dart';
export 'responsive.dart';
export 'scanner_error_widget.dart'; export 'scanner_error_widget.dart';
export 'styled_dialog.dart'; export 'styled_dialog.dart';
export 'styled_scaffold.dart'; export 'styled_scaffold.dart';

View File

@ -183,9 +183,9 @@ Widget styledTitleContainer({
child: Column(children: [ child: Column(children: [
Text( Text(
title, title,
style: textTheme.titleMedium! style: textTheme.titleSmall!
.copyWith(color: titleColor ?? scale.primaryScale.borderText), .copyWith(color: titleColor ?? scale.primaryScale.borderText),
).paddingLTRB(8, 8, 8, 4), ).paddingLTRB(8, 6, 8, 2),
DecoratedBox( DecoratedBox(
decoration: ShapeDecoration( decoration: ShapeDecoration(
color: color:

View File

@ -9,7 +9,7 @@ import 'package:loggy/loggy.dart';
import 'package:veilid_support/veilid_support.dart'; import 'package:veilid_support/veilid_support.dart';
import '../veilid_processor/views/developer.dart'; import '../veilid_processor/views/developer.dart';
import 'responsive.dart'; import '../theme/views/responsive.dart';
import 'state_logger.dart'; import 'state_logger.dart';
String wrapWithLogColor(LogLevel? level, String text) { String wrapWithLogColor(LogLevel? level, String text) {

View File

@ -1,13 +1,8 @@
export 'animations.dart'; export 'animations.dart';
export 'enter_password.dart';
export 'enter_pin.dart';
export 'loggy.dart'; export 'loggy.dart';
export 'misc.dart'; export 'misc.dart';
export 'package_info.dart'; export 'package_info.dart';
export 'phono_byte.dart'; export 'phono_byte.dart';
export 'pop_control.dart';
export 'responsive.dart';
export 'shared_preferences.dart'; export 'shared_preferences.dart';
export 'state_logger.dart'; export 'state_logger.dart';
export 'stream_listenable.dart'; export 'stream_listenable.dart';

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import '../../tools/responsive.dart'; import '../theme/views/responsive.dart';
export 'package:window_manager/window_manager.dart' show TitleBarStyle; export 'package:window_manager/window_manager.dart' show TitleBarStyle;
@ -21,7 +21,7 @@ Future<void> initializeWindowControl() async {
const windowOptions = WindowOptions( const windowOptions = WindowOptions(
size: Size(768, 1024), size: Size(768, 1024),
//minimumSize: Size(480, 480), minimumSize: Size(400, 500),
center: true, center: true,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
skipTaskbar: false, skipTaskbar: false,

View File

@ -273,59 +273,61 @@ class _DeveloperPageState extends State<DeveloperPage> {
body: GestureDetector( body: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(), onTap: () => FocusScope.of(context).unfocus(),
child: SafeArea( child: SafeArea(
bottom: false,
child: Column(children: [ child: Column(children: [
Stack(alignment: AlignmentDirectional.center, children: [ Stack(alignment: AlignmentDirectional.center, children: [
Image.asset('assets/images/ellet.png'), Image.asset('assets/images/ellet.png'),
TerminalView(globalDebugTerminal, TerminalView(globalDebugTerminal,
textStyle: kDefaultTerminalStyle, textStyle: kDefaultTerminalStyle,
controller: _terminalController, controller: _terminalController,
keyboardType: TextInputType.none, keyboardType: TextInputType.none,
//autofocus: true, //autofocus: true,
backgroundOpacity: _showEllet ? 0.75 : 1.0, backgroundOpacity: _showEllet ? 0.75 : 1.0,
onSecondaryTapDown: (details, offset) async { onSecondaryTapDown: (details, offset) async {
await copySelection(context); await copySelection(context);
}) })
]).expanded(), ]).expanded(),
TextField( TextField(
controller: _debugCommandController, controller: _debugCommandController,
onTapOutside: (event) { onTapOutside: (event) {
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
}, },
decoration: InputDecoration( decoration: InputDecoration(
filled: true, filled: true,
contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2), contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
8 * scaleConfig.borderRadiusScale), 8 * scaleConfig.borderRadiusScale),
borderSide: BorderSide.none), borderSide: BorderSide.none),
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
8 * scaleConfig.borderRadiusScale), 8 * scaleConfig.borderRadiusScale),
), ),
fillColor: scale.primaryScale.subtleBackground, fillColor: scale.primaryScale.subtleBackground,
hintText: translate('developer.command'), hintText: translate('developer.command'),
suffixIcon: IconButton( suffixIcon: IconButton(
icon: Icon(Icons.send, icon: Icon(Icons.send,
color: _debugCommandController.text.isEmpty color: _debugCommandController.text.isEmpty
? scale.primaryScale.primary.withAlpha(0x3F) ? scale.primaryScale.primary.withAlpha(0x3F)
: scale.primaryScale.primary), : scale.primaryScale.primary),
onPressed: _debugCommandController.text.isEmpty onPressed: _debugCommandController.text.isEmpty
? null ? null
: () async { : () async {
final debugCommand = _debugCommandController.text; final debugCommand =
_debugCommandController.clear(); _debugCommandController.text;
await _sendDebugCommand(debugCommand); _debugCommandController.clear();
}, await _sendDebugCommand(debugCommand);
)), },
onChanged: (_) { )),
setState(() => {}); onChanged: (_) {
}, setState(() => {});
onSubmitted: (debugCommand) async { },
_debugCommandController.clear(); onSubmitted: (debugCommand) async {
await _sendDebugCommand(debugCommand); _debugCommandController.clear();
}, await _sendDebugCommand(debugCommand);
).paddingAll(4) },
])))); ).paddingAll(4)
]))));
} }
@override @override

View File

@ -6,7 +6,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <file_saver/file_saver_plugin.h>
#include <pasteboard/pasteboard_plugin.h> #include <pasteboard/pasteboard_plugin.h>
#include <printing/printing_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h> #include <screen_retriever/screen_retriever_plugin.h>
#include <smart_auth/smart_auth_plugin.h> #include <smart_auth/smart_auth_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
@ -14,9 +16,15 @@
#include <window_manager/window_manager_plugin.h> #include <window_manager/window_manager_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_saver_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSaverPlugin");
file_saver_plugin_register_with_registrar(file_saver_registrar);
g_autoptr(FlPluginRegistrar) pasteboard_registrar = g_autoptr(FlPluginRegistrar) pasteboard_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin");
pasteboard_plugin_register_with_registrar(pasteboard_registrar); pasteboard_plugin_register_with_registrar(pasteboard_registrar);
g_autoptr(FlPluginRegistrar) printing_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin");
printing_plugin_register_with_registrar(printing_registrar);
g_autoptr(FlPluginRegistrar) screen_retriever_registrar = g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);

View File

@ -3,7 +3,9 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
file_saver
pasteboard pasteboard
printing
screen_retriever screen_retriever
smart_auth smart_auth
url_launcher_linux url_launcher_linux

View File

@ -5,10 +5,12 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import file_saver
import mobile_scanner import mobile_scanner
import package_info_plus import package_info_plus
import pasteboard import pasteboard
import path_provider_foundation import path_provider_foundation
import printing
import screen_retriever import screen_retriever
import share_plus import share_plus
import shared_preferences_foundation import shared_preferences_foundation
@ -19,10 +21,12 @@ import veilid
import window_manager import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin"))
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

View File

@ -1,4 +1,6 @@
PODS: PODS:
- file_saver (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0) - FlutterMacOS (1.0.0)
- mobile_scanner (5.1.1): - mobile_scanner (5.1.1):
- FlutterMacOS - FlutterMacOS
@ -9,6 +11,8 @@ PODS:
- path_provider_foundation (0.0.1): - path_provider_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- printing (1.0.0):
- FlutterMacOS
- screen_retriever (0.0.1): - screen_retriever (0.0.1):
- FlutterMacOS - FlutterMacOS
- share_plus (0.0.1): - share_plus (0.0.1):
@ -29,11 +33,13 @@ PODS:
- FlutterMacOS - FlutterMacOS
DEPENDENCIES: DEPENDENCIES:
- file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`)
- FlutterMacOS (from `Flutter/ephemeral`) - FlutterMacOS (from `Flutter/ephemeral`)
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`) - mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`) - pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- printing (from `Flutter/ephemeral/.symlinks/plugins/printing/macos`)
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
@ -44,6 +50,8 @@ DEPENDENCIES:
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
EXTERNAL SOURCES: EXTERNAL SOURCES:
file_saver:
:path: Flutter/ephemeral/.symlinks/plugins/file_saver/macos
FlutterMacOS: FlutterMacOS:
:path: Flutter/ephemeral :path: Flutter/ephemeral
mobile_scanner: mobile_scanner:
@ -54,6 +62,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos :path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos
path_provider_foundation: path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
printing:
:path: Flutter/ephemeral/.symlinks/plugins/printing/macos
screen_retriever: screen_retriever:
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
share_plus: share_plus:
@ -72,11 +82,13 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
SPEC CHECKSUMS: SPEC CHECKSUMS:
file_saver: 44e6fbf666677faf097302460e214e977fdd977b
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
mobile_scanner: 1efac1e53c294b24e3bb55bcc7f4deee0233a86b mobile_scanner: 1efac1e53c294b24e3bb55bcc7f4deee0233a86b
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99 pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
printing: 1dd6a1fce2209ec240698e2439a4adbb9b427637
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78

View File

@ -12,6 +12,8 @@
<true/> <true/>
<key>com.apple.security.network.server</key> <key>com.apple.security.network.server</key>
<true/> <true/>
<key>com.apple.security.print</key>
<true/>
<key>keychain-access-groups</key> <key>keychain-access-groups</key>
<array> <array>
<string>$(AppIdentifierPrefix)com.veilid.veilidchat</string> <string>$(AppIdentifierPrefix)com.veilid.veilidchat</string>

View File

@ -10,6 +10,8 @@
<true/> <true/>
<key>com.apple.security.network.server</key> <key>com.apple.security.network.server</key>
<true/> <true/>
<key>com.apple.security.print</key>
<true/>
<key>keychain-access-groups</key> <key>keychain-access-groups</key>
<array> <array>
<string>$(AppIdentifierPrefix)com.veilid.veilidchat</string> <string>$(AppIdentifierPrefix)com.veilid.veilidchat</string>

View File

@ -19,7 +19,9 @@ class IdentityInstance with _$IdentityInstance {
required PublicKey publicKey, required PublicKey publicKey,
// Secret key of identity instance // Secret key of identity instance
// Encrypted with DH(publicKey, SuperIdentity.secret) with appended salt // Encrypted with appended salt, key is DeriveSharedSecret(
// password = SuperIdentity.secret,
// salt = publicKey)
// Used to recover accounts without generating a new instance // Used to recover accounts without generating a new instance
@Uint8ListJsonConverter() required Uint8List encryptedSecretKey, @Uint8ListJsonConverter() required Uint8List encryptedSecretKey,

View File

@ -69,6 +69,12 @@ class WritableSuperIdentity {
/// Delete a super identity with secrets /// Delete a super identity with secrets
Future<void> delete() async => superIdentity.delete(); Future<void> delete() async => superIdentity.delete();
/// Produce a recovery key for this superIdentity
Uint8List get recoveryKey => (BytesBuilder()
..add(superIdentity.recordKey.decode())
..add(superSecret.decode()))
.toBytes();
/// xxx: migration support, new identities, reveal identity secret etc /// xxx: migration support, new identities, reveal identity secret etc
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@ -113,8 +119,8 @@ class WritableSuperIdentity {
// Make encrypted secret key // Make encrypted secret key
final cs = await Veilid.instance.getCryptoSystem(identityRecordKey.kind); final cs = await Veilid.instance.getCryptoSystem(identityRecordKey.kind);
final encryptionKey = await cs.generateSharedSecret( final encryptionKey = await cs.deriveSharedSecret(
identityPublicKey, superSecret, identityCryptoDomain); superSecret.decode(), identityPublicKey.decode());
final encryptedSecretKey = await cs.encryptNoAuthWithNonce( final encryptedSecretKey = await cs.encryptNoAuthWithNonce(
identitySecretKey.decode(), encryptionKey); identitySecretKey.decode(), encryptionKey);

View File

@ -97,6 +97,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
barcode:
dependency: transitive
description:
name: barcode
sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003
url: "https://pub.dev"
source: hosted
version: "2.2.8"
basic_utils: basic_utils:
dependency: "direct main" dependency: "direct main"
description: description:
@ -105,6 +113,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.7.0" version: "5.7.0"
bidi:
dependency: transitive
description:
name: bidi
sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63"
url: "https://pub.dev"
source: hosted
version: "2.0.10"
bloc: bloc:
dependency: "direct main" dependency: "direct main"
description: description:
@ -237,10 +253,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: camera_android name: camera_android
sha256: "3af7f0b55f184d392d2eec238aaa30552ebeef2915e5e094f5488bf50d6d7ca2" sha256: "981654e0e56a4c735f7ecc7bd3921385eb5f7dd13deaf4a6431255d9731df01a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.10.9+3" version: "0.10.9+7"
camera_avfoundation: camera_avfoundation:
dependency: transitive dependency: transitive
description: description:
@ -409,6 +425,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.1" version: "4.0.1"
dio:
dependency: transitive
description:
name: dio
sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714
url: "https://pub.dev"
source: hosted
version: "5.5.0+1"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
equatable: equatable:
dependency: "direct main" dependency: "direct main"
description: description:
@ -441,6 +473,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.0" version: "7.0.0"
file_saver:
dependency: "direct main"
description:
name: file_saver
sha256: d375b351e3331663abbaf99747abd72f159260c58fbbdbca9f926f02c01bdc48
url: "https://pub.dev"
source: hosted
version: "0.2.13"
fixnum: fixnum:
dependency: "direct main" dependency: "direct main"
description: description:
@ -466,10 +506,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_bloc name: flutter_bloc
sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.1.5" version: "8.1.6"
flutter_cache_manager: flutter_cache_manager:
dependency: transitive dependency: transitive
description: description:
@ -489,9 +529,11 @@ packages:
flutter_chat_ui: flutter_chat_ui:
dependency: "direct main" dependency: "direct main"
description: description:
path: "../flutter_chat_ui" path: "."
relative: true ref: main
source: path resolved-ref: d4b9d507d10f5d640156cacfd754f661f8c0f4c1
url: "https://gitlab.com/veilid/flutter-chat-ui.git"
source: git
version: "1.6.14" version: "1.6.14"
flutter_form_builder: flutter_form_builder:
dependency: "direct main" dependency: "direct main"
@ -534,10 +576,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_native_splash name: flutter_native_splash
sha256: edf39bcf4d74aca1eb2c1e43c3e445fd9f494013df7f0da752fefe72020eedc0 sha256: aa06fec78de2190f3db4319dd60fdc8d12b2626e93ef9828633928c2dcaea840
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0" version: "2.4.1"
flutter_parsed_text: flutter_parsed_text:
dependency: transitive dependency: transitive
description: description:
@ -627,10 +669,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: freezed_annotation name: freezed_annotation
sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d sha256: f54946fdb1fa7b01f780841937b1a80783a20b393485f3f6cdf336fd6f4705f2
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.1" version: "2.4.2"
frontend_server_client: frontend_server_client:
dependency: transitive dependency: transitive
description: description:
@ -659,10 +701,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: go_router name: go_router
sha256: abec47eb8c8c36ebf41d0a4c64dbbe7f956e39a012b3aafc530e951bdc12fe3f sha256: cdae1b9c8bd7efadcef6112e81c903662ef2ce105cbd220a04bbb7c3425b5554
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.1.4" version: "14.2.0"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@ -931,10 +973,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514" sha256: bca87b0165ffd7cdb9cad8edd22d18d2201e886d9a9f19b4fb3452ea7df3a72a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.5" version: "2.2.6"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
@ -967,6 +1009,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "2.2.1"
pdf:
dependency: "direct main"
description:
name: pdf
sha256: "81d5522bddc1ef5c28e8f0ee40b71708761753c163e0c93a40df56fd515ea0f0"
url: "https://pub.dev"
source: hosted
version: "3.11.0"
pdf_widget_wrapper:
dependency: transitive
description:
name: pdf_widget_wrapper
sha256: c930860d987213a3d58c7ec3b7ecf8085c3897f773e8dc23da9cae60a5d6d0f5
url: "https://pub.dev"
source: hosted
version: "1.0.4"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@ -995,10 +1053,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.4" version: "3.1.5"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -1031,6 +1089,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.0" version: "0.2.0"
printing:
dependency: "direct main"
description:
name: printing
sha256: cc4b256a5a89d5345488e3318897b595867f5181b8c5ed6fc63bfa5f2044aec3
url: "https://pub.dev"
source: hosted
version: "5.13.1"
protobuf: protobuf:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1075,10 +1141,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: qr_code_dart_scan name: qr_code_dart_scan
sha256: "948271f8dc39ab3798341783f0ab7bfdb723054fdc9ea0928c0a5be8503ee01c" sha256: "52912da40f5e40a197b890108af9d2a6baa0c5812b77bfb085c8ee9e3c4f1f52"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.0" version: "0.8.1"
qr_flutter: qr_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1135,6 +1201,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.9" version: "0.1.9"
screenshot:
dependency: "direct main"
description:
name: screenshot
sha256: "63817697a7835e6ce82add4228e15d233b74d42975c143ad8cfe07009fab866b"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
scroll_to_index: scroll_to_index:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1547,7 +1621,7 @@ packages:
path: "../veilid/veilid-flutter" path: "../veilid/veilid-flutter"
relative: true relative: true
source: path source: path
version: "0.3.2" version: "0.3.3"
veilid_support: veilid_support:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -28,6 +28,7 @@ dependencies:
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
equatable: ^2.0.5 equatable: ^2.0.5
fast_immutable_collections: ^10.2.4 fast_immutable_collections: ^10.2.4
file_saver: ^0.2.13
fixnum: ^1.1.0 fixnum: ^1.1.0
flutter: flutter:
sdk: flutter sdk: flutter
@ -63,8 +64,10 @@ dependencies:
pasteboard: ^0.2.0 pasteboard: ^0.2.0
path: ^1.9.0 path: ^1.9.0
path_provider: ^2.1.3 path_provider: ^2.1.3
pdf: ^3.11.0
pinput: ^4.0.0 pinput: ^4.0.0
preload_page_view: ^0.2.0 preload_page_view: ^0.2.0
printing: ^5.13.1
protobuf: ^3.1.0 protobuf: ^3.1.0
provider: ^6.1.2 provider: ^6.1.2
qr_code_dart_scan: ^0.8.0 qr_code_dart_scan: ^0.8.0
@ -72,6 +75,7 @@ dependencies:
quickalert: ^1.1.0 quickalert: ^1.1.0
radix_colors: ^1.0.4 radix_colors: ^1.0.4
reorderable_grid: ^1.0.10 reorderable_grid: ^1.0.10
screenshot: ^3.0.0
scroll_to_index: ^3.0.1 scroll_to_index: ^3.0.1
searchable_listview: ^2.14.0 searchable_listview: ^2.14.0
share_plus: ^9.0.0 share_plus: ^9.0.0
@ -95,13 +99,13 @@ dependencies:
xterm: ^4.0.0 xterm: ^4.0.0
zxing2: ^0.2.3 zxing2: ^0.2.3
dependency_overrides: # dependency_overrides:
# async_tools: # async_tools:
# path: ../dart_async_tools # path: ../dart_async_tools
# bloc_advanced_tools: # bloc_advanced_tools:
# path: ../bloc_advanced_tools # path: ../bloc_advanced_tools
flutter_chat_ui: # flutter_chat_ui:
path: ../flutter_chat_ui # path: ../flutter_chat_ui
dev_dependencies: dev_dependencies:
build_runner: ^2.4.11 build_runner: ^2.4.11
@ -146,6 +150,8 @@ flutter:
- assets/images/title.svg - assets/images/title.svg
- assets/images/vlogo.svg - assets/images/vlogo.svg
- assets/images/ellet.png - assets/images/ellet.png
# Printing
- assets/js/pdf/3.2.146/pdf.min.js
# Fonts # Fonts
fonts: fonts:
- family: Source Code Pro - family: Source Code Pro

View File

@ -56,7 +56,10 @@
} }
run(); run();
</script> </script>
<script> <script>
var dartPdfJsBaseUrl = "assets/js/pdf/3.2.146/";
window.addEventListener('load', function (ev) { window.addEventListener('load', function (ev) {
// Download main.dart.js // Download main.dart.js
_flutter.loader.loadEntrypoint({ _flutter.loader.loadEntrypoint({

View File

@ -6,7 +6,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <file_saver/file_saver_plugin.h>
#include <pasteboard/pasteboard_plugin.h> #include <pasteboard/pasteboard_plugin.h>
#include <printing/printing_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h> #include <screen_retriever/screen_retriever_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h> #include <share_plus/share_plus_windows_plugin_c_api.h>
#include <smart_auth/smart_auth_plugin.h> #include <smart_auth/smart_auth_plugin.h>
@ -15,8 +17,12 @@
#include <window_manager/window_manager_plugin.h> #include <window_manager/window_manager_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSaverPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSaverPlugin"));
PasteboardPluginRegisterWithRegistrar( PasteboardPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PasteboardPlugin")); registry->GetRegistrarForPlugin("PasteboardPlugin"));
PrintingPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PrintingPlugin"));
ScreenRetrieverPluginRegisterWithRegistrar( ScreenRetrieverPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
SharePlusWindowsPluginCApiRegisterWithRegistrar( SharePlusWindowsPluginCApiRegisterWithRegistrar(

View File

@ -3,7 +3,9 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
file_saver
pasteboard pasteboard
printing
screen_retriever screen_retriever
share_plus share_plus
smart_auth smart_auth