twonly-app/lib/app.dart

197 lines
6 KiB
Dart
Raw Normal View History

2025-07-14 22:38:23 +00:00
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
2025-01-25 12:17:10 +00:00
import 'package:provider/provider.dart';
2025-02-09 17:02:52 +00:00
import 'package:twonly/globals.dart';
2025-04-21 19:32:18 +00:00
import 'package:twonly/src/localization/generated/app_localizations.dart';
2025-05-30 15:24:02 +00:00
import 'package:twonly/src/providers/connection.provider.dart';
import 'package:twonly/src/providers/settings.provider.dart';
2025-06-19 06:48:34 +00:00
import 'package:twonly/src/services/api/media_upload.dart';
2025-01-24 14:25:44 +00:00
import 'package:twonly/src/utils/storage.dart';
2025-07-14 22:38:23 +00:00
import 'package:twonly/src/views/components/app_outdated.dart';
2025-05-30 15:24:02 +00:00
import 'package:twonly/src/views/home.view.dart';
2025-07-14 22:38:23 +00:00
import 'package:twonly/src/views/onboarding/onboarding.view.dart';
2025-06-19 08:22:16 +00:00
import 'package:twonly/src/views/onboarding/register.view.dart';
2024-12-31 08:50:53 +00:00
2025-04-21 19:32:18 +00:00
class App extends StatefulWidget {
const App({super.key});
2024-12-31 08:50:53 +00:00
@override
2025-04-21 19:32:18 +00:00
State<App> createState() => _AppState();
2024-12-31 08:50:53 +00:00
}
2025-04-21 19:32:18 +00:00
class _AppState extends State<App> with WidgetsBindingObserver {
2025-02-09 20:01:01 +00:00
bool wasPaused = false;
2025-01-20 23:30:57 +00:00
@override
2025-10-14 12:16:55 +00:00
Future<void> initState() async {
2025-01-20 23:30:57 +00:00
super.initState();
2025-02-09 20:01:01 +00:00
globalIsAppInBackground = false;
2025-02-08 22:07:36 +00:00
WidgetsBinding.instance.addObserver(this);
2025-01-25 12:17:10 +00:00
2025-10-14 12:16:55 +00:00
globalCallbackConnectionState = ({required bool isConnected}) async {
await context
.read<CustomChangeProvider>()
.updateConnectionState(isConnected);
await setUserPlan();
2025-01-27 00:43:52 +00:00
};
2025-10-14 12:16:55 +00:00
globalCallbackUpdatePlan = (String planId) async {
await context.read<CustomChangeProvider>().updatePlan(planId);
2025-07-18 12:22:34 +00:00
};
2025-10-14 12:16:55 +00:00
await initAsync();
2025-02-09 20:01:01 +00:00
}
2025-07-14 22:38:23 +00:00
Future<void> setUserPlan() async {
2025-05-07 21:39:39 +00:00
final user = await getUser();
2025-05-11 18:37:22 +00:00
globalBestFriendUserId = -1;
2025-05-30 16:04:04 +00:00
if (user != null && mounted) {
2025-05-11 18:37:22 +00:00
if (user.myBestFriendContactId != null) {
2025-05-30 15:24:02 +00:00
final contact = await twonlyDB.contactsDao
2025-05-11 18:37:22 +00:00
.getContactByUserId(user.myBestFriendContactId!)
.getSingleOrNull();
if (contact != null) {
if (contact.alsoBestFriend) {
globalBestFriendUserId = user.myBestFriendContactId ?? 0;
}
}
}
2025-05-30 16:04:04 +00:00
if (mounted) {
2025-07-14 22:38:23 +00:00
await context
.read<CustomChangeProvider>()
.updatePlan(user.subscriptionPlan);
2025-05-30 16:04:04 +00:00
}
2025-05-07 21:39:39 +00:00
}
2025-02-09 17:02:52 +00:00
}
2025-07-14 22:38:23 +00:00
Future<void> initAsync() async {
await setUserPlan();
2025-06-05 14:07:39 +00:00
await apiService.connect(force: true);
2025-07-14 22:38:23 +00:00
await apiService.listenToNetworkChanges();
2025-06-03 13:19:35 +00:00
// call this function so invalid media files are get purged
2025-07-14 22:38:23 +00:00
await retryMediaUpload(true);
2025-05-08 19:37:14 +00:00
}
2025-02-08 22:07:36 +00:00
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed) {
2025-02-09 20:01:01 +00:00
if (wasPaused) {
globalIsAppInBackground = false;
2025-05-30 15:24:02 +00:00
twonlyDB.markUpdated();
2025-10-14 12:16:55 +00:00
unawaited(apiService.connect(force: true));
2025-02-09 20:01:01 +00:00
}
2025-02-09 17:02:52 +00:00
} else if (state == AppLifecycleState.paused) {
2025-02-09 20:01:01 +00:00
wasPaused = true;
globalIsAppInBackground = true;
2025-10-14 12:16:55 +00:00
unawaited(handleUploadWhenAppGoesBackground());
2025-02-08 22:07:36 +00:00
}
}
2025-01-27 19:30:41 +00:00
@override
void dispose() {
2025-02-08 22:07:36 +00:00
WidgetsBinding.instance.removeObserver(this);
2025-07-14 22:38:23 +00:00
globalCallbackConnectionState = ({required bool isConnected}) {};
2025-07-18 12:22:34 +00:00
globalCallbackUpdatePlan = (String planId) {};
2025-01-27 19:30:41 +00:00
super.dispose();
}
2024-12-31 08:50:53 +00:00
@override
Widget build(BuildContext context) {
return ListenableBuilder(
2025-02-02 22:19:43 +00:00
listenable: context.watch<SettingsChangeProvider>(),
2024-12-31 08:50:53 +00:00
builder: (BuildContext context, Widget? child) {
return MaterialApp(
restorationScopeId: 'app',
2025-01-19 22:03:37 +00:00
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
2024-12-31 08:50:53 +00:00
supportedLocales: const [
2025-01-19 22:03:37 +00:00
Locale('en', ''),
Locale('de', ''),
2024-12-31 08:50:53 +00:00
],
2025-07-15 11:38:38 +00:00
onGenerateTitle: (BuildContext context) => 'twonly',
2024-12-31 08:50:53 +00:00
theme: ThemeData(
2025-06-15 21:51:17 +00:00
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF57CC99),
),
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: PredictiveBackPageTransitionsBuilder(),
},
),
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
),
),
2024-12-31 08:50:53 +00:00
darkTheme: ThemeData.dark().copyWith(
colorScheme: ColorScheme.fromSeed(
2025-04-21 19:32:18 +00:00
brightness: Brightness.dark,
2025-03-22 16:50:15 +00:00
seedColor: const Color(0xFF57CC99),
),
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
),
2024-12-31 08:50:53 +00:00
),
2025-02-02 22:19:43 +00:00
themeMode: context.watch<SettingsChangeProvider>().themeMode,
2025-02-09 20:01:01 +00:00
initialRoute: '/',
routes: {
2025-07-15 11:38:38 +00:00
'/': (context) => const AppMainWidget(initialPage: 1),
2025-10-14 12:16:55 +00:00
'/chats': (context) => const AppMainWidget(initialPage: 0),
2025-02-09 20:01:01 +00:00
},
2024-12-31 08:50:53 +00:00
);
},
);
}
}
2025-02-09 20:01:01 +00:00
2025-04-21 19:32:18 +00:00
class AppMainWidget extends StatefulWidget {
2025-07-14 22:38:23 +00:00
const AppMainWidget({
required this.initialPage,
2025-07-15 11:38:38 +00:00
super.key,
2025-07-14 22:38:23 +00:00
});
2025-02-09 20:01:01 +00:00
final int initialPage;
@override
2025-04-21 19:32:18 +00:00
State<AppMainWidget> createState() => _AppMainWidgetState();
2025-02-09 20:01:01 +00:00
}
2025-04-21 19:32:18 +00:00
class _AppMainWidgetState extends State<AppMainWidget> {
Future<bool> userCreated = isUserCreated();
2025-07-14 22:38:23 +00:00
bool showOnboarding = true;
2025-02-09 20:01:01 +00:00
@override
Widget build(BuildContext context) {
return Stack(
children: [
FutureBuilder<bool>(
2025-04-21 19:32:18 +00:00
future: userCreated,
2025-02-09 20:01:01 +00:00
builder: (context, snapshot) {
2025-04-21 19:32:18 +00:00
if (!snapshot.hasData) {
2025-02-09 20:01:01 +00:00
return Center(child: Container());
2025-07-14 22:38:23 +00:00
} else if (snapshot.data!) {
2025-04-21 19:32:18 +00:00
return HomeView(
initialPage: widget.initialPage,
);
2025-07-14 22:38:23 +00:00
} else if (showOnboarding) {
return OnboardingView(
callbackOnSuccess: () => setState(() {
showOnboarding = false;
}),
);
2025-04-21 19:32:18 +00:00
}
2025-07-14 22:38:23 +00:00
return RegisterView(
callbackOnSuccess: () => setState(() {
userCreated = isUserCreated();
}),
);
2025-02-09 20:01:01 +00:00
},
),
2025-07-15 11:38:38 +00:00
const AppOutdated(),
2025-02-09 20:01:01 +00:00
],
);
}
}