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

View File

@ -132,7 +132,8 @@ class AccountRepository {
/// Creates a new super identity, an identity instance, an account associated
/// with the identity instance, stores the account in the identity key and
/// 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');
final wsi = await WritableSuperIdentity.create();
try {
@ -146,7 +147,7 @@ class AccountRepository {
localAccount.superIdentity.recordKey, EncryptionKeyType.none, '');
assert(ok, 'login with none should never fail');
return wsi.superSecret;
return wsi;
} on Exception catch (_) {
await wsi.delete();
rethrow;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,18 @@
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:async_tools/async_tools.dart';
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:file_saver/file_saver.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.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 '../../layout/default_app_bar.dart';
@ -13,13 +21,18 @@ import '../../tools/tools.dart';
import '../../veilid_processor/veilid_processor.dart';
class ShowRecoveryKeyPage extends StatefulWidget {
const ShowRecoveryKeyPage({required SecretKey secretKey, super.key})
: _secretKey = secretKey;
const ShowRecoveryKeyPage(
{required WritableSuperIdentity writableSuperIdentity,
required String name,
super.key})
: _writableSuperIdentity = writableSuperIdentity,
_name = name;
@override
ShowRecoveryKeyPageState createState() => ShowRecoveryKeyPageState();
final SecretKey _secretKey;
final WritableSuperIdentity _writableSuperIdentity;
final String _name;
}
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 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 cardsize =
min<double>(MediaQuery.of(context).size.shortestSide - 48.0, 400);
final phonoString = prettyPhonoString(
encodePhono(_secretKey.decode()),
wordsPerLine: 2,
);
return Dialog(
shape: RoundedRectangleBorder(
side: const BorderSide(width: 2),
@ -55,65 +149,19 @@ class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
constraints: BoxConstraints(
minWidth: cardsize,
maxWidth: cardsize,
minHeight: cardsize,
maxHeight: cardsize),
child: Column(mainAxisSize: MainAxisSize.min, children: [
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);
minHeight: cardsize + 16,
maxHeight: cardsize + 16),
child: _recoveryKeyWidget(context, recoveryKey, name)));
}
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
final secretKey = widget._secretKey;
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
// final scale = theme.extension<ScaleScheme>()!;
// final scaleConfig = theme.extension<ScaleConfig>()!;
final displayModalHUD = _isInAsyncCall;
return StyledScaffold(
// resizeToAvoidBottomInset: false,
@ -146,42 +194,55 @@ class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
Text(
textAlign: TextAlign.center,
translate('show_recovery_key_page.instructions_options'))
.paddingLTRB(12, 0, 12, 12),
_optionBox(
.paddingLTRB(12, 0, 12, 24),
OptionBox(
instructions:
translate('show_recovery_key_page.instructions_print'),
buttonIcon: const Icon(Icons.print),
buttonIcon: Icons.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: () {
//
singleFuture(this, () async {
await showDialog<void>(
context: context,
builder: (context) => _recoveryKeyWidget(secretKey));
await _printRecoveryKey(context,
widget._writableSuperIdentity.recoveryKey, widget._name);
});
setState(() {
_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:
translate('show_recovery_key_page.instructions_share'),
buttonIcon: const Icon(Icons.ios_share),
buttonIcon: Icons.ios_share,
buttonText: translate('show_recovery_key_page.share'),
onClick: () {
//
singleFuture(this, () async {
await _shareRecoveryKey(context,
widget._writableSuperIdentity.recoveryKey, widget._name);
});
setState(() {
_codeHandled = true;
});
@ -198,8 +259,9 @@ class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
},
child: Text(translate('button.finish')).paddingAll(8))
.paddingAll(12))
])));
]))).withModalHUD(context, displayModalHUD);
}
bool _codeHandled = false;
bool _isInAsyncCall = false;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,12 +10,15 @@ import '../../theme/theme.dart';
import 'contact_item_widget.dart';
import 'empty_contact_list_widget.dart';
class ContactListWidget extends StatelessWidget {
class ContactListWidget extends StatefulWidget {
const ContactListWidget(
{required this.contactList, required this.disabled, super.key});
final IList<proto.Contact> contactList;
final bool disabled;
@override
State<ContactListWidget> createState() => _ContactListWidgetState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@ -23,45 +26,51 @@ class ContactListWidget extends StatelessWidget {
..add(IterableProperty<proto.Contact>('contactList', contactList))
..add(DiagnosticsProperty<bool>('disabled', disabled));
}
}
class _ContactListWidgetState extends State<ContactListWidget> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
return SizedBox.expand(
child: styledTitleContainer(
context: context,
title: translate('contact_list.title'),
child: SizedBox.expand(
child: (contactList.isEmpty)
? const EmptyContactListWidget()
: SearchableList<proto.Contact>(
initialList: contactList.toList(),
itemBuilder: (c) =>
ContactItemWidget(contact: c, disabled: disabled)
.paddingLTRB(0, 4, 0, 0),
filter: (value) {
final lowerValue = value.toLowerCase();
return contactList
.where((element) =>
element.nickname
.toLowerCase()
.contains(lowerValue) ||
element.profile.name
.toLowerCase()
.contains(lowerValue) ||
element.profile.pronouns
.toLowerCase()
.contains(lowerValue))
.toList();
},
spaceBetweenSearchAndList: 4,
defaultSuffixIconColor: scale.primaryScale.border,
inputDecoration: InputDecoration(
labelText: translate('contact_list.search'),
),
).paddingAll(8),
))).paddingLTRB(8, 0, 8, 8);
return styledTitleContainer(
context: context,
title: translate('contact_list.title'),
child: SearchableList<proto.Contact>(
shrinkWrap: true,
initialList: widget.contactList.toList(),
itemBuilder: (c) =>
ContactItemWidget(contact: c, disabled: widget.disabled)
.paddingLTRB(0, 4, 0, 0),
filter: (value) {
final lowerValue = value.toLowerCase();
return widget.contactList
.where((element) =>
element.nickname.toLowerCase().contains(lowerValue) ||
element.profile.name.toLowerCase().contains(lowerValue) ||
element.profile.pronouns.toLowerCase().contains(lowerValue))
.toList();
},
searchFieldHeight: 40,
spaceBetweenSearchAndList: 4,
emptyWidget: const EmptyContactListWidget(),
defaultSuffixIconColor: scale.primaryScale.border,
closeKeyboardWhenScrolling: true,
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(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
@ -25,6 +26,7 @@ class EmptyContactListWidget extends StatelessWidget {
size: 48,
),
Text(
textAlign: TextAlign.center,
translate('contact_list.invite_people'),
style: textTheme.bodyMedium?.copyWith(
color: scale.primaryScale.subtleBorder,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:pinput/pinput.dart';
import '../theme/theme.dart';
import '../theme.dart';
class EnterPinDialog extends StatefulWidget {
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 =>
!isWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS);
const kMobileWidthCutoff = 479.0;
const kMobileWidthCutoff = 500.0;
bool isMobileWidth(BuildContext context) =>
MediaQuery.of(context).size.width < kMobileWidthCutoff;

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.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;
@ -21,7 +21,7 @@ Future<void> initializeWindowControl() async {
const windowOptions = WindowOptions(
size: Size(768, 1024),
//minimumSize: Size(480, 480),
minimumSize: Size(400, 500),
center: true,
backgroundColor: Colors.transparent,
skipTaskbar: false,

View File

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

View File

@ -6,7 +6,9 @@
#include "generated_plugin_registrant.h"
#include <file_saver/file_saver_plugin.h>
#include <pasteboard/pasteboard_plugin.h>
#include <printing/printing_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <smart_auth/smart_auth_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
@ -14,9 +16,15 @@
#include <window_manager/window_manager_plugin.h>
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 =
fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin");
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 =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,9 @@ class IdentityInstance with _$IdentityInstance {
required PublicKey publicKey,
// 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
@Uint8ListJsonConverter() required Uint8List encryptedSecretKey,

View File

@ -69,6 +69,12 @@ class WritableSuperIdentity {
/// Delete a super identity with secrets
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
////////////////////////////////////////////////////////////////////////////
@ -113,8 +119,8 @@ class WritableSuperIdentity {
// Make encrypted secret key
final cs = await Veilid.instance.getCryptoSystem(identityRecordKey.kind);
final encryptionKey = await cs.generateSharedSecret(
identityPublicKey, superSecret, identityCryptoDomain);
final encryptionKey = await cs.deriveSharedSecret(
superSecret.decode(), identityPublicKey.decode());
final encryptedSecretKey = await cs.encryptNoAuthWithNonce(
identitySecretKey.decode(), encryptionKey);

View File

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

View File

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

View File

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

View File

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

View File

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