From 40f120700cbc093379273e0cd306b77d4e389658 Mon Sep 17 00:00:00 2001 From: Joan Cabezas Date: Thu, 13 Jun 2024 14:30:57 -0700 Subject: [PATCH] improved chat page UI performance --- .../lib/backend/storage/message.dart | 8 ++- apps/AppWithWearable/lib/main.dart | 8 --- apps/AppWithWearable/lib/pages/chat/page.dart | 66 +++++++---------- .../lib/pages/memories/page.dart | 71 ++++++++++--------- 4 files changed, 70 insertions(+), 83 deletions(-) diff --git a/apps/AppWithWearable/lib/backend/storage/message.dart b/apps/AppWithWearable/lib/backend/storage/message.dart index f650c68f9..ae2ba4e5d 100644 --- a/apps/AppWithWearable/lib/backend/storage/message.dart +++ b/apps/AppWithWearable/lib/backend/storage/message.dart @@ -1,9 +1,15 @@ +// TODO: Migrate messages to object box +// 1. Create a new class Message (include memories Id maybe as oneToMany relationship) +// 2. Create messages provider +// 3. Consume stuff from messages provider +// 4. Make sure message creation includes memories properly in msg object + class Message { String id; DateTime? createdAt; String text; String type; - List? memoryIds; // Optional list of strings. + List? memoryIds; // Optional list of strings. Message({ required this.text, diff --git a/apps/AppWithWearable/lib/main.dart b/apps/AppWithWearable/lib/main.dart index 720c2ec25..c8b3c05a6 100644 --- a/apps/AppWithWearable/lib/main.dart +++ b/apps/AppWithWearable/lib/main.dart @@ -40,14 +40,6 @@ void main() async { } } -_initStuff() async { - ble.FlutterBluePlus.setLogLevel(ble.LogLevel.info, color: true); - await initializeNotifications(); - await SharedPreferencesUtil.init(); - await MixpanelManager.init(); - await ObjectBoxUtil.init(); -} - _getRunApp() { return runApp(const MyApp()); } diff --git a/apps/AppWithWearable/lib/pages/chat/page.dart b/apps/AppWithWearable/lib/pages/chat/page.dart index 2562fb58b..1d48d8e42 100644 --- a/apps/AppWithWearable/lib/pages/chat/page.dart +++ b/apps/AppWithWearable/lib/pages/chat/page.dart @@ -8,7 +8,6 @@ import 'package:friend_private/backend/database/memory.dart'; import 'package:friend_private/backend/database/memory_provider.dart'; import 'package:friend_private/backend/mixpanel.dart'; import 'package:friend_private/backend/preferences.dart'; -import 'package:friend_private/backend/storage/memories.dart'; import 'package:friend_private/backend/storage/message.dart'; import 'package:friend_private/pages/chat/widgets/ai_message.dart'; import 'package:friend_private/pages/chat/widgets/user_message.dart'; @@ -71,47 +70,32 @@ class _ChatPageState extends State with AutomaticKeepAliveClientMixin Widget build(BuildContext context) { return Stack( children: [ - Container( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 0), - child: Column( - children: [ - Expanded( - child: ListView.builder( - scrollDirection: Axis.vertical, - controller: listViewController, - itemCount: _messages.length, - itemBuilder: (context, chatIndex) { - final message = _messages[chatIndex]; - if (message.type == 'ai') { - var messageMemoriesId = Set.from(message.memoryIds ?? []); - return Padding( - padding: EdgeInsets.only( - bottom: chatIndex == _messages.length - 1 - ? widget.textFieldFocusNode.hasFocus - ? 120 - : 160 - : 0), - child: AIMessage( + Align( + alignment: Alignment.bottomCenter, + child: ListView.builder( + scrollDirection: Axis.vertical, + controller: listViewController, + itemCount: _messages.length, + itemBuilder: (context, chatIndex) { + final message = _messages[chatIndex]; + final isLastMessage = chatIndex == _messages.length - 1; + double topPadding = chatIndex == 0 ? 24 : 0; + double bottomPadding = isLastMessage ? (widget.textFieldFocusNode.hasFocus ? 120 : 180) : 0; + return Padding( + key: ValueKey(message.id), + padding: EdgeInsets.only(bottom: bottomPadding, left: 18, right: 18, top: topPadding), + child: message.type == 'ai' + ? AIMessage( message: message, sendMessage: _sendMessageUtil, displayOptions: _messages.length <= 1, - memories: widget.memories.where((m) => messageMemoriesId.contains(m.id.toString())).toList(), - ), - ); - } - return Padding( - padding: EdgeInsets.only( - bottom: chatIndex == _messages.length - 1 - ? widget.textFieldFocusNode.hasFocus - ? 120 - : 160 - : 0), - child: HumanMessage(message: message), - ); - }, - )), - // const SizedBox(height: 160), - ], + memories: widget.memories + .where((m) => message.memoryIds?.contains(m.id.toString()) ?? false) + .toList(), + ) + : HumanMessage(message: message), + ); + }, ), ), Align( @@ -121,7 +105,7 @@ class _ChatPageState extends State with AutomaticKeepAliveClientMixin child: Container( width: double.maxFinite, padding: const EdgeInsets.fromLTRB(16, 4, 16, 0), - margin: const EdgeInsets.fromLTRB(32, 0, 32, 0), + margin: const EdgeInsets.fromLTRB(18, 0, 18, 0), decoration: const BoxDecoration( color: Colors.black, borderRadius: BorderRadius.all(Radius.circular(16)), @@ -243,6 +227,6 @@ class _ChatPageState extends State with AutomaticKeepAliveClientMixin } _moveListToBottom({bool initial = false}) async { - listViewController.jumpTo(listViewController.position.maxScrollExtent + (initial ? 240 : 0)); + listViewController.jumpTo(listViewController.position.maxScrollExtent); } } diff --git a/apps/AppWithWearable/lib/pages/memories/page.dart b/apps/AppWithWearable/lib/pages/memories/page.dart index d2025e4e4..86804e65b 100644 --- a/apps/AppWithWearable/lib/pages/memories/page.dart +++ b/apps/AppWithWearable/lib/pages/memories/page.dart @@ -27,60 +27,65 @@ class _MemoriesPageState extends State with AutomaticKeepAliveClie @override Widget build(BuildContext context) { - return ListView( - children: [ - const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 18.0), - child: Text( - 'Welcome back.', - style: Theme.of(context).textTheme.titleLarge!.copyWith(color: Colors.grey, fontSize: 20), + return CustomScrollView( + slivers: [ + const SliverToBoxAdapter(child: SizedBox(height: 16)), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18.0), + child: Text( + 'Welcome back.', + style: Theme.of(context).textTheme.titleLarge!.copyWith(color: Colors.grey, fontSize: 20), + ), ), ), - const SizedBox(height: 16), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text( - widget.displayDiscardMemories ? 'Hide Discarded' : 'Show Discarded', - style: const TextStyle(color: Colors.white, fontSize: 16), - ), - const SizedBox(width: 8), - IconButton( + const SliverToBoxAdapter(child: SizedBox(height: 16)), + SliverToBoxAdapter( + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + widget.displayDiscardMemories ? 'Hide Discarded' : 'Show Discarded', + style: const TextStyle(color: Colors.white, fontSize: 16), + ), + const SizedBox(width: 8), + IconButton( onPressed: () { widget.toggleDiscardMemories(); }, icon: Icon( widget.displayDiscardMemories ? Icons.cancel_outlined : Icons.filter_list, color: Colors.white, - )), - ], + ), + ), + ], + ), ), - Padding( - padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), - child: (widget.memories.isEmpty) - ? const Center( + widget.memories.isEmpty + ? const SliverToBoxAdapter( + child: Center( child: Padding( padding: EdgeInsets.only(top: 32.0), child: EmptyMemoriesWidget(), ), - ) - : ListView.builder( - itemCount: widget.memories.length, - primary: false, - shrinkWrap: true, - scrollDirection: Axis.vertical, - itemBuilder: (context, index) { + ), + ) + : SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { return MemoryListItem( memoryIdx: index, memory: widget.memories[index], loadMemories: widget.refreshMemories, ); }, + childCount: widget.memories.length, ), + ), + const SliverToBoxAdapter( + child: SizedBox(height: 80), ), - const SizedBox(height: 80), ], ); }