ui cleanup

This commit is contained in:
Christien Rioux 2024-04-10 16:13:08 -04:00
parent 1f99279cd2
commit 23ec185324
26 changed files with 419 additions and 244 deletions

View File

@ -58,12 +58,15 @@
"account_page": { "account_page": {
"contact_invitations": "Contact Invitations" "contact_invitations": "Contact Invitations"
}, },
"accounts_menu": { "add_contact_sheet": {
"invite_contact": "Invite Contact", "new_contact": "New Contact",
"create_invite": "Create Invitation", "create_invite": "Create Invitation",
"scan_invite": "Scan Invitation", "scan_invite": "Scan Invitation",
"paste_invite": "Paste Invitation" "paste_invite": "Paste Invitation"
}, },
"add_chat_sheet": {
"new_chat": "New Chat"
},
"create_invitation_dialog": { "create_invitation_dialog": {
"title": "Create Contact Invitation", "title": "Create Contact Invitation",
"connect_with_me": "Connect with me on VeilidChat!", "connect_with_me": "Connect with me on VeilidChat!",

View File

@ -113,7 +113,9 @@ class NewAccountPageState extends State<NewAccountPage> {
body: _newAccountForm( body: _newAccountForm(
context, context,
onSubmit: (formKey) async { onSubmit: (formKey) async {
// dismiss the keyboard by unfocusing the textfield
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
try { try {
final name = final name =
_formKey.currentState!.fields[formFieldName]!.value as String; _formKey.currentState!.fields[formFieldName]!.value as String;

View File

@ -1,6 +1,7 @@
import 'package:animated_theme_switcher/animated_theme_switcher.dart'; import 'package:animated_theme_switcher/animated_theme_switcher.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_translate/flutter_translate.dart'; import 'package:flutter_translate/flutter_translate.dart';
@ -12,9 +13,15 @@ import 'init.dart';
import 'layout/splash.dart'; import 'layout/splash.dart';
import 'router/router.dart'; import 'router/router.dart';
import 'settings/settings.dart'; import 'settings/settings.dart';
import 'theme/models/theme_preference.dart';
import 'tick.dart'; import 'tick.dart';
import 'tools/loggy.dart';
import 'veilid_processor/veilid_processor.dart'; import 'veilid_processor/veilid_processor.dart';
class ReloadThemeIntent extends Intent {
const ReloadThemeIntent();
}
class VeilidChatApp extends StatelessWidget { class VeilidChatApp extends StatelessWidget {
const VeilidChatApp({ const VeilidChatApp({
required this.initialThemeData, required this.initialThemeData,
@ -25,6 +32,28 @@ class VeilidChatApp extends StatelessWidget {
final ThemeData initialThemeData; final ThemeData initialThemeData;
void _reloadTheme(BuildContext context) {
log.info('Reloading theme');
final theme =
PreferencesRepository.instance.value.themePreferences.themeData();
ThemeSwitcher.of(context).changeTheme(theme: theme);
}
Widget _buildShortcuts(
{required BuildContext context,
required Widget Function(BuildContext) builder}) =>
ThemeSwitcher(
builder: (context) => Shortcuts(
shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(
LogicalKeyboardKey.alt, LogicalKeyboardKey.keyR):
const ReloadThemeIntent(),
},
child: Actions(actions: <Type, Action<Intent>>{
ReloadThemeIntent: CallbackAction<ReloadThemeIntent>(
onInvoke: (intent) => _reloadTheme(context)),
}, child: Focus(autofocus: true, child: builder(context)))));
@override @override
Widget build(BuildContext context) => FutureProvider<VeilidChatGlobalInit?>( Widget build(BuildContext context) => FutureProvider<VeilidChatGlobalInit?>(
initialData: null, initialData: null,
@ -68,9 +97,12 @@ class VeilidChatApp extends StatelessWidget {
) )
], ],
child: BackgroundTicker( child: BackgroundTicker(
child: _buildShortcuts(
context: context,
builder: (context) => MaterialApp.router( builder: (context) => MaterialApp.router(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
routerConfig: context.watch<RouterCubit>().router(), routerConfig:
context.watch<RouterCubit>().router(),
title: translate('app.title'), title: translate('app.title'),
theme: theme, theme: theme,
localizationsDelegates: [ localizationsDelegates: [
@ -82,7 +114,7 @@ class VeilidChatApp extends StatelessWidget {
supportedLocales: supportedLocales:
localizationDelegate.supportedLocales, localizationDelegate.supportedLocales,
locale: localizationDelegate.currentLocale, locale: localizationDelegate.currentLocale,
)), ))),
)), )),
); );
}); });

View File

@ -0,0 +1,71 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_translate/flutter_translate.dart';
import '../../theme/theme.dart';
import '../../tools/tools.dart';
Widget newChatBottomSheetBuilder(
BuildContext sheetContext, BuildContext context) {
final theme = Theme.of(sheetContext);
final scale = theme.extension<ScaleScheme>()!;
return KeyboardListener(
focusNode: FocusNode(),
onKeyEvent: (ke) {
if (ke.logicalKey == LogicalKeyboardKey.escape) {
Navigator.pop(sheetContext);
}
},
child: styledBottomSheet(
context: context,
title: translate('add_chat_sheet.new_chat'),
child: SizedBox(
height: 160,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
'Group and custom chat functionality is not available yet')
// Column(children: [
// IconButton(
// onPressed: () async {
// Navigator.pop(sheetContext);
// await CreateInvitationDialog.show(context);
// },
// iconSize: 64,
// icon: const Icon(Icons.contact_page),
// color: scale.primaryScale.background),
// Text(
// translate('accounts_menu.create_invite'),
// )
// ]),
// Column(children: [
// IconButton(
// onPressed: () async {
// Navigator.pop(sheetContext);
// await ScanInvitationDialog.show(context);
// },
// iconSize: 64,
// icon: const Icon(Icons.qr_code_scanner),
// color: scale.primaryScale.background),
// Text(
// translate('accounts_menu.scan_invite'),
// )
// ]),
// Column(children: [
// IconButton(
// onPressed: () async {
// Navigator.pop(sheetContext);
// await PasteInvitationDialog.show(context);
// },
// iconSize: 64,
// icon: const Icon(Icons.paste),
// color: scale.primaryScale.background),
// Text(
// translate('accounts_menu.paste_invite'),
// )
// ])
]).paddingAll(16))));
}

View File

@ -1,3 +1,4 @@
export 'chat_component.dart'; export 'chat_component.dart';
export 'empty_chat_widget.dart'; export 'empty_chat_widget.dart';
export 'new_chat_bottom_sheet.dart';
export 'no_conversation_widget.dart'; export 'no_conversation_widget.dart';

View File

@ -7,7 +7,6 @@ import 'package:searchable_listview/searchable_listview.dart';
import '../../contacts/contacts.dart'; import '../../contacts/contacts.dart';
import '../../proto/proto.dart' as proto; import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart';
import '../../tools/tools.dart'; import '../../tools/tools.dart';
import '../chat_list.dart'; import '../chat_list.dart';
@ -17,10 +16,6 @@ class ChatSingleContactListWidget extends StatelessWidget {
@override @override
// ignore: prefer_expression_function_bodies // ignore: prefer_expression_function_bodies
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context);
//final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!;
final contactListV = context.watch<ContactListCubit>().state; final contactListV = context.watch<ContactListCubit>().state;
return contactListV.builder((context, contactList) { return contactListV.builder((context, contactList) {
@ -29,7 +24,8 @@ class ChatSingleContactListWidget extends StatelessWidget {
valueMapper: (c) => c); valueMapper: (c) => c);
final chatListV = context.watch<ChatListCubit>().state; final chatListV = context.watch<ChatListCubit>().state;
return chatListV.builder((context, chatList) => SizedBox.expand( return chatListV
.builder((context, chatList) => SizedBox.expand(
child: styledTitleContainer( child: styledTitleContainer(
context: context, context: context,
title: translate('chat_list.chats'), title: translate('chat_list.chats'),
@ -67,17 +63,10 @@ class ChatSingleContactListWidget extends StatelessWidget {
spaceBetweenSearchAndList: 4, spaceBetweenSearchAndList: 4,
inputDecoration: InputDecoration( inputDecoration: InputDecoration(
labelText: translate('chat_list.search'), labelText: translate('chat_list.search'),
contentPadding: const EdgeInsets.all(2),
fillColor: scale.primaryScale.elementBackground,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: scale.primaryScale.hoverBorder,
),
borderRadius: BorderRadius.circular(8),
), ),
), ),
).paddingAll(8)))) ).paddingAll(8))))
.paddingLTRB(8, 0, 8, 8)); .paddingLTRB(8, 0, 8, 8);
}); });
} }
} }

