tasq/lib/main.dart

108 lines
3.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'app.dart';
import 'providers/notifications_provider.dart';
import 'utils/app_time.dart';
import 'utils/notification_permission.dart';
import 'services/notification_service.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: '.env');
AppTime.initialize(location: 'Asia/Manila');
final supabaseUrl = dotenv.env['SUPABASE_URL'] ?? '';
final supabaseAnonKey = dotenv.env['SUPABASE_ANON_KEY'] ?? '';
if (supabaseUrl.isEmpty || supabaseAnonKey.isEmpty) {
runApp(const _MissingConfigApp());
return;
}
await Supabase.initialize(url: supabaseUrl, anonKey: supabaseAnonKey);
// on Android 13+ we must request POST_NOTIFICATIONS at runtime; without it
// notifications are automatically denied and cannot be reenabled from the
// system settings. The helper uses `permission_handler`.
final granted = await ensureNotificationPermission();
if (!granted) {
// we dont block startup, but its worth logging so developers notice. A
// real app might show a dialog pointing the user to settings.
// debugPrint('notification permission not granted');
}
// initialize the local notifications plugin so we can post alerts later
await NotificationService.initialize(
onDidReceiveNotificationResponse: (response) {
// handle user tapping a notification; the payload format is up to us
final payload = response.payload;
if (payload != null && payload.startsWith('ticket:')) {
// ignore if context not mounted; we might use a navigator key in real
// app, but keep this simple for now
// TODO: navigate to ticket/task as appropriate
}
},
);
runApp(
ProviderScope(
observers: [NotificationSoundObserver()],
child: const TasqApp(),
),
);
}
class NotificationSoundObserver extends ProviderObserver {
static final AudioPlayer _player = AudioPlayer();
@override
void didUpdateProvider(
ProviderBase provider,
Object? previousValue,
Object? newValue,
ProviderContainer container,
) {
if (provider == unreadNotificationsCountProvider) {
final prev = previousValue as int?;
final next = newValue as int?;
if (prev != null && next != null && next > prev) {
_player.play(AssetSource('tasq_notification.wav'));
// also post a system notification so the user sees it outside the app
NotificationService.show(
id: DateTime.now().millisecondsSinceEpoch ~/ 1000,
title: 'New notifications',
body: 'You have $next unread notifications.',
);
}
}
}
}
class _MissingConfigApp extends StatelessWidget {
const _MissingConfigApp();
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Padding(
padding: EdgeInsets.all(24),
child: Text(
'Missing SUPABASE_URL or SUPABASE_ANON_KEY. '
'Provide them in the .env file.',
textAlign: TextAlign.center,
),
),
),
),
);
}
}