diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 6ac97a3..9e76d61 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -85,6 +85,7 @@ android { ndkVersion = flutter.ndkVersion compileOptions { + isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } @@ -111,3 +112,7 @@ android { flutter { source = "../.." } + +dependencies { + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 450264d..b6707dc 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -53,4 +53,10 @@ + + diff --git a/lib/const.dart b/lib/const.dart index 535361b..28f9f03 100644 --- a/lib/const.dart +++ b/lib/const.dart @@ -1,6 +1,7 @@ import 'package:amberflutter/amberflutter.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:ndk/ndk.dart'; import 'package:ndk_amber/ndk_amber.dart'; import 'package:ndk_objectbox/ndk_objectbox.dart'; @@ -38,6 +39,7 @@ const searchRelays = ["wss://relay.nostr.band", "wss://search.nos.today"]; final loginData = LoginData(); final RouteObserver> routeObserver = RouteObserver>(); +final localNotifications = FlutterLocalNotificationsPlugin(); Future initLogin() async { // reload / cache login data diff --git a/lib/main.dart b/lib/main.dart index 21afeda..666dff5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,11 @@ +import 'dart:developer' as developer; + import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:zap_stream_flutter/app.dart'; import 'package:zap_stream_flutter/const.dart'; import 'package:zap_stream_flutter/i18n/strings.g.dart'; +import 'package:zap_stream_flutter/notifications.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -10,5 +13,9 @@ Future main() async { await Firebase.initializeApp(); await initLogin(); + setupNotifications().catchError((e) { + developer.log("Failed to setup notifications: $e"); + }); + runZapStream(); } diff --git a/lib/notifications.dart b/lib/notifications.dart new file mode 100644 index 0000000..50d5968 --- /dev/null +++ b/lib/notifications.dart @@ -0,0 +1,103 @@ +import 'dart:developer' as developer; +import 'dart:io'; + +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:ndk/ndk.dart'; +import 'package:zap_stream_flutter/const.dart'; +import 'package:http/http.dart' as http; + +class Notepush { + final String base; + final EventSigner signer; + + Notepush(this.base, {required this.signer}); + + Future register(String token) async { + final pubkey = signer.getPublicKey(); + final url = + "$base/user-info/$pubkey/${Uri.encodeComponent(token)}?backend=fcm"; + developer.log(url); + final auth = await _makeAuth("PUT", url); + final rsp = await http.put( + Uri.parse(url), + headers: { + "authorization": "Nostr $auth", + "accept": "application/json", + "content-type": "application/json", + }, + ); + developer.log(rsp.body); + return rsp.body; + } + + Future _makeAuth(String method, String url) async { + final pubkey = signer.getPublicKey(); + final authEvent = Nip01Event( + pubKey: pubkey, + kind: 27235, + tags: [ + ["u", url], + ["method", "PUT"], + ], + content: "", + ); + await signer.sign(authEvent); + return authEvent.toBase64(); + } +} + +Future setupNotifications() async { + final signer = ndk.accounts.getLoggedAccount()?.signer; + if (signer != null) { + final pusher = Notepush("http://10.0.2.2:8000", signer: signer); + final fbase = FirebaseMessaging.instance; + FirebaseMessaging.onMessage.listen((msg) { + developer.log(msg.notification?.body ?? ""); + final notification = msg.notification; + if (notification != null && notification.android != null) { + /*FlutterLocalNotificationsPlugin().show( + notification.hashCode, + notification.title, + notification.body, + NotificationDetails( + android: AndroidNotificationDetails( + notification.android!.channelId ?? "fcm", + "fcm", + ), + ), + );*/ + // TODO: foreground notification + } + }); + await fbase.setAutoInitEnabled(true); + await fbase.setForegroundNotificationPresentationOptions( + alert: true, + badge: true, + sound: true, + ); + await fbase.requestPermission(provisional: true); + + await localNotifications.initialize( + InitializationSettings( + android: AndroidInitializationSettings("@mipmap/ic_launcher"), + ), + ); + fbase.onTokenRefresh.listen((token) async { + developer.log("NEW TOKEN: $token"); + await pusher.register(token); + }); + + if (Platform.isIOS) { + final apnsToken = await FirebaseMessaging.instance.getAPNSToken(); + if (apnsToken == null) { + throw "APNS token not availble"; + } + } + final fcmToken = await FirebaseMessaging.instance.getToken(); + if (fcmToken == null) { + throw "Push token is null"; + } + await pusher.register(fcmToken); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 6841ae6..8a74080 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,6 +9,7 @@ import emoji_picker_flutter import file_selector_macos import firebase_core import firebase_messaging +import flutter_local_notifications import flutter_secure_storage_macos import objectbox_flutter_libs import package_info_plus @@ -25,6 +26,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) + FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) ObjectboxFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "ObjectboxFlutterLibsPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) diff --git a/pubspec.lock b/pubspec.lock index ceaab27..0e32520 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -374,6 +374,38 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: b94a50aabbe56ef254f95f3be75640f99120429f0a153b2dc30143cffc9bfdf3 + url: "https://pub.dev" + source: hosted + version: "19.2.1" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: e3c277b2daab8e36ac5a6820536668d07e83851aeeb79c446e525a70710770a5 + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "2569b973fc9d1f63a37410a9f7c1c552081226c597190cb359ef5d5762d1631c" + url: "https://pub.dev" + source: hosted + version: "9.0.0" + flutter_local_notifications_windows: + dependency: transitive + description: + name: flutter_local_notifications_windows + sha256: f8fc0652a601f83419d623c85723a3e82ad81f92b33eaa9bcc21ea1b94773e6e + url: "https://pub.dev" + source: hosted + version: "1.0.0" flutter_localizations: dependency: "direct main" description: flutter @@ -494,7 +526,7 @@ packages: source: hosted version: "0.15.6" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" @@ -1095,6 +1127,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.4" + timezone: + dependency: transitive + description: + name: timezone + sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1 + url: "https://pub.dev" + source: hosted + version: "0.10.1" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4276648..1a61d13 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,8 @@ dependencies: sdk: flutter firebase_core: ^3.13.1 firebase_messaging: ^15.2.6 + http: ^1.4.0 + flutter_local_notifications: ^19.2.1 dependency_overrides: ndk: diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 79a04d4..e1a7c66 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -13,6 +13,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + flutter_local_notifications_windows rust_lib_ndk )