From d3e7a63f38e626a11e48fda2d8179e1acc9fcbf5 Mon Sep 17 00:00:00 2001 From: otsmr Date: Tue, 29 Jul 2025 11:18:27 +0200 Subject: [PATCH] add default avatar --- assets/images/default_avatar.svg | 27 +++ lib/src/database/daos/contacts_dao.dart | 9 - lib/src/database/daos/messages_dao.dart | 4 +- lib/src/model/json/message.dart | 2 +- lib/src/views/chats/chat_list.view.dart | 23 ++- .../in_chat_media_viewer.dart | 2 +- .../message_send_state_icon.dart | 0 lib/src/views/components/initialsavatar.dart | 168 +++--------------- 8 files changed, 80 insertions(+), 155 deletions(-) create mode 100644 assets/images/default_avatar.svg rename lib/src/views/{components => chats/chat_messages_components}/message_send_state_icon.dart (100%) diff --git a/assets/images/default_avatar.svg b/assets/images/default_avatar.svg new file mode 100644 index 0000000..26d14d5 --- /dev/null +++ b/assets/images/default_avatar.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/src/database/daos/contacts_dao.dart b/lib/src/database/daos/contacts_dao.dart index f1a4123..f889628 100644 --- a/lib/src/database/daos/contacts_dao.dart +++ b/lib/src/database/daos/contacts_dao.dart @@ -115,15 +115,6 @@ class ContactsDao extends DatabaseAccessor } } - Future newMessageExchange(int userId) { - return updateContact( - userId, - ContactsCompanion( - lastMessageExchange: Value(DateTime.now()), - ), - ); - } - Stream> watchNotAcceptedContacts() { return (select(contacts) ..where((t) => diff --git a/lib/src/database/daos/messages_dao.dart b/lib/src/database/daos/messages_dao.dart index 1c9e333..95891f4 100644 --- a/lib/src/database/daos/messages_dao.dart +++ b/lib/src/database/daos/messages_dao.dart @@ -193,7 +193,9 @@ class MessagesDao extends DatabaseAccessor Future insertMessage(MessagesCompanion message) async { try { await (update(contacts) - ..where((c) => c.userId.equals(message.contactId.value))) + ..where( + (c) => c.userId.equals(message.contactId.value), + )) .write(ContactsCompanion(lastMessageExchange: Value(DateTime.now()))); return await into(messages).insert(message); diff --git a/lib/src/model/json/message.dart b/lib/src/model/json/message.dart index deeea6c..be48336 100644 --- a/lib/src/model/json/message.dart +++ b/lib/src/model/json/message.dart @@ -15,7 +15,7 @@ Color getMessageColorFromType(MessageContent content, BuildContext context) { color = context.color.primary; } else { if (content.isVideo) { - color = const Color.fromARGB(255, 240, 243, 33); + color = const Color.fromARGB(255, 243, 33, 208); } else { color = Colors.redAccent; } diff --git a/lib/src/views/chats/chat_list.view.dart b/lib/src/views/chats/chat_list.view.dart index 0b9b070..4dab012 100644 --- a/lib/src/views/chats/chat_list.view.dart +++ b/lib/src/views/chats/chat_list.view.dart @@ -10,6 +10,7 @@ import 'package:twonly/globals.dart'; import 'package:twonly/src/database/daos/contacts_dao.dart'; import 'package:twonly/src/database/tables/messages_table.dart'; import 'package:twonly/src/database/twonly_database.dart'; +import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/providers/connection.provider.dart'; import 'package:twonly/src/services/api/media_download.dart'; import 'package:twonly/src/utils/misc.dart'; @@ -21,14 +22,15 @@ import 'package:twonly/src/views/chats/chat_list_components/connection_info.comp import 'package:twonly/src/views/chats/chat_list_components/feedback_btn.dart'; import 'package:twonly/src/views/chats/chat_list_components/last_message_time.dart'; import 'package:twonly/src/views/chats/chat_messages.view.dart'; +import 'package:twonly/src/views/chats/chat_messages_components/message_send_state_icon.dart'; import 'package:twonly/src/views/chats/media_viewer.view.dart'; import 'package:twonly/src/views/chats/start_new_chat.view.dart'; import 'package:twonly/src/views/components/flame.dart'; import 'package:twonly/src/views/components/initialsavatar.dart'; -import 'package:twonly/src/views/components/message_send_state_icon.dart'; import 'package:twonly/src/views/components/notification_badge.dart'; import 'package:twonly/src/views/components/user_context_menu.dart'; import 'package:twonly/src/views/settings/help/changelog.view.dart'; +import 'package:twonly/src/views/settings/profile/profile.view.dart'; import 'package:twonly/src/views/settings/settings_main.view.dart'; import 'package:twonly/src/views/settings/subscription/subscription.view.dart'; import 'package:twonly/src/views/tutorial/tutorials.dart'; @@ -43,6 +45,7 @@ class _ChatListViewState extends State { late StreamSubscription> _contactsSub; List _contacts = []; List _pinnedContacts = []; + UserData? _user; GlobalKey firstUserListItemKey = GlobalKey(); GlobalKey searchForOtherUsers = GlobalKey(); @@ -76,6 +79,7 @@ class _ChatListViewState extends State { final user = await getUser(); if (user == null) return; + _user = user; final changeLog = await rootBundle.loadString('CHANGELOG.md'); final changeLogHash = (await compute(Sha256().hash, changeLog.codeUnits)).bytes; @@ -112,6 +116,23 @@ class _ChatListViewState extends State { return Scaffold( appBar: AppBar( title: Row(children: [ + GestureDetector( + onTap: () async { + await Navigator.push(context, + MaterialPageRoute(builder: (context) { + return const ProfileView(); + })); + _user = await getUser(); + if (!mounted) return; + setState(() {}); + }, + child: ContactAvatar( + userData: _user, + fontSize: 14, + color: context.color.onSurface.withAlpha(20), + ), + ), + const SizedBox(width: 10), const Text('twonly '), if (planId != 'Free') GestureDetector( diff --git a/lib/src/views/chats/chat_messages_components/in_chat_media_viewer.dart b/lib/src/views/chats/chat_messages_components/in_chat_media_viewer.dart index 77fed7d..1a130c2 100644 --- a/lib/src/views/chats/chat_messages_components/in_chat_media_viewer.dart +++ b/lib/src/views/chats/chat_messages_components/in_chat_media_viewer.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/src/database/twonly_database.dart'; import 'package:twonly/src/model/memory_item.model.dart'; -import 'package:twonly/src/views/components/message_send_state_icon.dart'; +import 'package:twonly/src/views/chats/chat_messages_components/message_send_state_icon.dart'; import 'package:twonly/src/views/memories/memories_item_thumbnail.dart'; import 'package:twonly/src/views/memories/memories_photo_slider.view.dart'; diff --git a/lib/src/views/components/message_send_state_icon.dart b/lib/src/views/chats/chat_messages_components/message_send_state_icon.dart similarity index 100% rename from lib/src/views/components/message_send_state_icon.dart rename to lib/src/views/chats/chat_messages_components/message_send_state_icon.dart diff --git a/lib/src/views/components/initialsavatar.dart b/lib/src/views/components/initialsavatar.dart index b5fe029..59068db 100644 --- a/lib/src/views/components/initialsavatar.dart +++ b/lib/src/views/components/initialsavatar.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:twonly/src/database/daos/contacts_dao.dart'; import 'package:twonly/src/database/twonly_database.dart'; import 'package:twonly/src/model/json/userdata.dart'; import 'package:twonly/src/utils/log.dart'; @@ -11,21 +10,20 @@ class ContactAvatar extends StatelessWidget { this.contact, this.userData, this.fontSize = 20, + this.color, }); final Contact? contact; final UserData? userData; final double? fontSize; + final Color? color; @override Widget build(BuildContext context) { - var displayName = ''; String? avatarSvg; if (contact != null) { - displayName = getContactDisplayName(contact!).replaceAll('\u0336', ''); avatarSvg = contact!.avatarSvg; } else if (userData != null) { - displayName = userData!.displayName; avatarSvg = userData!.avatarSvg; } else { return Container(); @@ -33,148 +31,34 @@ class ContactAvatar extends StatelessWidget { final proSize = (fontSize == null) ? 40 : (fontSize! * 2); - if (avatarSvg != null) { - return Container( - constraints: BoxConstraints( - minHeight: 2 * (fontSize ?? 20), - minWidth: 2 * (fontSize ?? 20), - maxWidth: 2 * (fontSize ?? 20), - maxHeight: 2 * (fontSize ?? 20), - ), - child: Center( - child: ClipRRect( - borderRadius: BorderRadius.circular(12), - child: SizedBox( - height: proSize as double, - width: proSize, - child: Center( - child: SvgPicture.string( - avatarSvg, - errorBuilder: (context, error, stackTrace) { - Log.error('$error'); - return Container(); - }, - ), - ), + return Container( + constraints: BoxConstraints( + minHeight: 2 * (fontSize ?? 20), + minWidth: 2 * (fontSize ?? 20), + maxWidth: 2 * (fontSize ?? 20), + maxHeight: 2 * (fontSize ?? 20), + ), + child: Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Container( + height: proSize as double, + width: proSize, + color: color, + child: Center( + child: avatarSvg == null + ? SvgPicture.asset('assets/images/default_avatar.svg') + : SvgPicture.string( + avatarSvg, + errorBuilder: (context, error, stackTrace) { + Log.error('$error'); + return Container(); + }, + ), ), ), ), - ); - } - - // Extract initials from the displayName - final nameParts = displayName.split(' '); - var initials = nameParts.map((part) => part[0]).join().toUpperCase(); - - if (initials.length > 2) { - initials = initials[0] + initials[1]; - } else if (initials.length == 1) { - initials = displayName[0] + displayName[1]; - } - - initials = initials.toUpperCase(); - - // Generate a color based on the initials (you can customize this logic) - final avatarColor = _getColorFromUsername( - displayName, Theme.of(context).brightness == Brightness.dark); - - final Widget child = Text( - initials, - style: TextStyle( - color: _getTextColor(avatarColor), - fontWeight: FontWeight.normal, - fontSize: fontSize, ), ); - - final isPro = initials[0] == 'T'; - - if (isPro) { - return Container( - constraints: BoxConstraints( - minHeight: 2 * (fontSize ?? 20), - minWidth: 2 * (fontSize ?? 20), - maxWidth: 2 * (fontSize ?? 20), - maxHeight: 2 * (fontSize ?? 20), - ), - child: Center( - child: ClipRRect( - borderRadius: BorderRadius.circular(12), - child: Container( - height: proSize as double, - width: proSize, - //padding: EdgeInsets.symmetric(vertical: 5, horizontal: 10), - color: avatarColor, - child: Center(child: child), - ), - ), - ), - ); - } else { - return CircleAvatar( - backgroundColor: avatarColor, - radius: fontSize, - child: child, - ); - } - } - - Color _getTextColor(Color color) { - const value = 100.0; - // Ensure the value does not exceed the RGB limits - final newRed = ((color.r * 255) - value).clamp(0, 255).round(); - final newGreen = (color.g * 255 - value).clamp(0, 255).round(); - final newBlue = (color.b * 255 - value).clamp(0, 255).round(); - - return Color.fromARGB((color.a * 255).round(), newRed, newGreen, newBlue); - } - - Color _getColorFromUsername(String displayName, bool isDarkMode) { - // Define color lists for light and dark themes - final lightColors = [ - Colors.red, - Colors.green, - Colors.blue, - Colors.orange, - Colors.purple, - Colors.teal, - Colors.amber, - Colors.indigo, - Colors.cyan, - Colors.lime, - Colors.pink, - Colors.brown, - Colors.grey, - ]; - - final darkColors = [ - const Color.fromARGB(255, 246, 227, 254), // Light Lavender - const Color.fromARGB(255, 246, 216, 215), // Light Pink - const Color.fromARGB(255, 226, 236, 235), // Light Teal - const Color.fromARGB(255, 255, 224, 178), // Light Yellow - const Color.fromARGB(255, 255, 182, 193), // Light Pink (Hot Pink) - const Color.fromARGB(255, 173, 216, 230), // Light Blue - const Color.fromARGB(255, 221, 160, 221), // Plum - const Color.fromARGB(255, 255, 228, 196), // Bisque - const Color.fromARGB(255, 240, 230, 140), // Khaki - const Color.fromARGB(255, 255, 192, 203), // Pink - const Color.fromARGB(255, 255, 218, 185), // Peach Puff - const Color.fromARGB(255, 255, 160, 122), // Light Salmon - const Color.fromARGB(255, 135, 206, 250), // Light Sky Blue - const Color.fromARGB(255, 255, 228, 225), // Misty Rose - const Color.fromARGB(255, 240, 248, 255), // Alice Blue - const Color.fromARGB(255, 255, 250, 205), // Lemon Chiffon - const Color.fromARGB(255, 255, 218, 185), // Peach Puff - ]; - - // Simple logic to generate a hash from initials - final hash = - displayName.codeUnits.fold(0, (prev, element) => prev + element); - - // Select the appropriate color list based on the current theme brightness - final colors = isDarkMode ? darkColors : lightColors; - - // Use the hash to select a color from the list - return colors[hash % colors.length]; } }