Firebase push notification

This commit is contained in:
LittleSheep 2024-06-07 00:00:28 +08:00
parent 0b8daad945
commit 0d179f6544
6 changed files with 105 additions and 2 deletions

View File

@ -2,6 +2,8 @@ PODS:
- connectivity_plus (0.0.1): - connectivity_plus (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- device_info (0.0.1):
- Flutter
- device_info_plus (0.0.1): - device_info_plus (0.0.1):
- Flutter - Flutter
- DKImagePickerController/Core (4.3.9): - DKImagePickerController/Core (4.3.9):
@ -124,6 +126,8 @@ PODS:
- FlutterMacOS - FlutterMacOS
- permission_handler_apple (9.3.0): - permission_handler_apple (9.3.0):
- Flutter - Flutter
- platform_device_id (0.0.1):
- Flutter
- PromisesObjC (2.4.0) - PromisesObjC (2.4.0)
- SDWebImage (5.19.2): - SDWebImage (5.19.2):
- SDWebImage/Core (= 5.19.2) - SDWebImage/Core (= 5.19.2)
@ -148,6 +152,7 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
- device_info (from `.symlinks/plugins/device_info/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
@ -161,6 +166,7 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- platform_device_id (from `.symlinks/plugins/platform_device_id/ios`)
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`) - sqflite (from `.symlinks/plugins/sqflite/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@ -188,6 +194,8 @@ SPEC REPOS:
EXTERNAL SOURCES: EXTERNAL SOURCES:
connectivity_plus: connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/darwin" :path: ".symlinks/plugins/connectivity_plus/darwin"
device_info:
:path: ".symlinks/plugins/device_info/ios"
device_info_plus: device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios" :path: ".symlinks/plugins/device_info_plus/ios"
file_picker: file_picker:
@ -214,6 +222,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin" :path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple: permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios" :path: ".symlinks/plugins/permission_handler_apple/ios"
platform_device_id:
:path: ".symlinks/plugins/platform_device_id/ios"
sentry_flutter: sentry_flutter:
:path: ".symlinks/plugins/sentry_flutter/ios" :path: ".symlinks/plugins/sentry_flutter/ios"
sqflite: sqflite:
@ -227,6 +237,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
device_info: d7d233b645a32c40dfdc212de5cf646ca482f175
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
@ -250,6 +261,7 @@ SPEC CHECKSUMS:
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a
Sentry: 51b056d96914a741f63eca774d118678b1eb05a1 Sentry: 51b056d96914a741f63eca774d118678b1eb05a1

View File

@ -173,11 +173,12 @@ class AccountProvider extends GetxController {
if (!await auth.isAuthorized) throw Exception('unauthorized'); if (!await auth.isAuthorized) throw Exception('unauthorized');
final deviceUuid = await PlatformDeviceId.getDeviceId; final deviceUuid = await PlatformDeviceId.getDeviceId;
final token = await FirebaseMessaging.instance.setAutoInitEnabled(true); final token = await FirebaseMessaging.instance.getToken();
// TODO On iOS/macOS, using getAPNSToken() instead.
final client = auth.configureClient(service: 'passport'); final client = auth.configureClient(service: 'passport');
final resp = await client.post('/api/notifications/subtribe', { final resp = await client.post('/api/notifications/subscribe', {
'provider': 'firebase', 'provider': 'firebase',
'device_token': token, 'device_token': token,
'device_id': deviceUuid, 'device_id': deviceUuid,

View File

@ -6,6 +6,7 @@ import 'package:solian/router.dart';
import 'package:solian/screens/auth/signin.dart'; import 'package:solian/screens/auth/signin.dart';
import 'package:solian/screens/auth/signup.dart'; import 'package:solian/screens/auth/signup.dart';
import 'package:solian/widgets/account/account_heading.dart'; import 'package:solian/widgets/account/account_heading.dart';
import 'package:solian/widgets/account/push_notify_register_dialog.dart';
class AccountScreen extends StatefulWidget { class AccountScreen extends StatefulWidget {
const AccountScreen({super.key}); const AccountScreen({super.key});
@ -101,6 +102,31 @@ class _AccountScreenState extends State<AccountScreen> {
setState(() {}); setState(() {});
}, },
), ),
const Divider(thickness: 0.3, height: 0.3)
.paddingSymmetric(vertical: 16),
Wrap(
spacing: 4,
children: [
InkWell(
child: Text(
'pushNotifyRegisterAction'.tr,
style: TextStyle(
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.85),
),
),
onTap: () {
showDialog(
context: context,
builder: (context) =>
const PushNotifyRegisterDialog(),
);
},
)
],
)
], ],
); );
}, },

View File

@ -3,6 +3,7 @@ import 'package:get/get.dart';
import 'package:solian/exts.dart'; import 'package:solian/exts.dart';
import 'package:solian/providers/auth.dart'; import 'package:solian/providers/auth.dart';
import 'package:solian/services.dart'; import 'package:solian/services.dart';
import 'package:solian/widgets/account/push_notify_register_dialog.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
class SignInPopup extends StatefulWidget { class SignInPopup extends StatefulWidget {
@ -23,6 +24,11 @@ class _SignInPopupState extends State<SignInPopup> {
final password = _passwordController.value.text; final password = _passwordController.value.text;
if (username.isEmpty || password.isEmpty) return; if (username.isEmpty || password.isEmpty) return;
provider.signin(context, username, password).then((_) async { provider.signin(context, username, password).then((_) async {
await showDialog(
context: context,
builder: (context) => const PushNotifyRegisterDialog(),
);
Navigator.pop(context, true); Navigator.pop(context, true);
}).catchError((e) { }).catchError((e) {
List<String> messages = e.toString().split('\n'); List<String> messages = e.toString().split('\n');

View File

@ -193,6 +193,11 @@ class SolianMessages extends Translations {
'badgeSolsynthStaff': 'Solsynth Staff', 'badgeSolsynthStaff': 'Solsynth Staff',
'badgeSolarOriginalCitizen': 'Solar Network Natives', 'badgeSolarOriginalCitizen': 'Solar Network Natives',
'badgeGreatCommunityContributor': 'Great Community Contributor', 'badgeGreatCommunityContributor': 'Great Community Contributor',
'pushNotifyRegisterAction': 'Enable Push Notifications',
'pushNotifyRegister': 'Register Push Notification Device',
'pushNotifyRegisterCaption':
'Activating push notifications allows you to get our latest notifications even when the app is completely closed. We use Apple\'s official push service on iOS/macOS devices; other devices provide push notifications through Google Firebase. To register a device for push notifications, you may need to connect to Google\'s servers and install the Google Framework on your device.',
'pushNotifyRegisterDone': 'Push Notifications has been activated.',
}, },
'zh_CN': { 'zh_CN': {
'hide': '隐藏', 'hide': '隐藏',
@ -372,6 +377,11 @@ class SolianMessages extends Translations {
'badgeSolsynthStaff': 'Solsynth 工作人员', 'badgeSolsynthStaff': 'Solsynth 工作人员',
'badgeSolarOriginalCitizen': 'Solar Network 原住民', 'badgeSolarOriginalCitizen': 'Solar Network 原住民',
'badgeGreatCommunityContributor': '优秀社区贡献者', 'badgeGreatCommunityContributor': '优秀社区贡献者',
'pushNotifyRegisterAction': '激活推送通知',
'pushNotifyRegister': '注册推送通知设备',
'pushNotifyRegisterCaption':
'激活推送通知便可以让你在应用程序完全关闭的时候仍然获取到我们最新的通知。在 iOS/macOS 设备上我们使用 Apple 官方的推送服务;其他设备则通过 Google Firebase 提供推送通知。要注册推送通知设备,您可能需要连接到 Google 的服务器(在中国大陆不可用)并在您的设备上安装 Google Framework。',
'pushNotifyRegisterDone': '推送通知已成功激活',
} }
}; };
} }

View File

@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:solian/exts.dart';
import 'package:solian/providers/account.dart';
class PushNotifyRegisterDialog extends StatefulWidget {
const PushNotifyRegisterDialog({super.key});
@override
State<PushNotifyRegisterDialog> createState() =>
_PushNotifyRegisterDialogState();
}
class _PushNotifyRegisterDialogState extends State<PushNotifyRegisterDialog> {
bool _isBusy = false;
void performAction() async {
setState(() => _isBusy = true);
try {
await Get.find<AccountProvider>().registerPushNotifications();
context.showSnackbar('pushNotifyRegisterDone'.tr);
Navigator.pop(context);
} catch (e) {
context.showErrorDialog(e);
}
setState(() => _isBusy = false);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('pushNotifyRegister'.tr),
content: Text('pushNotifyRegisterCaption'.tr),
actions: [
TextButton(
onPressed: _isBusy ? null : () => Navigator.pop(context),
child: Text('cancel'.tr),
),
TextButton(
onPressed: _isBusy ? null : performAction,
child: Text('confirm'.tr),
),
],
);
}
}