diff --git a/lib/components/constants.dart b/lib/components/constants.dart index de320f2..0555409 100644 --- a/lib/components/constants.dart +++ b/lib/components/constants.dart @@ -14,4 +14,4 @@ class Constants { var convCollectionId = '678c8667001944dee794'; var conversationLinkId = '67b949c9001234c0d7b1'; var imageStorageBucket = '678d2162002a6770aaad'; -} +} \ No newline at end of file diff --git a/lib/components/messaging_provider.dart b/lib/components/messaging_provider.dart index 618046a..f6a191e 100644 --- a/lib/components/messaging_provider.dart +++ b/lib/components/messaging_provider.dart @@ -25,28 +25,16 @@ class MessagingProviders extends ChangeNotifier { late final Account account; late final Databases database; late final Storage storage; - late final Realtime realtime; - late final RealtimeSubscription? subscription; + late model.User? user; // Auth Status AuthStatus? status; - late model.User? user; static MessagingProviders instance = MessagingProviders(); MessagingProviders() { _init(); _loadUser(); - subscribe(); - } - - subscribe() { - subscription = realtime.subscribe( - [ - 'database.${Constants.instance.databaseId}.chats', - 'database.${Constants.instance.databaseId}.conversations' - ], - ); } _init() { @@ -57,7 +45,6 @@ class MessagingProviders extends ChangeNotifier { account = Account(client); database = Databases(client); storage = Storage(client); - realtime = Realtime(client); } _loadUser() async { @@ -66,22 +53,15 @@ class MessagingProviders extends ChangeNotifier { if (user != null) { status = AuthStatus.authenticated; await updateLastSeen(); - _autoLogin(); + NavigationService.instance.navigateToReplacement("home"); } } catch (e) { status = AuthStatus.notAuthenticated; - } finally { - notifyListeners(); } } - void _autoLogin() { - NavigationService.instance.navigateToReplacement("home"); - } - void login(String _email, String _password) async { // - notifyListeners(); try { status != AuthStatus.error; @@ -105,9 +85,10 @@ class MessagingProviders extends ChangeNotifier { SnackBarService.instance.showSnackBarError(e.toString()); // } - notifyListeners(); } on Exception catch (e) { SnackBarService.instance.showSnackBarError(e.toString()); + } finally { + notifyListeners(); } } @@ -115,22 +96,20 @@ class MessagingProviders extends ChangeNotifier { try { await updateLastSeen(); await account.deleteSession(sessionId: 'current'); - user = null; + // user = null; status = AuthStatus.notAuthenticated; // await onSuccess(); - await NavigationService.instance.navigateToReplacement("login"); SnackBarService.instance.showSnackBarError("logged out successfully"); + await NavigationService.instance.navigateToReplacement("login"); } on AppwriteException catch (e) { SnackBarService.instance.showSnackBarError(e.message!); } on Exception catch (e) { SnackBarService.instance.showSnackBarError(e.toString()); } - notifyListeners(); } Future createUser(String _email, String _password, String _name, Future Function(String _uid) onSuccess) async { - notifyListeners(); try { user = await account.create( userId: ID.unique(), email: _email, password: _password, name: _name); @@ -149,12 +128,10 @@ class MessagingProviders extends ChangeNotifier { SnackBarService.instance.showSnackBarError(e.toString()); // } - notifyListeners(); } Future createUserInDB( String _documentId, String _email, String _name, String _imageURL) async { - notifyListeners(); try { await database.createDocument( databaseId: Constants.instance.databaseId, @@ -184,7 +161,6 @@ class MessagingProviders extends ChangeNotifier { user = null; // } - notifyListeners(); } Future updateLastSeen() async { @@ -196,7 +172,7 @@ class MessagingProviders extends ChangeNotifier { ); } - Stream getUser(String userId) { + Stream getUser(String userId) async* { // var _userData = database.getDocument( databaseId: Constants.instance.databaseId, @@ -204,14 +180,14 @@ class MessagingProviders extends ChangeNotifier { documentId: userId, ); - return _userData.asStream().map((_document) { + updateLastSeen(); + yield* _userData.asStream().map((_document) { return User.fromData(_document); }); } Stream> getAllUsers(String _searchText) async* { // - // while (true) { List _returnValue = []; if (_searchText.isNotEmpty) { var _usersData = database.listDocuments( @@ -234,12 +210,10 @@ class MessagingProviders extends ChangeNotifier { } else { yield* Stream.empty(); } - // } } Stream> getUserConversation(String userId) async* { // - // while (true) { List _chatId = []; List _details = []; List _lastModified = []; @@ -265,107 +239,53 @@ class MessagingProviders extends ChangeNotifier { return _returnValue; }, ); - // } - } - Future> getUserConversations(String userId) async { - List _chatId = []; - List _details = []; - List _lastModified = []; - List _returnValue = []; - - var _document = await database.getDocument( - databaseId: Constants.instance.databaseId, - collectionId: Constants.instance.convCollectionId, - documentId: userId.trim(), - ); - - _chatId = _document.data['chatId']; - _details = _document.data['details']; - _lastModified = _document.data['lastModified']; - - for (int i = 0; i < _chatId.length; i++) { - _returnValue.add( - Conversations.fromData(_chatId[i], _details[i], _lastModified[i])); - } - _returnValue.sort((a, b) => b.lastModified!.compareTo(a.lastModified!)); - - return _returnValue; - // .then((document) { - // _chatId = document.data['chatId']; - // _details = document.data['details']; - // _lastModified = document.data['lastModified']; - - // for (int i = 0; i < _chatId.length; i++) { - // _returnValue.add( - // Conversations.fromData(_chatId[i], _details[i], _lastModified[i])); - // } - // _returnValue.sort((a, b) => b.lastModified!.compareTo(a.lastModified!)); - - // //conversationsController.add(_returnValue); - // }).catchError((error) { - // _returnValue; - // // conversationsController.addError(error); - // }); - - // return Future.delayed(Duration(microseconds: 300)); + notifyListeners(); } Stream getMessage(String _chatId) async* { - // - // while (true) { - var _chat = database.getDocument( - databaseId: Constants.instance.databaseId, - collectionId: Constants.instance.chatsCollectionId, - documentId: _chatId, - ); - - yield* _chat.asStream().map( - (_document) { - return Chats.fromDocument(_document); - }, - ); - // } - } - - Future getConversationLink(var _linkId) async { // try { - var _conversationLinkDetail = await database.getDocument( + var _chat = database.getDocument( databaseId: Constants.instance.databaseId, - collectionId: Constants.instance.conversationLinkId, - documentId: _linkId, + collectionId: Constants.instance.chatsCollectionId, + documentId: _chatId, + ); + yield* _chat.asStream().map( + (_document) { + return Chats.fromDocument(_document); + }, ); - notifyListeners(); - return _conversationLinkDetail.data['chatId']; } on AppwriteException catch (e) { - notifyListeners(); - return ''; + e.message!; + yield* Stream.empty(); + } on Exception catch (e) { + e.toString(); + yield* Stream.empty(); } - } - - Future newMesaage(Map _chat) async { - // - var chatId = ID.unique(); - await database.createDocument( - databaseId: Constants.instance.databaseId, - collectionId: Constants.instance.chatsCollectionId, - documentId: chatId, - data: _chat, - ); + // else {} notifyListeners(); - return chatId; } - Future sendMessage( - String chatId, Map _messages) async { + Future sendMessage(String _chatId, Map _messages, + String _newOrUpdate) async { // - await database.updateDocument( - databaseId: Constants.instance.databaseId, - collectionId: Constants.instance.chatsCollectionId, - documentId: chatId, - data: _messages, - ); + if (_newOrUpdate == 'new') { + await database.createDocument( + databaseId: Constants.instance.databaseId, + collectionId: Constants.instance.chatsCollectionId, + documentId: _chatId, + data: _messages, + ); + } else if (_newOrUpdate == 'update') { + await database.updateDocument( + databaseId: Constants.instance.databaseId, + collectionId: Constants.instance.chatsCollectionId, + documentId: _chatId, + data: _messages, + ); + } + notifyListeners(); } @@ -394,7 +314,6 @@ class MessagingProviders extends ChangeNotifier { SnackBarService.instance.showSnackBarError(e.toString()); // } - notifyListeners(); return ""; } } diff --git a/lib/components/messaging_routes.dart b/lib/components/messaging_routes.dart index 9e3b50a..20c01d5 100644 --- a/lib/components/messaging_routes.dart +++ b/lib/components/messaging_routes.dart @@ -12,4 +12,4 @@ class MessagingRoutes { "register": (BuildContext _context) => RegisterationPage(), "home": (BuildContext _context) => HomePage(), }; -} +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 1a850c5..5ff0782 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:messaging_app/components/messaging_routes.dart'; -import 'package:messaging_app/service/navigation_service.dart'; +import 'package:messaging_app/messaging_app.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); @@ -9,27 +8,4 @@ void main() { MessagingApp(), ); } - -class MessagingApp extends StatelessWidget { - - const MessagingApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Messaging App', - navigatorKey: NavigationService.instance.navigatorKey, - theme: ThemeData( - brightness: Brightness.dark, - scaffoldBackgroundColor: Colors.black, - primaryColor: Color.fromRGBO(42, 117, 188, 1.0), - colorScheme: ColorScheme.dark(), - useMaterial3: true, - ), - routes: MessagingRoutes.instance.messagingRoutes, - initialRoute: "login", - // home: RegisterationPage(), - ); - } -} + \ No newline at end of file diff --git a/lib/main_sub_test.dart b/lib/main_sub_test.dart deleted file mode 100644 index ac4ea79..0000000 --- a/lib/main_sub_test.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'package:appwrite/appwrite.dart'; -import 'package:flutter/material.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'FlAppwrite Realtime Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - home: const HomePage(), - ); - } -} - -class HomePage extends StatefulWidget { - const HomePage({super.key}); - - @override - State createState() => _HomePageState(); -} - -class _HomePageState extends State { - List> items = []; - final TextEditingController _nameController = TextEditingController(); - RealtimeSubscription? subscription; - late final Client client; - final database = '67db1d75002a1826f6f9'; // your database id - final itemsCollection = '67db1e36001ffd2719b7'; // your collection id - late final Databases databases; - - @override - initState() { - super.initState(); - - client = Client() - .setEndpoint('https://api.theonasanyas.com/v1') - .setProject('67833160000cb0801fdb'); // your project id - databases = Databases(client); - loadItems(); - subscribe(); - } - - loadItems() async { - try { - final res = await databases.listDocuments( - databaseId: database, - collectionId: itemsCollection, - ); - setState(() { - items = List>.from(res.documents.map( - (e) => e.data, - )); - }); - } on AppwriteException catch (e) { - print(e.message); - } - } - - void subscribe() { - final realtime = Realtime(client); - - subscription = realtime.subscribe([ - 'documents' // subscribe to all documents in every database and collection - ]); - - // listen to changes - subscription!.stream.listen((data) { - // data will consist of `events` and a `payload` - final event = data.events.first; - if (data.payload.isNotEmpty) { - if (event.endsWith('.create')) { - var item = data.payload; - items.add(item); - setState(() {}); - } else if (event.endsWith('.delete')) { - var item = data.payload; - items.removeWhere((it) => it['\$id'] == item['\$id']); - setState(() {}); - } - } - }); - } - - @override - dispose() { - subscription?.close(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('FlAppwrite Realtime Demo'), - ), - body: ListView(children: [ - ...items.map((item) => ListTile( - title: Text(item['name']), - trailing: IconButton( - icon: const Icon(Icons.delete), - onPressed: () async { - await databases.deleteDocument( - databaseId: database, - collectionId: itemsCollection, - documentId: item['\$id'], - ); - }, - ), - )), - ]), - floatingActionButton: FloatingActionButton( - child: const Icon(Icons.add), - onPressed: () { - // dialog to add new item - showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Add new item'), - content: TextField( - controller: _nameController, - ), - actions: [ - TextButton( - child: const Text('Cancel'), - onPressed: () => Navigator.of(context).pop(), - ), - TextButton( - child: const Text('Add'), - onPressed: () { - // add new item - final name = _nameController.text; - if (name.isNotEmpty) { - _nameController.clear(); - _addItem(name); - } - Navigator.of(context).pop(); - }, - ), - ], - ), - ); - }, - ), - ); - } - - void _addItem(String name) async { - try { - await databases.createDocument( - databaseId: database, - collectionId: itemsCollection, - documentId: ID.unique(), - data: {'name': name}, - ); - } on AppwriteException catch (e) { - print(e.message); - } - } -} diff --git a/lib/messaging_app.dart b/lib/messaging_app.dart new file mode 100644 index 0000000..054d2e0 --- /dev/null +++ b/lib/messaging_app.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:messaging_app/components/messaging_routes.dart'; +import 'package:messaging_app/service/navigation_service.dart'; + +class MessagingApp extends StatelessWidget { + + const MessagingApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Messaging App', + navigatorKey: NavigationService.instance.navigatorKey, + theme: ThemeData( + brightness: Brightness.dark, + scaffoldBackgroundColor: Colors.black, + primaryColor: Color.fromRGBO(42, 117, 188, 1.0), + colorScheme: ColorScheme.dark(), + useMaterial3: true, + ), + routes: MessagingRoutes.instance.messagingRoutes, + initialRoute: "login", + ); + } +} \ No newline at end of file diff --git a/lib/models/chats.dart b/lib/models/chats.dart index c168242..7cd54be 100644 --- a/lib/models/chats.dart +++ b/lib/models/chats.dart @@ -41,4 +41,4 @@ class Chats { messages: _messagesList, ); } -} +} \ No newline at end of file diff --git a/lib/models/conversations.dart b/lib/models/conversations.dart index b913463..6bd32d1 100644 --- a/lib/models/conversations.dart +++ b/lib/models/conversations.dart @@ -1,16 +1,28 @@ import 'package:messaging_app/models/details.dart'; class Conversations { + String? id; String chatId; Details details; DateTime? lastModified; Conversations({ + this.id, required this.chatId, required this.details, this.lastModified, }); + // Convert the details string to a Details object + // and parse the lastModified string to a DateTime object + // Note: You may need to adjust the parsing logic based on your data format + // For example, if _details is a JSON string, you can use json.decode to parse it + // and create a Details object from the parsed data. + // Assuming _details is a JSON string, you can use the Details.fromData method + // to create a Details object. + // Assuming _lastModified is a string in ISO 8601 format + + factory Conversations.fromData( String _chatId, String _details, String _lastModified) => Conversations( diff --git a/lib/models/details.dart b/lib/models/details.dart index 7bc73ef..e2b8339 100644 --- a/lib/models/details.dart +++ b/lib/models/details.dart @@ -3,28 +3,28 @@ import 'dart:convert'; class Details { String name; String image; + String email; int unseenCount; String? lastMessage; DateTime? timeStamp; String? type; - Details({ - required this.name, - required this.image, - required this.lastMessage, - required this.timeStamp, - required this.unseenCount, - required this.type - }); + Details( + {required this.name, + required this.image, + required this.email, + required this.lastMessage, + required this.timeStamp, + required this.unseenCount, + required this.type}); - factory Details.fromData(String _details) { - return Details( - name: json.decode(_details)['name'], - image: json.decode(_details)['image'], - lastMessage: json.decode(_details)['lastMessage'], - timeStamp: DateTime.parse(json.decode(_details)['timeStamp'] ?? ""), - unseenCount: json.decode(_details)['unseenCount'], - type: json.decode(_details)['type'], - ); - } + factory Details.fromData(String _details) => Details( + email: json.decode(_details)['email'], + name: json.decode(_details)['name'], + image: json.decode(_details)['image'], + lastMessage: json.decode(_details)['lastMessage'] ?? "", + timeStamp: DateTime.parse(json.decode(_details)['timeStamp']), + unseenCount: json.decode(_details)['unseenCount'], + type: json.decode(_details)['type'] ?? "", + ); } diff --git a/lib/models/messages.dart b/lib/models/messages.dart index dedb2c0..bd4405c 100644 --- a/lib/models/messages.dart +++ b/lib/models/messages.dart @@ -27,11 +27,10 @@ class Messages { timeStamp: _message['timeStamp'] ?? "", ); - Map toJson() => { "message": message, "senderId": senderId, "type": type, "timeStamp": timeStamp, }; -} +} \ No newline at end of file diff --git a/lib/models/users.dart b/lib/models/users.dart index 3412cee..56c13fd 100644 --- a/lib/models/users.dart +++ b/lib/models/users.dart @@ -15,13 +15,11 @@ class User { required this.lastSeen, }); - factory User.fromData(Document _document) { - return User( - id: _document.data['\$id'], - name: _document.data['name'], - email: _document.data['email'], - image: _document.data['image'], - lastSeen: DateTime.parse(_document.data['lastSeen']), - ); - } -} + factory User.fromData(Document _document) => User( + id: _document.data['\$id'], + name: _document.data['name'], + email: _document.data['email'], + image: _document.data['image'], + lastSeen: DateTime.parse(_document.data['lastSeen']), + ); +} \ No newline at end of file diff --git a/lib/pages/conversations_page.dart b/lib/pages/conversations_page.dart index 8b5e8d6..5b28e35 100644 --- a/lib/pages/conversations_page.dart +++ b/lib/pages/conversations_page.dart @@ -16,15 +16,19 @@ class ConversationsPage extends StatefulWidget { ConversationsPage({ super.key, required this.chatId, - // required this.receiverId, - required this.chatImage, - required this.chatName, + this.userId, + required this.userName, + required this.userImage, + this.userEmail, + // this.chatMessage, }); String chatId; - // String receiverId; - String chatName; - String chatImage; + String? userId; + String userName; + String userImage; + String? userEmail; + // Map? chatMessage; @override State createState() { @@ -34,13 +38,12 @@ class ConversationsPage extends StatefulWidget { class _ConversationsPageState extends State { // - + MessagingProviders _auth = MessagingProviders.instance; + final ScrollController _scrollController = ScrollController(); + final GlobalKey _formKey = GlobalKey(); late double _deviceHeight; late double _deviceWidth; - final ScrollController _scrollController; - final GlobalKey _formKey; - late MessagingProviders _auth; List> _messagesArray; List _messages; List _members; @@ -49,8 +52,6 @@ class _ConversationsPageState extends State { _ConversationsPageState() : _messageText = '', - _formKey = GlobalKey(), - _scrollController = ScrollController(), _messagesArray = [], _messages = [], _members = [], @@ -64,12 +65,46 @@ class _ConversationsPageState extends State { return Scaffold( appBar: AppBar( + actions: [ + IconButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => Dialog( + child: SizedBox( + height: _deviceHeight * 0.25, + width: _deviceHeight * 0.25, + child: Column( + children: [ + Container( + // padding: EdgeInsets.all(20), + height: _deviceHeight * 0.20, + width: _deviceHeight * 0.20, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + image: DecorationImage( + image: NetworkImage(widget.userImage), + fit: BoxFit.cover, + ), + ), + ), + Text(widget.userName), + Text(widget.userEmail!) + ], + ), + ), + ), + ); + }, + icon: Icon(Icons.info_outline), + ) + ], backgroundColor: Theme.of(context).scaffoldBackgroundColor, - title: Text(widget.chatName), + title: Text(widget.userName), titleTextStyle: TextStyle(fontSize: 16), ), body: ChangeNotifierProvider.value( - value: MessagingProviders.instance, + value: _auth, child: _conversationPageUI(), ), ); @@ -92,11 +127,11 @@ class _ConversationsPageState extends State { } Widget _messageListView() { - return Container( + return SizedBox( height: _deviceHeight * 0.80, width: _deviceWidth, child: StreamBuilder( - stream: MessagingProviders.instance.getMessage(widget.chatId), + stream: _auth.getMessage(widget.chatId), builder: (_context, _snapshot) { if (_snapshot.hasData) { _members = _snapshot.data!.members; @@ -110,6 +145,15 @@ class _ConversationsPageState extends State { child: Text("No Chat"), ); } else { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_scrollController.hasClients) { + _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(microseconds: 500), + curve: Curves.fastOutSlowIn); + } + }); + return ListView.builder( controller: _scrollController, padding: EdgeInsets.symmetric( @@ -203,7 +247,7 @@ class _ConversationsPageState extends State { Widget _imageMessageBubble(bool _isOwnMessage, Messages _message) { List _colorScheme = _isOwnMessage ? [Colors.blue, Color.fromRGBO(42, 117, 188, 1)] - : [Color.fromRGBO(49, 69, 69, 1), Color.fromRGBO(43, 43, 43, 1)]; + : [Color.fromRGBO(49, 69, 69, 1), Color.fromRGBO(249, 249, 249, 1)]; return Container( // height: _deviceHeight * 0.10, // width: _deviceWidth * 0.75, @@ -236,6 +280,7 @@ class _ConversationsPageState extends State { height: 10, ), Text( + // '${DateTime.parse(_message.timeStamp).day}', timeago.format(DateTime.parse(_message.timeStamp)), style: TextStyle(color: Colors.white70, fontSize: 10), ), @@ -253,7 +298,7 @@ class _ConversationsPageState extends State { image: DecorationImage( fit: BoxFit.cover, image: NetworkImage( - widget.chatImage, + widget.userImage, ), ), ), @@ -318,7 +363,7 @@ class _ConversationsPageState extends State { } Widget _sendText(BuildContext _context) { - return Container( + return SizedBox( height: _deviceHeight * 0.045, width: _deviceHeight * 0.045, child: IconButton( @@ -349,8 +394,7 @@ class _ConversationsPageState extends State { onPressed: () async { XFile? _imageFile = await MediaService.instance.getImageFromLibrary(); if (_imageFile != null) { - var _url = await MessagingProviders.instance - .uploadUserImage(_imageFile, '_uid'); + var _url = await _auth.uploadUserImage(_imageFile, '_uid'); await sendMessage('image', _url); } @@ -387,9 +431,7 @@ class _ConversationsPageState extends State { } await _auth.sendMessage( - widget.chatId, - {"messages": outMessages}, - ); + widget.chatId, {"messages": outMessages}, 'update'); }, ), ], @@ -397,7 +439,30 @@ class _ConversationsPageState extends State { } Future sendMessage(var type, var _message) async { - List outMessages = []; + List _outMessages = []; + + if (_members.isEmpty) { + _members = [_auth.user!.$id, widget.userId!]; + + List messagesTemp = []; + messagesTemp = [ + json.encode({ + _auth.user!.$id: [], + }), + json.encode({ + widget.userId: [], + }) + ]; + + Map _chat = { + "ownerId": _auth.user!.$id, + "members": _members, + "messages": messagesTemp + }; + + await _auth.sendMessage(widget.chatId, _chat, 'new'); + await Future.delayed(Duration(seconds: 1)); + } for (var i = 0; i < _members.length; i++) { _messagesArray[i].add( @@ -409,7 +474,7 @@ class _ConversationsPageState extends State { ), ); - outMessages.add( + _outMessages.add( json.encode( {_members[i]: _messagesArray[i]}, ), @@ -418,7 +483,8 @@ class _ConversationsPageState extends State { await _auth.sendMessage( widget.chatId, - {"messages": outMessages}, + {"messages": _outMessages}, + 'update', ); } } diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index d757b1c..7efddcf 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -65,27 +65,28 @@ class _HomePageState extends State _deviceWidth = MediaQuery.of(context).size.width; return Scaffold( - // backgroundColor: Theme.of(context).scaffoldBackgroundColor, - appBar: AppBar( - automaticallyImplyLeading: false, - // backgroundColor: Theme.of(context).scaffoldBackgroundColor, - title: Text("the messenger"), - titleTextStyle: TextStyle(fontSize: 16), - bottom: TabBar( - unselectedLabelColor: Colors.grey, - indicatorColor: Colors.blue, - labelColor: Colors.blue, - controller: _tabController, - tabs: _iconTabs, - ), + // backgroundColor: Theme.of(context).scaffoldBackgroundColor, + appBar: AppBar( + automaticallyImplyLeading: false, + // backgroundColor: Theme.of(context).scaffoldBackgroundColor, + title: Text("the messenger"), + titleTextStyle: TextStyle(fontSize: 16), + bottom: TabBar( + unselectedLabelColor: Colors.grey, + indicatorColor: Colors.blue, + labelColor: Colors.blue, + controller: _tabController, + tabs: _iconTabs, ), - body: _tabBarPages(), - floatingActionButton: _tabController.index == 1 - ? Container() - : FloatingActionButton( - child: Icon(Icons.add), - onPressed: () {}, - )); + ), + body: _tabBarPages(), + // floatingActionButton: _tabController.index == 1 + // ? Container() + // : FloatingActionButton( + // child: Icon(Icons.add), + // onPressed: () {}, + // ), + ); } Widget _tabBarPages() { diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index f0c9962..f8b36e5 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -23,12 +23,8 @@ class _LoginPageState extends State { String? _email; String? _password; - late GlobalKey _formKey; - late MessagingProviders _auth; - - _LoginPageState() { - _formKey = GlobalKey(); - } + final GlobalKey _formKey = GlobalKey(); + MessagingProviders _auth = MessagingProviders.instance; @override Widget build(BuildContext context) { @@ -40,7 +36,7 @@ class _LoginPageState extends State { backgroundColor: Theme.of(context).scaffoldBackgroundColor, body: Align( child: ChangeNotifierProvider.value( - value: MessagingProviders.instance, + value: _auth, child: _loginPageUI(), ), ), @@ -133,9 +129,11 @@ class _LoginPageState extends State { }, cursorColor: Colors.white, decoration: InputDecoration( - hintText: "email address", - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.white))), + hintText: "email address", + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.white), + ), + ), ); } @@ -158,9 +156,11 @@ class _LoginPageState extends State { obscureText: true, cursorColor: Colors.white, decoration: InputDecoration( - hintText: "password", - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.white))), + hintText: "password", + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.white), + ), + ), ); } diff --git a/lib/pages/profile_page.dart b/lib/pages/profile_page.dart index 652a9f8..ac602f4 100644 --- a/lib/pages/profile_page.dart +++ b/lib/pages/profile_page.dart @@ -11,17 +11,17 @@ class ProfilePage extends StatelessWidget { final double _deviceHeight; final double _deviceWidth; - late MessagingProviders _auth; - + MessagingProviders _auth = MessagingProviders.instance; + ProfilePage(this._deviceHeight, this._deviceWidth, {super.key}); @override Widget build(BuildContext context) { - return Container( + return SizedBox( height: _deviceHeight, width: _deviceWidth, child: ChangeNotifierProvider.value( - value: MessagingProviders.instance, + value: _auth, child: _profilePageUI(), ), ); @@ -29,10 +29,11 @@ class ProfilePage extends StatelessWidget { Widget _profilePageUI() { return Builder(builder: (BuildContext _context) { + // assert(_auth.user != null); SnackBarService.instance.buildContext = _context; _auth = Provider.of(_context); return StreamBuilder( - stream: MessagingProviders.instance.getUser(_auth.user!.$id), + stream: _auth.getUser(_auth.user!.$id), builder: (_, _snapshot) { var _userData = _snapshot.data; return _snapshot.hasData @@ -76,7 +77,7 @@ class ProfilePage extends StatelessWidget { } Widget _userNameWidget(String _name) { - return Container( + return SizedBox( height: _deviceHeight * 0.05, width: _deviceWidth, child: Text( @@ -91,7 +92,7 @@ class ProfilePage extends StatelessWidget { } Widget _userEmailWidget(String _email) { - return Container( + return SizedBox( height: _deviceHeight * 0.03, width: _deviceWidth, child: Text( @@ -106,7 +107,7 @@ class ProfilePage extends StatelessWidget { } Widget _logoutButton() { - return Container( + return SizedBox( height: _deviceHeight * 0.06, width: _deviceWidth * 0.80, child: MaterialButton( @@ -115,7 +116,7 @@ class ProfilePage extends StatelessWidget { }, color: Colors.red, child: Text( - "Logout".toUpperCase(), + "logout".toUpperCase(), style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, diff --git a/lib/pages/recent_converstations_page.dart b/lib/pages/recent_converstations_page.dart index 17c58fb..449d47e 100644 --- a/lib/pages/recent_converstations_page.dart +++ b/lib/pages/recent_converstations_page.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:messaging_app/components/messaging_provider.dart'; @@ -25,38 +23,8 @@ class RecentConverstationsPage extends StatefulWidget { } class _RecentConverstationsPageState extends State { - late MessagingProviders _auth; - List streamData = []; - -// To listen to the conversations stream - @override - void dispose() { - super.dispose(); - loadStream(); - } - - loadStream() async { - streamData = - await MessagingProviders.instance.getUserConversations(_auth.user!.$id); - } - - subscribe() { - MessagingProviders.instance.subscription!.stream.listen((data) { - final event = data.events.first; - if (event.endsWith('.create')) { - streamData.add(data.payload['']); - } else if (event.endsWith('.update')) {} - }); - } - - @override - void initState() { - super.initState(); - MessagingProviders.instance.subscription!.stream.listen((data) { - final event = data.events.first; - ; - }); - } + // + MessagingProviders _auth = MessagingProviders.instance; @override Widget build(BuildContext context) { @@ -64,7 +32,7 @@ class _RecentConverstationsPageState extends State { height: widget._deviceHeight, width: widget._deviceWidth, child: ChangeNotifierProvider.value( - value: MessagingProviders.instance, + value: _auth, child: _conversationView(context), ), ); @@ -78,8 +46,7 @@ class _RecentConverstationsPageState extends State { height: widget._deviceHeight, width: widget._deviceWidth, child: StreamBuilder>( - stream: MessagingProviders.instance - .getUserConversation(_auth.user!.$id), + stream: _auth.getUserConversation(_auth.user!.$id), builder: (_context, _snapshot) { if (_snapshot.hasData) { return ListView.builder( @@ -95,8 +62,9 @@ class _RecentConverstationsPageState extends State { builder: (BuildContext _context) { return ConversationsPage( chatId: _chatId, - chatName: _details.name, - chatImage: _details.image, + userName: _details.name, + userImage : _details.image, + userEmail: _details.email, ); }, ), @@ -104,9 +72,11 @@ class _RecentConverstationsPageState extends State { }, title: Text(_details.name), subtitle: Text( - _details.type == 'text' - ? _details.lastMessage! - : 'attachment: image', + _details.type == '' + ? 'start chatting...' + : _details.type == 'text' + ? _details.lastMessage! + : 'attachment: image', ), leading: Container( width: 50, diff --git a/lib/pages/registeration_page.dart b/lib/pages/registeration_page.dart index 45565f6..63724b8 100644 --- a/lib/pages/registeration_page.dart +++ b/lib/pages/registeration_page.dart @@ -26,10 +26,8 @@ class _RegisterationPageState extends State { String? _email; String? _password; - GlobalKey _formKey; - late MessagingProviders _auth; - - _RegisterationPageState() : _formKey = GlobalKey(); + final GlobalKey _formKey = GlobalKey(); + MessagingProviders _auth = MessagingProviders.instance; @override Widget build(BuildContext context) { @@ -43,7 +41,7 @@ class _RegisterationPageState extends State { // color: Colors.blue, alignment: Alignment.center, child: ChangeNotifierProvider.value( - value: MessagingProviders.instance, + value: _auth, child: _registerationPageUI(), ), ), @@ -163,9 +161,11 @@ class _RegisterationPageState extends State { }, cursorColor: Colors.white, decoration: InputDecoration( - hintText: "name", - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.white))), + hintText: "name", + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.white), + ), + ), ); } @@ -189,9 +189,11 @@ class _RegisterationPageState extends State { }, cursorColor: Colors.white, decoration: InputDecoration( - hintText: "email", - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.white))), + hintText: "email", + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.white), + ), + ), ); } @@ -214,9 +216,11 @@ class _RegisterationPageState extends State { obscureText: true, cursorColor: Colors.white, decoration: InputDecoration( - hintText: "password", - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.white))), + hintText: "password", + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: Colors.white), + ), + ), ); } diff --git a/lib/pages/search_page.dart b/lib/pages/search_page.dart index 7012ea8..ad7681d 100644 --- a/lib/pages/search_page.dart +++ b/lib/pages/search_page.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:messaging_app/components/messaging_provider.dart'; import 'package:messaging_app/models/users.dart'; @@ -18,7 +16,6 @@ class SearchPage extends StatefulWidget { double deviceHeight; double deviceWidth; - late MessagingProviders _auth; @override State createState() => _SearchPageState(); @@ -27,6 +24,7 @@ class SearchPage extends StatefulWidget { class _SearchPageState extends State { late String _searchText; late List _usersData; + MessagingProviders _auth = MessagingProviders.instance; // @override void initState() { @@ -42,7 +40,7 @@ class _SearchPageState extends State { height: widget.deviceHeight, width: widget.deviceWidth, child: ChangeNotifierProvider.value( - value: MessagingProviders.instance, + value: _auth, child: _searchPaheUI(), ), ), @@ -51,7 +49,7 @@ class _SearchPageState extends State { Widget _searchPaheUI() { return Builder(builder: (_context) { - widget._auth = Provider.of(_context); + _auth = Provider.of(_context); return Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, @@ -68,7 +66,9 @@ class _SearchPageState extends State { return Container( height: widget.deviceHeight * 0.08, width: widget.deviceWidth, - padding: EdgeInsets.symmetric(vertical: widget.deviceHeight * 0.02), + padding: EdgeInsets.symmetric( + vertical: widget.deviceHeight * 0.02, + ), child: TextField( autocorrect: false, style: TextStyle( @@ -91,133 +91,106 @@ class _SearchPageState extends State { Widget _searchPageUIList() { return StreamBuilder>( - stream: MessagingProviders.instance.getAllUsers(_searchText.trim()), + stream: _auth.getAllUsers(_searchText.trim()), builder: (_, _snapshot) { if (_snapshot.hasData) { _usersData = _snapshot.data! - .where((_data) => _data.id != widget._auth.user!.$id) + .where((_data) => _data.id != _auth.user!.$id) .toList(); } return _snapshot.hasData - ? SizedBox( - height: widget.deviceHeight * 0.70, - child: ListView.builder( - itemCount: _usersData.length, - itemBuilder: (_, _index) { - var _userData = _usersData[_index]; - var _isUserActive = _userData.lastSeen.isBefore( - DateTime.now().toUtc().subtract( - Duration( - minutes: 1, - ), - ), - ); - return ListTile( - enabled: true, - onTap: () async { - String _chatId; - String _linkId = - '${widget._auth.user!.$id}${_userData.id}'; - - _chatId = await MessagingProviders.instance - .getConversationLink( - _linkId.substring(0, 36)); - - if (_chatId.isEmpty) { - List _members = [ - widget._auth.user!.$id, - _userData.id - ]; - - List messagesTemp = []; - messagesTemp = [ - json.encode({ - widget._auth.user!.$id: [], - }), - json.encode({ - _userData.id: [], - }) - ]; - - Map _chat = { - "ownerId": widget._auth.user!.$id, - "members": _members, - "messages": messagesTemp - }; - _chatId = await MessagingProviders.instance - .newMesaage(_chat); - } - - NavigationService.instance.navigateToRoute( - MaterialPageRoute( - builder: (BuildContext _context) { - return ConversationsPage( - chatId: _chatId, - chatName: _userData.name, - chatImage: _userData.image, - ); - }, - ), - ); - }, - title: Text(_userData.name), - leading: Container( - width: 50, - height: 50, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(100), - image: DecorationImage( - fit: BoxFit.cover, - image: NetworkImage(_userData.image), - ), + ? SizedBox( + height: widget.deviceHeight * 0.70, + child: ListView.builder( + itemCount: _usersData.length, + itemBuilder: (_, _index) { + var _userData = _usersData[_index]; + var _isUserActive = _userData.lastSeen.isBefore( + DateTime.now().toUtc().subtract( + Duration( + minutes: 1, ), ), - trailing: Column( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - _isUserActive - ? Text( - "last seen", - style: TextStyle( - fontSize: 15, - ), - ) - : Text( - "active", - style: TextStyle( - fontSize: 15, - ), - ), - _isUserActive - ? Text( - timeago.format( - _userData.lastSeen, - ), - style: TextStyle(fontSize: 15), - ) - : Container( - height: 10, - width: 10, - decoration: BoxDecoration( - color: Colors.blue, - borderRadius: - BorderRadius.circular(100), - ), - ) - ], + ); + return ListTile( + enabled: true, + onTap: () async { + List _linkId = [ + _auth.user!.$id.substring(0, 18), + _userData.id.substring(0, 18) + ]; + _linkId.sort(); + + NavigationService.instance.navigateToRoute( + MaterialPageRoute( + builder: (BuildContext _context) { + return ConversationsPage( + chatId: _linkId.join(), + userId: _userData.id, + userName: _userData.name, + userImage: _userData.image, + userEmail: _userData.email, + ); + }, ), ); - }), - ) - : Container() - // : SpinKitWanderingCubes( - // color: Colors.blue, - // size: 50.0, - // ); - ; + }, + title: Text(_userData.name), + leading: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(100), + image: DecorationImage( + fit: BoxFit.cover, + image: NetworkImage(_userData.image), + ), + ), + ), + trailing: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _isUserActive + ? Text( + "last seen", + style: TextStyle( + fontSize: 15, + ), + ) + : Text( + "active", + style: TextStyle( + fontSize: 15, + ), + ), + _isUserActive + ? Text( + timeago.format( + _userData.lastSeen, + ), + style: TextStyle(fontSize: 15), + ) + : Container( + height: 10, + width: 10, + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: + BorderRadius.circular(100), + ), + ) + ], + ), + ); + }), + ) + : Container(); + // : SpinKitWanderingCubes( + // color: Colors.blue, + // size: 50.0, + // ); }); } }