sojorn/sojorn_app/lib/utils/link_handler.dart
2026-02-15 00:33:24 -06:00

97 lines
3.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import 'package:url_launcher/url_launcher.dart';
import 'external_link_controller.dart';
import '../routes/app_routes.dart';
/// Utility for safely launching URLs and links.
///
/// Provides a clean interface for opening external links from anywhere
/// in the app, with automatic scheme detection and error handling.
///
/// NOTE: For external URLs, prefer using [ExternalLinkController.handleUrl]
/// which includes safety checks against the domain whitelist.
class LinkHandler {
/// Launches a URL string, handling common edge cases.
///
/// [context] - BuildContext for showing error SnackBars
/// [url] - The URL to open (can be with or without scheme)
///
/// If the URL doesn't have a scheme (http/https), https is assumed.
/// Uses the [ExternalLinkController] for safety checks on external links.
/// Handles sojorn:// deep links by navigating to the Beacon screen.
static Future<void> launchLink(BuildContext context, String url) async {
if (url.trim().isEmpty) return;
// Handle sojorn:// deep links - navigate within app to Beacon screen
if (url.startsWith('sojorn://')) {
Uri? uri = Uri.tryParse(url.replaceFirst('sojorn://', 'sojorn://'));
// Normalize to https for query parsing if needed
uri ??=
Uri.tryParse(url.replaceFirst('sojorn://', 'https://sojorn.net/'));
final latParam = uri?.queryParameters['lat'];
final longParam = uri?.queryParameters['long'];
if (latParam != null && longParam != null) {
final lat = double.tryParse(latParam);
final long = double.tryParse(longParam);
if (lat != null && long != null) {
AppRoutes.navigateToBeacon(context, LatLng(lat, long));
return;
}
}
// If no valid coordinates, just open beacon screen at current/default
AppRoutes.navigateToBeacon(context, LatLng(37.7749, -122.4194));
return;
}
// Ensure scheme exists
final Uri? uri = Uri.tryParse(url);
final Uri effectiveUri;
if (uri != null && !uri.hasScheme) {
effectiveUri = Uri.parse('https://$url');
} else {
effectiveUri = uri ?? Uri.parse(url);
}
// Use ExternalLinkController for safety checks
await ExternalLinkController.handleUrl(context, effectiveUri.toString());
}
/// Quick launcher for safe/trusted URLs without safety prompts.
/// Use this only for URLs you know are safe.
///
/// [context] - BuildContext for showing error SnackBars
/// [url] - The URL to open
static Future<void> launchSafeUrl(BuildContext context, String url) async {
if (url.trim().isEmpty) return;
final Uri? uri = Uri.tryParse(url);
if (uri == null) return;
try {
if (await canLaunchUrl(uri)) {
await launchUrl(
uri,
mode: LaunchMode.externalApplication,
);
} else {
_showError(context, 'Could not open link.');
}
} catch (e) {
_showError(context, 'Error opening link: $e');
}
}
static void _showError(BuildContext context, String message) {
final messenger = ScaffoldMessenger.of(context);
messenger.showSnackBar(
SnackBar(content: Text(message)),
);
}
}