View File

@ -182,7 +182,7 @@ class CreateInvitationDialogState extends State<CreateInvitationDialog> {
LengthLimitingTextInputFormatter(128), LengthLimitingTextInputFormatter(128),
], ],
decoration: InputDecoration( decoration: InputDecoration(
border: const OutlineInputBorder(), //border: const OutlineInputBorder(),
hintText: hintText:
translate('create_invitation_dialog.enter_message_hint'), translate('create_invitation_dialog.enter_message_hint'),
labelText: translate('create_invitation_dialog.message')), labelText: translate('create_invitation_dialog.message')),

View File

@ -0,0 +1,72 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_translate/flutter_translate.dart';
import '../../theme/theme.dart';
import '../../tools/tools.dart';
import 'create_invitation_dialog.dart';
import 'paste_invitation_dialog.dart';
import 'scan_invitation_dialog.dart';
Widget newContactBottomSheetBuilder(
BuildContext sheetContext, BuildContext context) {
final theme = Theme.of(sheetContext);
final scale = theme.extension<ScaleScheme>()!;
return KeyboardListener(
focusNode: FocusNode(),
onKeyEvent: (ke) {
if (ke.logicalKey == LogicalKeyboardKey.escape) {
Navigator.pop(sheetContext);
}
},
child: styledBottomSheet(
context: context,
title: translate('add_contact_sheet.new_contact'),
child: SizedBox(
height: 160,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await CreateInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.contact_page),
color: scale.primaryScale.background),
Text(
translate('add_contact_sheet.create_invite'),
)
]),
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await ScanInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.qr_code_scanner),
color: scale.primaryScale.background),
Text(
translate('add_contact_sheet.scan_invite'),
)
]),
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await PasteInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.paste),
color: scale.primaryScale.background),
Text(
translate('add_contact_sheet.paste_invite'),
)
])
]).paddingAll(16))));
}

