mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-09-20 12:34:49 -04:00
profile edit happens without requiring save button
This commit is contained in:
parent
b6a812af87
commit
030f9d9651
19 changed files with 499 additions and 266 deletions
|
@ -1,3 +1,4 @@
|
|||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
|
@ -5,21 +6,27 @@ import 'package:flutter_translate/flutter_translate.dart';
|
|||
import '../../proto/proto.dart' as proto;
|
||||
|
||||
class AvailabilityWidget extends StatelessWidget {
|
||||
const AvailabilityWidget({required this.availability, super.key});
|
||||
const AvailabilityWidget(
|
||||
{required this.availability,
|
||||
this.vertical = true,
|
||||
this.iconSize = 32,
|
||||
super.key});
|
||||
|
||||
static IconData availabilityIcon(proto.Availability availability) {
|
||||
late final IconData iconData;
|
||||
static Widget availabilityIcon(proto.Availability availability,
|
||||
{double size = 32}) {
|
||||
late final Widget iconData;
|
||||
switch (availability) {
|
||||
case proto.Availability.AVAILABILITY_AWAY:
|
||||
iconData = Icons.hot_tub;
|
||||
iconData =
|
||||
ImageIcon(const AssetImage('assets/images/toilet.png'), size: size);
|
||||
case proto.Availability.AVAILABILITY_BUSY:
|
||||
iconData = Icons.event_busy;
|
||||
iconData = Icon(Icons.event_busy, size: size);
|
||||
case proto.Availability.AVAILABILITY_FREE:
|
||||
iconData = Icons.event_available;
|
||||
iconData = Icon(Icons.event_available, size: size);
|
||||
case proto.Availability.AVAILABILITY_OFFLINE:
|
||||
iconData = Icons.cloud_off;
|
||||
iconData = Icon(Icons.cloud_off, size: size);
|
||||
case proto.Availability.AVAILABILITY_UNSPECIFIED:
|
||||
iconData = Icons.question_mark;
|
||||
iconData = Icon(Icons.question_mark, size: size);
|
||||
}
|
||||
return iconData;
|
||||
}
|
||||
|
@ -49,20 +56,35 @@ class AvailabilityWidget extends StatelessWidget {
|
|||
// final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
final name = availabilityName(availability);
|
||||
final iconData = availabilityIcon(availability);
|
||||
final icon = availabilityIcon(availability, size: iconSize);
|
||||
|
||||
return Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
Icon(iconData, size: 32),
|
||||
Text(name, style: textTheme.labelSmall)
|
||||
]);
|
||||
return vertical
|
||||
? Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
//mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
icon,
|
||||
Text(name, style: textTheme.labelSmall).paddingLTRB(0, 0, 0, 0)
|
||||
])
|
||||
: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
icon,
|
||||
Text(name, style: textTheme.labelSmall).paddingLTRB(8, 0, 0, 0)
|
||||
]);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
final proto.Availability availability;
|
||||
final bool vertical;
|
||||
final double iconSize;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(
|
||||
DiagnosticsProperty<proto.Availability>('availability', availability));
|
||||
properties
|
||||
..add(
|
||||
DiagnosticsProperty<proto.Availability>('availability', availability))
|
||||
..add(DiagnosticsProperty<bool>('vertical', vertical))
|
||||
..add(DoubleProperty('iconSize', iconSize));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,24 +28,28 @@ class ContactItemWidget extends StatelessWidget {
|
|||
Widget build(
|
||||
BuildContext context,
|
||||
) {
|
||||
late final String title;
|
||||
late final String subtitle;
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
if (_contact.nickname.isNotEmpty) {
|
||||
title = _contact.nickname;
|
||||
if (_contact.profile.pronouns.isNotEmpty) {
|
||||
subtitle = '${_contact.profile.name} (${_contact.profile.pronouns})';
|
||||
} else {
|
||||
subtitle = _contact.profile.name;
|
||||
}
|
||||
} else {
|
||||
title = _contact.profile.name;
|
||||
if (_contact.profile.pronouns.isNotEmpty) {
|
||||
subtitle = '(${_contact.profile.pronouns})';
|
||||
} else {
|
||||
subtitle = '';
|
||||
}
|
||||
}
|
||||
final name = _contact.nameOrNickname;
|
||||
final title = _contact.displayName;
|
||||
final subtitle = _contact.profile.status;
|
||||
|
||||
final avatar = AvatarWidget(
|
||||
name: name,
|
||||
size: 34,
|
||||
borderColor: _disabled
|
||||
? scale.grayScale.primaryText
|
||||
: scale.primaryScale.primaryText,
|
||||
foregroundColor: _disabled
|
||||
? scale.grayScale.primaryText
|
||||
: scale.primaryScale.primaryText,
|
||||
backgroundColor:
|
||||
_disabled ? scale.grayScale.primary : scale.primaryScale.primary,
|
||||
scaleConfig: scaleConfig,
|
||||
textStyle: theme.textTheme.titleLarge!,
|
||||
);
|
||||
|
||||
return SliderTile(
|
||||
key: ObjectKey(_contact),
|
||||
|
@ -54,7 +58,7 @@ class ContactItemWidget extends StatelessWidget {
|
|||
tileScale: ScaleKind.primary,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
icon: Icons.person,
|
||||
leading: avatar,
|
||||
onDoubleTap: _onDoubleTap == null
|
||||
? null
|
||||
: () => singleFuture<void>((this, _kOnTap), () async {
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:searchable_listview/searchable_listview.dart';
|
||||
import 'package:star_menu/star_menu.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../chat_list/chat_list.dart';
|
||||
|
@ -71,6 +72,75 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
|||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
final menuIconColor = scaleConfig.preferBorders
|
||||
? scale.primaryScale.hoverBorder
|
||||
: scale.primaryScale.borderText;
|
||||
final menuBackgroundColor = scaleConfig.preferBorders
|
||||
? scale.primaryScale.elementBackground
|
||||
: scale.primaryScale.border;
|
||||
// final menuHoverColor = scaleConfig.preferBorders
|
||||
// ? scale.primaryScale.hoverElementBackground
|
||||
// : scale.primaryScale.hoverBorder;
|
||||
|
||||
final menuBorderColor = scale.primaryScale.hoverBorder;
|
||||
|
||||
final menuParams = StarMenuParameters(
|
||||
shape: MenuShape.grid,
|
||||
checkItemsScreenBoundaries: true,
|
||||
centerOffset: const Offset(0, 64),
|
||||
backgroundParams:
|
||||
BackgroundParams(backgroundColor: theme.shadowColor.withAlpha(128)),
|
||||
boundaryBackground: BoundaryBackground(
|
||||
color: menuBackgroundColor,
|
||||
decoration: ShapeDecoration(
|
||||
color: menuBackgroundColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: scaleConfig.useVisualIndicators
|
||||
? BorderSide(
|
||||
width: 2, color: menuBorderColor, strokeAlign: 0)
|
||||
: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(
|
||||
8 * scaleConfig.borderRadiusScale)))));
|
||||
|
||||
final receiveInviteMenuItems = [
|
||||
Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
_receiveInviteMenuController.closeMenu!();
|
||||
await ScanInvitationDialog.show(context);
|
||||
},
|
||||
iconSize: 32,
|
||||
icon: Icon(
|
||||
Icons.qr_code_scanner,
|
||||
size: 32,
|
||||
color: menuIconColor,
|
||||
),
|
||||
),
|
||||
Text(translate('add_contact_sheet.scan_invite'),
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.labelSmall!.copyWith(color: menuIconColor))
|
||||
]).paddingAll(4),
|
||||
Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
_receiveInviteMenuController.closeMenu!();
|
||||
await PasteInvitationDialog.show(context);
|
||||
},
|
||||
iconSize: 32,
|
||||
icon: Icon(
|
||||
Icons.paste,
|
||||
size: 32,
|
||||
color: menuIconColor,
|
||||
),
|
||||
),
|
||||
Text(translate('add_contact_sheet.paste_invite'),
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.labelSmall!.copyWith(color: menuIconColor))
|
||||
]).paddingAll(4)
|
||||
];
|
||||
|
||||
return Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
||||
Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
IconButton(
|
||||
|
@ -80,30 +150,36 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
|||
iconSize: 32,
|
||||
icon: const Icon(Icons.contact_page),
|
||||
color: scale.primaryScale.hoverBorder,
|
||||
tooltip: translate('add_contact_sheet.create_invite'),
|
||||
)
|
||||
]),
|
||||
Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await ScanInvitationDialog.show(context);
|
||||
},
|
||||
iconSize: 32,
|
||||
icon: const Icon(Icons.qr_code_scanner),
|
||||
color: scale.primaryScale.hoverBorder,
|
||||
tooltip: translate('add_contact_sheet.scan_invite')),
|
||||
]),
|
||||
Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await PasteInvitationDialog.show(context);
|
||||
},
|
||||
iconSize: 32,
|
||||
icon: const Icon(Icons.paste),
|
||||
color: scale.primaryScale.hoverBorder,
|
||||
tooltip: translate('add_contact_sheet.paste_invite'),
|
||||
),
|
||||
])
|
||||
Text(translate('add_contact_sheet.create_invite'),
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.labelSmall!
|
||||
.copyWith(color: scale.primaryScale.hoverBorder))
|
||||
]),
|
||||
StarMenu(
|
||||
items: receiveInviteMenuItems,
|
||||
onItemTapped: (_index, controller) {
|
||||
controller.closeMenu!();
|
||||
},
|
||||
controller: _receiveInviteMenuController,
|
||||
params: menuParams,
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
iconSize: 32,
|
||||
icon: ImageIcon(
|
||||
const AssetImage('assets/images/handshake.png'),
|
||||
size: 32,
|
||||
color: scale.primaryScale.hoverBorder,
|
||||
)),
|
||||
Text(translate('add_contact_sheet.receive_invite'),
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.labelSmall!
|
||||
.copyWith(color: scale.primaryScale.hoverBorder))
|
||||
]),
|
||||
),
|
||||
]).paddingAll(16);
|
||||
}
|
||||
|
||||
|
@ -112,7 +188,7 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
|||
final theme = Theme.of(context);
|
||||
final textTheme = theme.textTheme;
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
//final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
final cilState = context.watch<ContactInvitationListCubit>().state;
|
||||
final cilBusy = cilState.busy;
|
||||
|
@ -244,4 +320,7 @@ class _ContactsBrowserState extends State<ContactsBrowser>
|
|||
await chatListCubit.deleteChat(
|
||||
localConversationRecordKey: localConversationRecordKey);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
final _receiveInviteMenuController = StarMenuController();
|
||||
}
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../chat/chat.dart';
|
||||
import '../../chat_list/chat_list.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../contact_invitation/contact_invitation.dart';
|
||||
import '../../layout/layout.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../theme/theme.dart';
|
||||
import '../../veilid_processor/veilid_processor.dart';
|
||||
import '../contacts.dart';
|
||||
|
||||
class ContactsDialog extends StatefulWidget {
|
||||
|
@ -48,9 +44,9 @@ class _ContactsDialogState extends State<ContactsDialog> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final textTheme = theme.textTheme;
|
||||
// final textTheme = theme.textTheme;
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
// final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
final enableSplit = !isMobileWidth(context);
|
||||
final enableLeft = enableSplit || _selectedContact == null;
|
||||
|
@ -105,7 +101,7 @@ class _ContactsDialogState extends State<ContactsDialog> {
|
|||
.toVeilid(),
|
||||
onContactSelected: onContactSelected,
|
||||
onChatStarted: onChatStarted,
|
||||
).paddingAll(8)))),
|
||||
).paddingLTRB(8, 0, 8, 8)))),
|
||||
if (enableRight)
|
||||
if (_selectedContact == null)
|
||||
const NoContactWidget().expanded()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue