From 20a8f0faa37fa8308f2b1fff9392054b56377c72 Mon Sep 17 00:00:00 2001 From: otsmr Date: Sun, 9 Feb 2025 21:01:01 +0100 Subject: [PATCH] some bug fixes --- lib/src/app.dart | 192 +++++++++--------- lib/src/components/connection_state.dart | 13 +- lib/src/model/messages_model.dart | 4 +- lib/src/providers/api/server_messages.dart | 5 +- .../providers/messages_change_provider.dart | 2 +- .../camera_to_share/share_image_view.dart | 1 + lib/src/views/home_view.dart | 12 +- 7 files changed, 123 insertions(+), 106 deletions(-) diff --git a/lib/src/app.dart b/lib/src/app.dart index 9bc7514..aeb9022 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter_foreground_task/flutter_foreground_task.dart'; import 'package:provider/provider.dart'; import 'package:twonly/globals.dart'; +import 'package:twonly/src/components/connection_state.dart'; import 'package:twonly/src/providers/contacts_change_provider.dart'; import 'package:twonly/src/providers/download_change_provider.dart'; import 'package:twonly/src/providers/messages_change_provider.dart'; @@ -22,10 +23,11 @@ import 'dart:async'; // this callback is called by the apiProvider Function(bool) globalCallbackConnectionState = (a) {}; +bool globalIsAppInBackground = true; // these two callbacks are called on updated to the corresponding database Function globalCallBackOnContactChange = () {}; -Function(int) globalCallBackOnMessageChange = (a) {}; +Future Function(int) globalCallBackOnMessageChange = (a) async {}; Function(List, bool) globalCallBackOnDownloadChange = (a, b) {}; /// The Widget that configures your application. @@ -37,18 +39,14 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State with WidgetsBindingObserver { - Future _isUserCreated = isUserCreated(); - bool _showOnboarding = true; bool _isConnected = false; - int redColorOpacity = 0; // Start with dark red - bool redColorGoUp = true; - bool isConnected = false; + bool wasPaused = false; @override void initState() { super.initState(); + globalIsAppInBackground = false; WidgetsBinding.instance.addObserver(this); - _startColorAnimation(); // init change provider to load data from the database context.read().update(); @@ -69,17 +67,23 @@ class _MyAppState extends State with WidgetsBindingObserver { context.read().update(token, add); }; - globalCallBackOnMessageChange = (userId) { - context.read().updateLastMessageFor(userId); + globalCallBackOnMessageChange = (userId) async { + await context.read().updateLastMessageFor(userId); }; - // connect async to the backend api - apiProvider.connect(); - FlutterForegroundTask.addTaskDataCallback(_onReceiveTaskData); WidgetsBinding.instance.addPostFrameCallback((_) { _requestPermissions(); _initService(); }); + initAsync(); + } + + Future initAsync() async { + // make sure the front end service will be killed + FlutterForegroundTask.sendDataToTask(""); + await FlutterForegroundTask.stopService(); + // connect async to the backend api + apiProvider.connect(); } Future _requestPermissions() async { @@ -114,17 +118,6 @@ class _MyAppState extends State with WidgetsBindingObserver { } } - void _onReceiveTaskData(Object data) { - if (data is Map) { - final dynamic timestampMillis = data["timestampMillis"]; - if (timestampMillis != null) { - final DateTime timestamp = - DateTime.fromMillisecondsSinceEpoch(timestampMillis, isUtc: true); - print('timestamp: ${timestamp.toString()}'); - } - } - } - void _initService() { FlutterForegroundTask.init( androidNotificationOptions: AndroidNotificationOptions( @@ -154,18 +147,23 @@ class _MyAppState extends State with WidgetsBindingObserver { } else { return FlutterForegroundTask.startService( serviceId: 256, - notificationTitle: 'Foreground Service is running', + notificationTitle: 'Staying connected to the server.', notificationText: 'Tap to return to the app', notificationIcon: NotificationIcon(metaDataName: "eu.twonly.service.TWONLY_LOGO"), - notificationInitialRoute: '/', + notificationInitialRoute: '/chats', callback: startCallback, ); } } Future _stopService() async { + FlutterForegroundTask.sendDataToTask(""); await FlutterForegroundTask.stopService(); + if (context.mounted) { + context.read().init(); + context.read().update(); + } if (!apiProvider.isAuthenticated) { apiProvider.connect(); } @@ -174,11 +172,14 @@ class _MyAppState extends State with WidgetsBindingObserver { @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); - print("STATE: $state"); if (state == AppLifecycleState.resumed) { - _stopService(); - //apiProvider.connect(); + if (wasPaused) { + globalIsAppInBackground = false; + _stopService(); + } } else if (state == AppLifecycleState.paused) { + wasPaused = true; + globalIsAppInBackground = true; apiProvider.close(() { _startService(); }); @@ -187,41 +188,18 @@ class _MyAppState extends State with WidgetsBindingObserver { @override void dispose() { - print("STATE: dispose"); // apiProvider.close(() {}); WidgetsBinding.instance.removeObserver(this); // disable globalCallbacks to the flutter tree globalCallbackConnectionState = (a) {}; globalCallBackOnDownloadChange = (a, b) {}; globalCallBackOnContactChange = () {}; - globalCallBackOnMessageChange = (a) {}; - FlutterForegroundTask.removeTaskDataCallback(_onReceiveTaskData); + globalCallBackOnMessageChange = (a) async {}; super.dispose(); } - void _startColorAnimation() { - // Change the color every second - Future.delayed(Duration(milliseconds: 200), () { - setState(() { - if (redColorOpacity <= 100) { - redColorGoUp = true; - } - if (redColorOpacity >= 150) { - redColorGoUp = false; - } - if (redColorGoUp) { - redColorOpacity += 10; - } else { - redColorOpacity -= 10; - } - }); - _startColorAnimation(); // Repeat the animation - }); - } - @override Widget build(BuildContext context) { - double screenWidth = MediaQuery.of(context).size.width; // var isConnected = context.watch().isConnected; // Glue the SettingsController to the MaterialApp. // @@ -256,54 +234,72 @@ class _MyAppState extends State with WidgetsBindingObserver { const InputDecorationTheme(border: OutlineInputBorder()), ), themeMode: context.watch().themeMode, - home: Stack( - children: [ - FutureBuilder( - future: _isUserCreated, - builder: (context, snapshot) { - if (snapshot.hasData) { - return snapshot.data! - ? HomeView() - : _showOnboarding - ? OnboardingView( - callbackOnSuccess: () { - setState(() { - _showOnboarding = false; - }); - }, - ) - : RegisterView( - callbackOnSuccess: () { - _isUserCreated = isUserCreated(); - setState(() {}); - }, - ); - } else { - return Container(); - } - }, - ), - if (!_isConnected) - Positioned( - top: 3, // Position it at the top - left: (screenWidth * 0.5) / 2, // Center it horizontally - child: AnimatedContainer( - duration: Duration(milliseconds: 100), - width: screenWidth * 0.5, // 50% of the screen width - decoration: BoxDecoration( - border: Border.all( - color: Colors.red[600]!.withAlpha(redColorOpacity), - width: 2.0), // Red border - borderRadius: BorderRadius.all( - Radius.circular(10.0), - ), // Rounded top corners - ), - ), - ), - ], - ), + initialRoute: '/', + routes: { + "/": (context) => + MyAppMainWidget(isConnected: _isConnected, initialPage: 0), + "/chats": (context) => + MyAppMainWidget(isConnected: _isConnected, initialPage: 1) + // home: MyAppMainWidget(isConnected: _isConnected, initialPage: 0), + }, ); }, ); } } + +class MyAppMainWidget extends StatefulWidget { + const MyAppMainWidget( + {super.key, required this.isConnected, required this.initialPage}); + + final bool isConnected; + final int initialPage; + + @override + State createState() => _MyAppMainWidgetState(); +} + +class _MyAppMainWidgetState extends State { + Future _isUserCreated = + isUserCreated(); // Assume this is a function that checks if the user is created + bool _showOnboarding = true; // Initial state for onboarding + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + FutureBuilder( + future: _isUserCreated, + builder: (context, snapshot) { + if (snapshot.hasData) { + if (snapshot.data!) { + return HomeView(initialPage: widget.initialPage); + } + + if (_showOnboarding) { + return OnboardingView( + callbackOnSuccess: () { + setState(() { + _showOnboarding = false; + }); + }, + ); + } + + return RegisterView( + callbackOnSuccess: () { + setState(() { + _isUserCreated = isUserCreated(); + }); + }, + ); + } else { + return Center(child: Container()); + } + }, + ), + if (!widget.isConnected) ConnectionInfo() + ], + ); + } +} diff --git a/lib/src/components/connection_state.dart b/lib/src/components/connection_state.dart index cae374b..7a2fcfc 100644 --- a/lib/src/components/connection_state.dart +++ b/lib/src/components/connection_state.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; class ConnectionInfo extends StatefulWidget { @@ -12,6 +14,8 @@ class _ConnectionInfoWidgetState extends State { bool redColorGoUp = true; // Direction of the opacity change double screenWidth = 0; // To hold the screen width + Timer? _colorAnimationTimer; + @override void initState() { super.initState(); @@ -20,7 +24,7 @@ class _ConnectionInfoWidgetState extends State { void _startColorAnimation() { // Change the color every 200 milliseconds - Future.delayed(Duration(milliseconds: 200), () { + _colorAnimationTimer = Timer.periodic(Duration(milliseconds: 200), (timer) { setState(() { if (redColorOpacity <= 100) { redColorGoUp = true; @@ -34,10 +38,15 @@ class _ConnectionInfoWidgetState extends State { redColorOpacity -= 10; } }); - _startColorAnimation(); // Repeat the animation }); } + @override + void dispose() { + _colorAnimationTimer?.cancel(); + super.dispose(); + } + @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; // Get the screen width diff --git a/lib/src/model/messages_model.dart b/lib/src/model/messages_model.dart index 0e246eb..b8ddcb3 100644 --- a/lib/src/model/messages_model.dart +++ b/lib/src/model/messages_model.dart @@ -181,7 +181,7 @@ class DbMessages extends CvModelBase { globalCallBackOnMessageChange(userIdFrom); return messageId; } catch (e) { - Logger("contacts_model/getUsers").shout("$e"); + Logger("messsage_model/insertMyMessage").shout("$e"); return null; } } @@ -202,7 +202,7 @@ class DbMessages extends CvModelBase { globalCallBackOnMessageChange(userIdFrom); return messageId; } catch (e) { - Logger("contacts_model/getUsers").shout("$e"); + Logger("messsage_model/insertOtherMessage").shout("$e"); return null; } } diff --git a/lib/src/providers/api/server_messages.dart b/lib/src/providers/api/server_messages.dart index befdf16..92f59de 100644 --- a/lib/src/providers/api/server_messages.dart +++ b/lib/src/providers/api/server_messages.dart @@ -105,7 +105,7 @@ Future handleDownloadData(DownloadData data) async { } box.delete(boxId); - globalCallBackOnMessageChange(fromUserId); + await globalCallBackOnMessageChange(fromUserId); globalCallBackOnDownloadChange(data.uploadToken, false); } } else { @@ -187,6 +187,9 @@ Future handleNewMessage( List downloadToken = content.downloadToken; Box box = await getMediaStorage(); box.put("${downloadToken}_fromUserId", fromUserId.toInt()); + if (box.get("${downloadToken}_fromUserId") == null) { + debugPrint("BOX IS NOT WORKING"); + } tryDownloadMedia(messageId, downloadToken); } } diff --git a/lib/src/providers/messages_change_provider.dart b/lib/src/providers/messages_change_provider.dart index db02a89..b89e16b 100644 --- a/lib/src/providers/messages_change_provider.dart +++ b/lib/src/providers/messages_change_provider.dart @@ -14,7 +14,7 @@ class MessagesChangeProvider with ChangeNotifier, DiagnosticableTreeMixin { Map get changeCounter => _changeCounter; Map get flamesCounter => _flamesCounter; - void updateLastMessageFor(int targetUserId) async { + Future updateLastMessageFor(int targetUserId) async { DbMessage? last = await DbMessages.getLastMessagesForPreviewForUser(targetUserId); if (last != null) { diff --git a/lib/src/views/camera_to_share/share_image_view.dart b/lib/src/views/camera_to_share/share_image_view.dart index a84fe86..e30085e 100644 --- a/lib/src/views/camera_to_share/share_image_view.dart +++ b/lib/src/views/camera_to_share/share_image_view.dart @@ -123,6 +123,7 @@ class _ShareImageView extends State { } else { _selectedUserIds.remove(userId); } + setState(() {}); } @override diff --git a/lib/src/views/home_view.dart b/lib/src/views/home_view.dart index c7f4222..6a294d7 100644 --- a/lib/src/views/home_view.dart +++ b/lib/src/views/home_view.dart @@ -7,7 +7,8 @@ import 'package:flutter/material.dart'; Function(int) globalUpdateOfHomeViewPageIndex = (a) {}; class HomeView extends StatefulWidget { - const HomeView({super.key}); + const HomeView({super.key, required this.initialPage}); + final int initialPage; @override State createState() => HomeViewState(); @@ -15,11 +16,13 @@ class HomeView extends StatefulWidget { class HomeViewState extends State { int activePageIdx = 0; - final PageController homeViewPageController = PageController(initialPage: 0); + late PageController homeViewPageController; @override void initState() { super.initState(); + activePageIdx = widget.initialPage; + homeViewPageController = PageController(initialPage: widget.initialPage); globalUpdateOfHomeViewPageIndex = (index) { homeViewPageController.jumpToPage(index); setState(() { @@ -32,6 +35,7 @@ class HomeViewState extends State { void dispose() { // disable globalCallbacks to the flutter tree globalUpdateOfHomeViewPageIndex = (a) {}; + //homeViewPageController.dispose(); super.dispose(); } @@ -64,6 +68,7 @@ class HomeViewState extends State { controller: homeViewPageController, onPageChanged: (index) { activePageIdx = index; + setState(() {}); }, children: [ CameraPreviewViewPermission(), @@ -73,6 +78,9 @@ class HomeViewState extends State { bottomNavigationBar: BottomNavigationBar( showSelectedLabels: false, showUnselectedLabels: false, + unselectedIconTheme: IconThemeData( + color: + Theme.of(context).colorScheme.inverseSurface.withAlpha(150)), selectedIconTheme: IconThemeData( color: Theme.of(context).colorScheme.inverseSurface), items: [