Files
jinzhi/lib/common/push/device_push_registrar.dart
T

67 lines
2.3 KiB
Dart

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../../models/pb/jinzhi/v1/push.pb.dart' as pb;
import '../network/api_exception.dart';
import 'notification_permission.dart';
import 'push_api_client.dart';
import 'umeng_push_service.dart';
class DevicePushRegistrar {
DevicePushRegistrar(this._read);
final T Function<T>(ProviderListenable<T>) _read;
StreamSubscription<String>? _subscription;
String? _lastToken;
bool? _lastPermission;
Future<void> _registrationQueue = Future.value();
void start() {
_subscription ??= UmengPushService.onDeviceToken.listen(_enqueue);
UmengPushService.emitCachedToken();
}
Future<void> stop() async => _subscription?.cancel();
Future<void> refresh() => UmengPushService.refreshDeviceToken();
void forceRegisterCurrentToken() {
// BLOCKED(server contract): guest registration is intentionally preserved.
// Do not add a client-side unbind call until the backend confirms whether
// guest registration reliably clears the previous user binding.
_lastToken = null;
_lastPermission = null;
UmengPushService.emitCachedToken();
}
void _enqueue(String token) {
_registrationQueue = _registrationQueue
.catchError((_) {})
.then((_) => _register(token));
}
Future<void> _register(String token) async {
final permission = await NotificationPermission.isGranted();
if (token == _lastToken && permission == _lastPermission) return;
final package = await PackageInfo.fromPlatform();
final request = pb.RegisterPushDeviceRequest(
umengDeviceToken: token,
platform: defaultTargetPlatform == TargetPlatform.iOS
? pb.Platform.PLATFORM_IOS
: pb.Platform.PLATFORM_ANDROID,
appVersion: package.version,
permissionGranted: permission,
);
for (var attempt = 0; attempt < 4; attempt++) {
try {
await _read(pushApiClientProvider).registerDevice(request);
_lastToken = token;
_lastPermission = permission;
return;
} catch (error) {
if (attempt == 3 || !ApiException.from(error).isRetryable) return;
await Future<void>.delayed(Duration(seconds: 1 << attempt));
}
}
}
}