View File

@ -1,66 +0,0 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_translate/flutter_translate.dart';
import '../../theme/theme.dart';
import 'paste_invitation_dialog.dart';
import 'scan_invitation_dialog.dart';
import 'create_invitation_dialog.dart';
Widget newContactInvitationBottomSheetBuilder(
BuildContext sheetContext, BuildContext context) {
final theme = Theme.of(sheetContext);
final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!;
return KeyboardListener(
focusNode: FocusNode(),
onKeyEvent: (ke) {
if (ke.logicalKey == LogicalKeyboardKey.escape) {
Navigator.pop(sheetContext);
}
},
child: SizedBox(
height: 200,
child: Column(children: [
Text(translate('accounts_menu.invite_contact'),
style: textTheme.titleMedium)
.paddingAll(8),
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await CreateInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.contact_page),
color: scale.primaryScale.background),
Text(translate('accounts_menu.create_invite'))
]),
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await ScanInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.qr_code_scanner),
color: scale.primaryScale.background),
Text(translate('accounts_menu.scan_invite'))
]),
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await PasteInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.paste),
color: scale.primaryScale.background),
Text(translate('accounts_menu.paste_invite'))
])
]).expanded()
])));
}

View File

@ -125,7 +125,6 @@ class PasteInvitationDialogState extends State<PasteInvitationDialog> {
maxLines: null, maxLines: null,
controller: _pasteTextController, controller: _pasteTextController,
decoration: const InputDecoration( decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: '--- BEGIN VEILIDCHAT CONTACT INVITE ----\n' hintText: '--- BEGIN VEILIDCHAT CONTACT INVITE ----\n'
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n' 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
'---- END VEILIDCHAT CONTACT INVITE -----\n', '---- END VEILIDCHAT CONTACT INVITE -----\n',

View File

@ -3,6 +3,6 @@ export 'contact_invitation_item_widget.dart';
export 'contact_invitation_list_widget.dart'; export 'contact_invitation_list_widget.dart';
export 'create_invitation_dialog.dart'; export 'create_invitation_dialog.dart';
export 'invitation_dialog.dart'; export 'invitation_dialog.dart';
export 'new_contact_invitation_bottom_sheet.dart'; export 'new_contact_bottom_sheet.dart';
export 'paste_invitation_dialog.dart'; export 'paste_invitation_dialog.dart';
export 'scan_invitation_dialog.dart'; export 'scan_invitation_dialog.dart';

View File

@ -29,14 +29,15 @@ class ContactItemWidget extends StatelessWidget {
final remoteConversationKey = final remoteConversationKey =
contact.remoteConversationRecordKey.toVeilid(); contact.remoteConversationRecordKey.toVeilid();
const selected = const selected = false; // xxx: eventually when we have selectable contacts:
false; // xxx: eventually when we have selectable contacts: activeContactCubit.state == remoteConversationRecordKey; // activeContactCubit.state == remoteConversationRecordKey;
return Container( return Container(
margin: const EdgeInsets.fromLTRB(0, 4, 0, 0), margin: const EdgeInsets.fromLTRB(0, 4, 0, 0),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration( decoration: ShapeDecoration(
color: selected color: selected
// ignore: dead_code
? scale.primaryScale.activeElementBackground ? scale.primaryScale.activeElementBackground
: scale.primaryScale.hoverElementBackground, : scale.primaryScale.hoverElementBackground,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -102,9 +103,11 @@ class ContactItemWidget extends StatelessWidget {
? Text(contact.editedProfile.pronouns) ? Text(contact.editedProfile.pronouns)
: null, : null,
iconColor: selected iconColor: selected
// ignore: dead_code
? scale.primaryScale.appText ? scale.primaryScale.appText
: scale.primaryScale.subtleText, : scale.primaryScale.subtleText,
textColor: selected textColor: selected
// ignore: dead_code
? scale.primaryScale.appText ? scale.primaryScale.appText
: scale.primaryScale.subtleText, : scale.primaryScale.subtleText,
selectedColor: scale.primaryScale.appText, selectedColor: scale.primaryScale.appText,

View File

@ -6,7 +6,6 @@ import 'package:flutter_translate/flutter_translate.dart';
import 'package:searchable_listview/searchable_listview.dart'; import 'package:searchable_listview/searchable_listview.dart';
import '../../proto/proto.dart' as proto; import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart';
import '../../tools/tools.dart'; import '../../tools/tools.dart';
import 'contact_item_widget.dart'; import 'contact_item_widget.dart';
import 'empty_contact_list_widget.dart'; import 'empty_contact_list_widget.dart';
@ -26,12 +25,7 @@ class ContactListWidget extends StatelessWidget {
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => SizedBox.expand(
final theme = Theme.of(context);
//final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!;
return SizedBox.expand(
child: styledTitleContainer( child: styledTitleContainer(
context: context, context: context,
title: translate('contact_list.title'), title: translate('contact_list.title'),
@ -57,16 +51,7 @@ class ContactListWidget extends StatelessWidget {
spaceBetweenSearchAndList: 4, spaceBetweenSearchAndList: 4,
inputDecoration: InputDecoration( inputDecoration: InputDecoration(
labelText: translate('contact_list.search'), labelText: translate('contact_list.search'),
contentPadding: const EdgeInsets.all(2),
fillColor: scale.primaryScale.appText,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: scale.primaryScale.hoverBorder,
),
borderRadius: BorderRadius.circular(8),
),
), ),
).paddingAll(8), ).paddingAll(8),
))).paddingLTRB(8, 0, 8, 8); ))).paddingLTRB(8, 0, 8, 8);
} }
}

View File

@ -12,8 +12,6 @@ class HomeAccountReadyChat extends StatefulWidget {
} }
class HomeAccountReadyChatState extends State<HomeAccountReadyChat> { class HomeAccountReadyChatState extends State<HomeAccountReadyChat> {
final _unfocusNode = FocusNode();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -26,7 +24,6 @@ class HomeAccountReadyChatState extends State<HomeAccountReadyChat> {
@override @override
void dispose() { void dispose() {
_unfocusNode.dispose();
super.dispose(); super.dispose();
} }
@ -42,8 +39,6 @@ class HomeAccountReadyChatState extends State<HomeAccountReadyChat> {
@override @override
Widget build(BuildContext context) => SafeArea( Widget build(BuildContext context) => SafeArea(
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(_unfocusNode),
child: buildChatComponent(context), child: buildChatComponent(context),
)); );
} }

View File

@ -18,8 +18,6 @@ class AccountPage extends StatefulWidget {
} }
class AccountPageState extends State<AccountPage> { class AccountPageState extends State<AccountPage> {
final _unfocusNode = FocusNode();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -27,7 +25,6 @@ class AccountPageState extends State<AccountPage> {
@override @override
void dispose() { void dispose() {
_unfocusNode.dispose();
super.dispose(); super.dispose();
} }

View File

@ -11,8 +11,6 @@ class ChatsPage extends StatefulWidget {
} }
class ChatsPageState extends State<ChatsPage> { class ChatsPageState extends State<ChatsPage> {
final _unfocusNode = FocusNode();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -20,7 +18,6 @@ class ChatsPageState extends State<ChatsPage> {
@override @override
void dispose() { void dispose() {
_unfocusNode.dispose();
super.dispose(); super.dispose();
} }

View File

@ -5,9 +5,9 @@ import 'package:flutter/rendering.dart';
import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_translate/flutter_translate.dart'; import 'package:flutter_translate/flutter_translate.dart';
import 'package:preload_page_view/preload_page_view.dart'; import 'package:preload_page_view/preload_page_view.dart';
import 'package:stylish_bottom_bar/model/bar_items.dart';
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart'; import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
import '../../../../chat/chat.dart';
import '../../../../contact_invitation/contact_invitation.dart'; import '../../../../contact_invitation/contact_invitation.dart';
import '../../../../theme/theme.dart'; import '../../../../theme/theme.dart';
import '../../../../tools/tools.dart'; import '../../../../tools/tools.dart';
@ -28,8 +28,6 @@ class MainPager extends StatefulWidget {
class MainPagerState extends State<MainPager> with TickerProviderStateMixin { class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
final _unfocusNode = FocusNode();
var _currentPage = 0; var _currentPage = 0;
final pageController = PreloadPageController(); final pageController = PreloadPageController();
@ -56,7 +54,6 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
@override @override
void dispose() { void dispose() {
_unfocusNode.dispose();
pageController.dispose(); pageController.dispose();
super.dispose(); super.dispose();
} }
@ -127,21 +124,13 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
}); });
} }
Widget _onNewChatBottomSheetBuilder(
BuildContext sheetContext, BuildContext context) =>
const SizedBox(
height: 200,
child: Center(
child: Text(
'Group and custom chat functionality is not available yet')));
Widget _bottomSheetBuilder(BuildContext sheetContext, BuildContext context) { Widget _bottomSheetBuilder(BuildContext sheetContext, BuildContext context) {
if (_currentPage == 0) { if (_currentPage == 0) {
// New contact invitation // New contact invitation
return newContactInvitationBottomSheetBuilder(sheetContext, context); return newContactBottomSheetBuilder(sheetContext, context);
} else if (_currentPage == 1) { } else if (_currentPage == 1) {
// New chat // New chat
return _onNewChatBottomSheetBuilder(sheetContext, context); return newChatBottomSheetBuilder(sheetContext, context);
} else { } else {
// Unknown error // Unknown error
return debugPage('unknown page'); return debugPage('unknown page');

View File

@ -19,8 +19,6 @@ class HomeShell extends StatefulWidget {
} }
class HomeShellState extends State<HomeShell> { class HomeShellState extends State<HomeShell> {
final _unfocusNode = FocusNode();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -28,7 +26,6 @@ class HomeShellState extends State<HomeShell> {
@override @override
void dispose() { void dispose() {
_unfocusNode.dispose();
super.dispose(); super.dispose();
} }
@ -69,11 +66,9 @@ class HomeShellState extends State<HomeShell> {
// XXX: eventually write account switcher here // XXX: eventually write account switcher here
return SafeArea( return SafeArea(
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(_unfocusNode),
child: DecoratedBox( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: scale.primaryScale.activeElementBackground), color: scale.primaryScale.activeElementBackground),
child: buildWithLogin(context)))); child: buildWithLogin(context)));
} }
} }

