Files
plezy/lib/screens/settings/tracker_connect_launcher.dart
T

68 lines
2.4 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../focus/input_mode_tracker.dart';
import '../../i18n/strings.g.dart';
import '../../utils/app_logger.dart';
import '../../utils/dialogs.dart';
import '../../utils/snackbar_helper.dart';
/// Shared "connect this tracker" launcher.
///
/// Handles the busy/already-connected guard, shows the service's code dialog
/// once `connect` hands us a payload, auto-launches the browser on pointer
/// platforms, closes the dialog when the flow resolves, and surfaces a failure
/// snack. Service-specific pieces are supplied via [connect], [buildDialog],
/// and [urlFor] so both `TrackersProvider`-backed and `TraktAccountProvider`-
/// backed flows share one code path.
Future<void> launchTrackerConnect<T>(
BuildContext context, {
required bool isBusyOrConnected,
required String serviceName,
required Future<bool> Function(void Function(T)) connect,
required VoidCallback onCancel,
required Widget Function(T payload, VoidCallback onCancel) buildDialog,
required String Function(T payload) urlFor,
}) async {
if (isBusyOrConnected) return;
final autoLaunchBrowser = !InputModeTracker.isKeyboardMode(context);
var dialogOpen = false;
final ok = await connect((payload) {
if (!context.mounted) return;
dialogOpen = true;
showScopedDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => buildDialog(payload, () {
// Flip synchronously so the post-await guard below is a no-op —
// `whenComplete` fires a microtask later and loses the race otherwise.
dialogOpen = false;
onCancel();
}),
).whenComplete(() => dialogOpen = false);
if (autoLaunchBrowser) {
unawaited(
launchUrl(Uri.parse(urlFor(payload)), mode: LaunchMode.externalApplication).catchError((Object e) {
appLogger.d('$serviceName: failed to auto-launch browser', error: e);
return false;
}),
);
}
});
if (!context.mounted) return;
// Close the dialog iff we showed one and it's still up (not already closed by
// the Cancel button). This is the ONLY site that dismisses the dialog —
// popping here and having the dialog self-pop would pop the screen behind.
if (dialogOpen) {
Navigator.of(context).pop();
}
if (!ok) {
showAppSnackBar(context, t.services.connectFailed(service: serviceName));
}
}