mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-16 05:08:41 +00:00
some bug fixes
This commit is contained in:
parent
84448f79ca
commit
20a8f0faa3
7 changed files with 123 additions and 106 deletions
192
lib/src/app.dart
192
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<int>, bool) globalCallBackOnDownloadChange = (a, b) {};
|
||||
|
||||
/// The Widget that configures your application.
|
||||
|
|
@ -37,18 +39,14 @@ class MyApp extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
||||
Future<bool> _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<ContactChangeProvider>().update();
|
||||
|
|
@ -69,17 +67,23 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||
context.read<DownloadChangeProvider>().update(token, add);
|
||||
};
|
||||
|
||||
globalCallBackOnMessageChange = (userId) {
|
||||
context.read<MessagesChangeProvider>().updateLastMessageFor(userId);
|
||||
globalCallBackOnMessageChange = (userId) async {
|
||||
await context.read<MessagesChangeProvider>().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<void> _requestPermissions() async {
|
||||
|
|
@ -114,17 +118,6 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||
}
|
||||
}
|
||||
|
||||
void _onReceiveTaskData(Object data) {
|
||||
if (data is Map<String, dynamic>) {
|
||||
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<MyApp> 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<MessagesChangeProvider>().init();
|
||||
context.read<ContactChangeProvider>().update();
|
||||
}
|
||||
if (!apiProvider.isAuthenticated) {
|
||||
apiProvider.connect();
|
||||
}
|
||||
|
|
@ -174,11 +172,14 @@ class _MyAppState extends State<MyApp> 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<MyApp> 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<ApiProvider>().isConnected;
|
||||
// Glue the SettingsController to the MaterialApp.
|
||||
//
|
||||
|
|
@ -256,54 +234,72 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||
const InputDecorationTheme(border: OutlineInputBorder()),
|
||||
),
|
||||
themeMode: context.watch<SettingsChangeProvider>().themeMode,
|
||||
home: Stack(
|
||||
children: [
|
||||
FutureBuilder<bool>(
|
||||
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<MyAppMainWidget> createState() => _MyAppMainWidgetState();
|
||||
}
|
||||
|
||||
class _MyAppMainWidgetState extends State<MyAppMainWidget> {
|
||||
Future<bool> _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<bool>(
|
||||
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()
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ConnectionInfo extends StatefulWidget {
|
||||
|
|
@ -12,6 +14,8 @@ class _ConnectionInfoWidgetState extends State<ConnectionInfo> {
|
|||
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<ConnectionInfo> {
|
|||
|
||||
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<ConnectionInfo> {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
|
|||
}
|
||||
|
||||
box.delete(boxId);
|
||||
globalCallBackOnMessageChange(fromUserId);
|
||||
await globalCallBackOnMessageChange(fromUserId);
|
||||
globalCallBackOnDownloadChange(data.uploadToken, false);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -187,6 +187,9 @@ Future<client.Response> handleNewMessage(
|
|||
List<int> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class MessagesChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
|
|||
Map<int, int> get changeCounter => _changeCounter;
|
||||
Map<int, int> get flamesCounter => _flamesCounter;
|
||||
|
||||
void updateLastMessageFor(int targetUserId) async {
|
||||
Future updateLastMessageFor(int targetUserId) async {
|
||||
DbMessage? last =
|
||||
await DbMessages.getLastMessagesForPreviewForUser(targetUserId);
|
||||
if (last != null) {
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ class _ShareImageView extends State<ShareImageView> {
|
|||
} else {
|
||||
_selectedUserIds.remove(userId);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -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<HomeView> createState() => HomeViewState();
|
||||
|
|
@ -15,11 +16,13 @@ class HomeView extends StatefulWidget {
|
|||
|
||||
class HomeViewState extends State<HomeView> {
|
||||
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<HomeView> {
|
|||
void dispose() {
|
||||
// disable globalCallbacks to the flutter tree
|
||||
globalUpdateOfHomeViewPageIndex = (a) {};
|
||||
//homeViewPageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +68,7 @@ class HomeViewState extends State<HomeView> {
|
|||
controller: homeViewPageController,
|
||||
onPageChanged: (index) {
|
||||
activePageIdx = index;
|
||||
setState(() {});
|
||||
},
|
||||
children: [
|
||||
CameraPreviewViewPermission(),
|
||||
|
|
@ -73,6 +78,9 @@ class HomeViewState extends State<HomeView> {
|
|||
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: [
|
||||
|
|
|
|||
Loading…
Reference in a new issue