View File

@ -53,11 +53,11 @@ class SettingsPageState extends State<SettingsPage> {
child: ListView( child: ListView(
children: [ children: [
buildSettingsPageColorPreferences( buildSettingsPageColorPreferences(
onChanged: () => setState(() {})), context: context, onChanged: () => setState(() {})),
buildSettingsPageBrightnessPreferences( buildSettingsPageBrightnessPreferences(
onChanged: () => setState(() {})), context: context, onChanged: () => setState(() {})),
], ].map((x) => x.paddingLTRB(0, 0, 0, 8)).toList(),
), ),
).paddingSymmetric(horizontal: 24, vertical: 8), ).paddingSymmetric(horizontal: 24, vertical: 16),
))); )));
} }

View File

@ -1,7 +1,10 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_chat_ui/flutter_chat_ui.dart'; import 'package:flutter_chat_ui/flutter_chat_ui.dart';
import 'package:radix_colors/radix_colors.dart'; import 'package:radix_colors/radix_colors.dart';
import '../../tools/tools.dart';
import 'scale_color.dart'; import 'scale_color.dart';
import 'scale_scheme.dart'; import 'scale_scheme.dart';
@ -571,26 +574,27 @@ ColorScheme _scaleToColorScheme(Brightness brightness, ScaleScheme scale) =>
Colors.red, // scale.primaryScale.hoverElementBackground, Colors.red, // scale.primaryScale.hoverElementBackground,
onPrimaryContainer: Colors.green, //scale.primaryScale.subtleText, onPrimaryContainer: Colors.green, //scale.primaryScale.subtleText,
secondary: scale.secondaryScale.background, secondary: scale.secondaryScale.background,
onSecondary: scale.secondaryScale.appText, onSecondary: scale.secondaryScale.foregroundText,
secondaryContainer: scale.secondaryScale.hoverElementBackground, secondaryContainer: scale.secondaryScale.hoverElementBackground,
onSecondaryContainer: scale.secondaryScale.subtleText, onSecondaryContainer: scale.secondaryScale.subtleText,
tertiary: scale.tertiaryScale.background, tertiary: scale.tertiaryScale.background,
onTertiary: scale.tertiaryScale.appText, onTertiary: scale.tertiaryScale.foregroundText,
tertiaryContainer: scale.tertiaryScale.hoverElementBackground, tertiaryContainer: scale.tertiaryScale.hoverElementBackground,
onTertiaryContainer: scale.tertiaryScale.subtleText, onTertiaryContainer: scale.tertiaryScale.subtleText,
error: scale.errorScale.background, error: scale.errorScale.background,
onError: scale.errorScale.appText, onError: scale.errorScale.foregroundText,
errorContainer: scale.errorScale.hoverElementBackground, errorContainer: scale.errorScale.hoverElementBackground,
onErrorContainer: scale.errorScale.subtleText, onErrorContainer: scale.errorScale.subtleText,
background: scale.grayScale.appBackground, // reviewed background: scale.grayScale.appBackground, // reviewed
onBackground: scale.grayScale.appText, // reviewed onBackground: scale.grayScale.appText, // reviewed
surface: scale.primaryScale.activeElementBackground, // reviewed surface: scale.primaryScale.background, // reviewed
onSurface: scale.primaryScale.subtleText, // reviewed onSurface: scale.primaryScale.foregroundText, // reviewed
surfaceVariant: scale.primaryScale.elementBackground, surfaceVariant: scale.primaryScale.elementBackground,
onSurfaceVariant: scale.primaryScale.subtleText, // ?? reviewed a little onSurfaceVariant:
scale.primaryScale.foregroundText, // ?? reviewed a little
outline: scale.primaryScale.border, outline: scale.primaryScale.border,
outlineVariant: scale.primaryScale.subtleBorder, outlineVariant: scale.primaryScale.subtleBorder,
shadow: RadixColors.dark.gray.step1, shadow: const Color(0xFF000000),
scrim: scale.primaryScale.background, scrim: scale.primaryScale.background,
inverseSurface: scale.primaryScale.subtleText, inverseSurface: scale.primaryScale.subtleText,
onInverseSurface: scale.primaryScale.subtleBackground, onInverseSurface: scale.primaryScale.subtleBackground,
@ -612,7 +616,7 @@ ChatTheme makeChatTheme(ScaleScheme scale, TextTheme textTheme) =>
contentPadding: const EdgeInsets.fromLTRB(8, 12, 8, 12), contentPadding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
border: const OutlineInputBorder( border: const OutlineInputBorder(
borderSide: BorderSide.none, borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(16))), borderRadius: BorderRadius.all(Radius.circular(8))),
), ),
inputContainerDecoration: inputContainerDecoration:
BoxDecoration(color: scale.primaryScale.border), BoxDecoration(color: scale.primaryScale.border),
@ -641,10 +645,39 @@ ChatTheme makeChatTheme(ScaleScheme scale, TextTheme textTheme) =>
fontSize: 64, fontSize: 64,
)); ));
ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) { TextTheme _makeTextTheme(Brightness brightness) {
final textTheme = (brightness == Brightness.light) late final TextTheme textTheme;
if (Platform.isIOS) {
textTheme = (brightness == Brightness.light)
? Typography.blackCupertino ? Typography.blackCupertino
: Typography.whiteCupertino; : Typography.whiteCupertino;
} else if (Platform.isMacOS) {
textTheme = (brightness == Brightness.light)
? Typography.blackRedwoodCity
: Typography.whiteRedwoodCity;
} else if (Platform.isAndroid || Platform.isFuchsia) {
textTheme = (brightness == Brightness.light)
? Typography.blackMountainView
: Typography.whiteMountainView;
} else if (Platform.isLinux) {
textTheme = (brightness == Brightness.light)
? Typography.blackHelsinki
: Typography.whiteHelsinki;
} else if (Platform.isWindows) {
textTheme = (brightness == Brightness.light)
? Typography.blackRedmond
: Typography.whiteRedmond;
} else {
log.warning('unknown platform');
textTheme = (brightness == Brightness.light)
? Typography.blackHelsinki
: Typography.whiteHelsinki;
}
return textTheme;
}
ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) {
final textTheme = _makeTextTheme(brightness);
final radix = _radixScheme(brightness, themeColor); final radix = _radixScheme(brightness, themeColor);
final scaleScheme = radix.toScale(); final scaleScheme = radix.toScale();
final colorScheme = _scaleToColorScheme(brightness, scaleScheme); final colorScheme = _scaleToColorScheme(brightness, scaleScheme);
@ -655,8 +688,42 @@ ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) {
bottomSheetTheme: themeData.bottomSheetTheme.copyWith( bottomSheetTheme: themeData.bottomSheetTheme.copyWith(
elevation: 0, elevation: 0,
modalElevation: 0, modalElevation: 0,
shape: shape: const RoundedRectangleBorder(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))), borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16)))),
canvasColor: scaleScheme.primaryScale.subtleBackground,
chipTheme: themeData.chipTheme.copyWith(
backgroundColor: scaleScheme.primaryScale.elementBackground,
selectedColor: scaleScheme.primaryScale.activeElementBackground,
surfaceTintColor: scaleScheme.primaryScale.hoverElementBackground,
checkmarkColor: scaleScheme.primaryScale.background,
side: BorderSide(color: scaleScheme.primaryScale.border)),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: scaleScheme.primaryScale.elementBackground,
foregroundColor: scaleScheme.primaryScale.appText,
disabledBackgroundColor: scaleScheme.grayScale.elementBackground,
disabledForegroundColor: scaleScheme.grayScale.appText,
shape: RoundedRectangleBorder(
side: BorderSide(color: scaleScheme.primaryScale.border),
borderRadius: BorderRadius.circular(8))),
),
focusColor: scaleScheme.primaryScale.activeElementBackground,
hoverColor: scaleScheme.primaryScale.hoverElementBackground,
inputDecorationTheme: themeData.inputDecorationTheme.copyWith(
border: OutlineInputBorder(
borderSide: BorderSide(color: scaleScheme.primaryScale.border),
borderRadius: BorderRadius.circular(8)),
contentPadding: const EdgeInsets.all(8),
labelStyle: TextStyle(
color: scaleScheme.primaryScale.subtleText.withAlpha(127)),
floatingLabelStyle:
TextStyle(color: scaleScheme.primaryScale.subtleText),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: scaleScheme.primaryScale.hoverBorder, width: 2),
borderRadius: BorderRadius.circular(8))),
extensions: <ThemeExtension<dynamic>>[ extensions: <ThemeExtension<dynamic>>[
scaleScheme, scaleScheme,
]); ]);

