✨ Firebase push notification
This commit is contained in:
		@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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(),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                      },
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                  ],
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
              ],
 | 
					              ],
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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': '推送通知已成功激活',
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										48
									
								
								lib/widgets/account/push_notify_register_dialog.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								lib/widgets/account/push_notify_register_dialog.dart
									
									
									
									
									
										Normal 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),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user