feat: Implement About Us and Agent Home screens, update routing, and add new assets and plugins.
This commit is contained in:
@@ -63,15 +63,15 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
// -- Header with back arrow, search, icons --
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 12),
|
||||
child: Container(
|
||||
height: 51,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: AppColors.primaryDark,
|
||||
width: 0.5,
|
||||
color: AppColors.primaryDark.withValues(alpha: 0.2),
|
||||
width: 1,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -88,11 +88,11 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
}
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 14),
|
||||
child: Icon(
|
||||
Icons.arrow_back_ios_new,
|
||||
size: 17,
|
||||
size: 16,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
),
|
||||
@@ -106,7 +106,7 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
decoration: const InputDecoration(
|
||||
@@ -114,7 +114,7 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
hintStyle: TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.hintText,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
@@ -129,7 +129,7 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
style: TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
),
|
||||
@@ -139,11 +139,11 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => _isSearchActive = true),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Icon(
|
||||
Icons.search,
|
||||
size: 20,
|
||||
size: 22,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
),
|
||||
@@ -152,11 +152,11 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
GestureDetector(
|
||||
onTap: () {},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 12, left: 4),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.only(right: 14, left: 2),
|
||||
child: Icon(
|
||||
Icons.more_horiz,
|
||||
size: 20,
|
||||
size: 22,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
),
|
||||
@@ -173,7 +173,7 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
baseColor: const Color(0xFFE8E8E8),
|
||||
highlightColor: const Color(0xFFF5F5F5),
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: 6,
|
||||
itemBuilder: (_, index) => Padding(
|
||||
@@ -181,14 +181,14 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 52,
|
||||
height: 52,
|
||||
width: 56,
|
||||
height: 56,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -215,7 +215,7 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
width: 45,
|
||||
width: 55,
|
||||
height: 12,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
@@ -303,10 +303,13 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
child: ListView.separated(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: filtered.length,
|
||||
separatorBuilder: (_, __) => Divider(
|
||||
color: AppColors.primaryDark.withValues(alpha: 0.1),
|
||||
height: 0.5,
|
||||
thickness: 0.5,
|
||||
separatorBuilder: (context, index) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Divider(
|
||||
color: AppColors.primaryDark.withValues(alpha: 0.1),
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
return _ConversationTile(
|
||||
@@ -318,7 +321,6 @@ class _ConversationsScreenState extends ConsumerState<ConversationsScreen> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -- Conversation Tile Widget --
|
||||
@@ -350,6 +352,7 @@ class _ConversationTile extends StatelessWidget {
|
||||
final avatarUrl = _resolveAvatarUrl(otherParty.avatar);
|
||||
final lastMessage = conversation.lastMessageText ?? '';
|
||||
final timestamp = _formatTimestamp(conversation.lastMessageAt);
|
||||
final unread = conversation.unreadCount;
|
||||
final firstLetter = otherParty.name.isNotEmpty
|
||||
? otherParty.name[0].toUpperCase()
|
||||
: '?';
|
||||
@@ -357,32 +360,45 @@ class _ConversationTile extends StatelessWidget {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Avatar with online indicator
|
||||
Stack(
|
||||
children: [
|
||||
ClipOval(
|
||||
child: avatarUrl.isNotEmpty
|
||||
? CachedNetworkImage(
|
||||
imageUrl: avatarUrl,
|
||||
width: 52,
|
||||
height: 52,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (_, url) => _AvatarFallback(
|
||||
letter: firstLetter,
|
||||
),
|
||||
errorWidget: (_, url, err) => _AvatarFallback(
|
||||
letter: firstLetter,
|
||||
),
|
||||
)
|
||||
: _AvatarFallback(letter: firstLetter),
|
||||
Container(
|
||||
width: 56,
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: AppColors.primaryDark.withValues(alpha: 0.15),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: ClipOval(
|
||||
child: avatarUrl.isNotEmpty
|
||||
? CachedNetworkImage(
|
||||
imageUrl: avatarUrl,
|
||||
width: 56,
|
||||
height: 56,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (context, url) => _AvatarFallback(
|
||||
letter: firstLetter,
|
||||
),
|
||||
errorWidget: (context, url, err) =>
|
||||
_AvatarFallback(
|
||||
letter: firstLetter,
|
||||
),
|
||||
)
|
||||
: _AvatarFallback(letter: firstLetter),
|
||||
),
|
||||
),
|
||||
if (otherParty.isOnline)
|
||||
Positioned(
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
right: 1,
|
||||
bottom: 1,
|
||||
child: Container(
|
||||
width: 14,
|
||||
height: 14,
|
||||
@@ -398,31 +414,61 @@ class _ConversationTile extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
const SizedBox(width: 14),
|
||||
// Name and last message
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
otherParty.name,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
// Name row with optional unread badge
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
otherParty.name,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 14,
|
||||
fontWeight:
|
||||
unread > 0 ? FontWeight.w700 : FontWeight.w600,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (unread > 0) ...[
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 7, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.accentOrange,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
'$unread',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
lastMessage,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontFamily: 'SourceSerif4',
|
||||
fontSize: 14,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.primaryDark,
|
||||
color: unread > 0
|
||||
? AppColors.primaryDark
|
||||
: AppColors.hintText,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
@@ -430,15 +476,17 @@ class _ConversationTile extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const SizedBox(width: 10),
|
||||
// Timestamp
|
||||
Text(
|
||||
timestamp,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontFamily: 'SourceSerif4',
|
||||
fontSize: 14,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.primaryDark,
|
||||
color: unread > 0
|
||||
? AppColors.primaryDark
|
||||
: AppColors.hintText,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -458,8 +506,8 @@ class _AvatarFallback extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 52,
|
||||
height: 52,
|
||||
width: 56,
|
||||
height: 56,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFFE8E8E8),
|
||||
shape: BoxShape.circle,
|
||||
@@ -469,7 +517,7 @@ class _AvatarFallback extends StatelessWidget {
|
||||
letter,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Fractul',
|
||||
fontSize: 20,
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
@@ -497,7 +545,7 @@ String _formatTimestamp(String? isoTimestamp) {
|
||||
return 'Now';
|
||||
}
|
||||
|
||||
// Same day: show time like "2 hours Ago"
|
||||
// Same day: show relative time
|
||||
if (messageDate == today) {
|
||||
if (diff.inHours >= 1) {
|
||||
return '${diff.inHours} hour${diff.inHours > 1 ? 's' : ''} Ago';
|
||||
|
||||
Reference in New Issue
Block a user