View File

@ -22,7 +22,7 @@ List<DropdownMenuItem<dynamic>> _getBrightnessDropdownItems() {
} }
Widget buildSettingsPageBrightnessPreferences( Widget buildSettingsPageBrightnessPreferences(
{required void Function() onChanged}) { {required BuildContext context, required void Function() onChanged}) {
final preferencesRepository = PreferencesRepository.instance; final preferencesRepository = PreferencesRepository.instance;
final themePreferences = preferencesRepository.value.themePreferences; final themePreferences = preferencesRepository.value.themePreferences;
return ThemeSwitcher.withTheme( return ThemeSwitcher.withTheme(

View File

@ -30,7 +30,8 @@ List<DropdownMenuItem<dynamic>> _getThemeDropdownItems() {
.toList(); .toList();
} }
Widget buildSettingsPageColorPreferences({required void Function() onChanged}) { Widget buildSettingsPageColorPreferences(
{required BuildContext context, required void Function() onChanged}) {
final preferencesRepository = PreferencesRepository.instance; final preferencesRepository = PreferencesRepository.instance;
final themePreferences = preferencesRepository.value.themePreferences; final themePreferences = preferencesRepository.value.themePreferences;
return ThemeSwitcher.withTheme( return ThemeSwitcher.withTheme(

View File

@ -1,24 +1,17 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:veilid_support/veilid_support.dart'; import 'package:veilid_support/veilid_support.dart';
import 'veilid_processor/veilid_processor.dart'; import 'veilid_processor/veilid_processor.dart';
class BackgroundTicker extends StatefulWidget { class BackgroundTicker extends StatefulWidget {
const BackgroundTicker({required this.builder, super.key}); const BackgroundTicker({required this.child, super.key});
final Widget Function(BuildContext) builder; final Widget child;
@override @override
BackgroundTickerState createState() => BackgroundTickerState(); BackgroundTickerState createState() => BackgroundTickerState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(ObjectFlagProperty<Widget Function(BuildContext p1)>.has(
'builder', builder));
}
} }
class BackgroundTickerState extends State<BackgroundTicker> { class BackgroundTickerState extends State<BackgroundTicker> {
@ -48,7 +41,7 @@ class BackgroundTickerState extends State<BackgroundTicker> {
@override @override
// ignore: prefer_expression_function_bodies // ignore: prefer_expression_function_bodies
Widget build(BuildContext context) { Widget build(BuildContext context) {
return widget.builder(context); return widget.child;
} }
Future<void> _onTick() async { Future<void> _onTick() async {

View File

@ -154,7 +154,7 @@ Widget styledTitleContainer({
title, title,
style: textTheme.titleMedium! style: textTheme.titleMedium!
.copyWith(color: scale.primaryScale.subtleText), .copyWith(color: scale.primaryScale.subtleText),
).paddingLTRB(8, 8, 8, 8), ).paddingLTRB(8, 8, 8, 4),
DecoratedBox( DecoratedBox(
decoration: ShapeDecoration( decoration: ShapeDecoration(
color: color:
@ -168,6 +168,43 @@ Widget styledTitleContainer({
])); ]));
} }
Widget styledBottomSheet({
required BuildContext context,
required String title,
required Widget child,
Color? borderColor,
Color? backgroundColor,
}) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final textTheme = theme.textTheme;
return DecoratedBox(
decoration: ShapeDecoration(
color: borderColor ?? scale.primaryScale.border,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16)))),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Text(
title,
style: textTheme.titleMedium!
.copyWith(color: scale.primaryScale.subtleText),
).paddingLTRB(8, 8, 8, 4),
DecoratedBox(
decoration: ShapeDecoration(
color:
backgroundColor ?? scale.primaryScale.subtleBackground,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16)))),
child: child)
.paddingLTRB(4, 4, 4, 0)
]));
}
bool get isPlatformDark => bool get isPlatformDark =>
WidgetsBinding.instance.platformDispatcher.platformBrightness == WidgetsBinding.instance.platformDispatcher.platformBrightness ==
Brightness.dark; Brightness.dark;

View File

@ -80,6 +80,18 @@ class _DeveloperPageState extends State<DeveloperPage> {
return; return;
} }
if (debugCommand.startsWith('change_log_ignore ')) {
final args = debugCommand.split(' ');
if (args.length < 3) {
_debugOut('Incorrect number of arguments');
return;
}
final layer = args[1];
final changes = args[2].split(',');
Veilid.instance.changeLogIgnore(layer, changes);
return;
}
if (debugCommand == 'ellet') { if (debugCommand == 'ellet') {
setState(() { setState(() {
_showEllet = !_showEllet; _showEllet = !_showEllet;

View File

@ -1,6 +1,7 @@
import 'package:veilid/veilid.dart';
import 'dart:io' show Platform; import 'dart:io' show Platform;
import 'package:veilid/veilid.dart';
Map<String, dynamic> getDefaultVeilidPlatformConfig( Map<String, dynamic> getDefaultVeilidPlatformConfig(
bool isWeb, String appName) { bool isWeb, String appName) {
final ignoreLogTargetsStr = final ignoreLogTargetsStr =