mirror of
https://github.com/localsend/localsend.git
synced 2026-04-29 03:00:23 -04:00
refactor: send tab and logging
This commit is contained in:
@@ -3,7 +3,7 @@ import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:localsend_app/pages/debug/multicast_debug_page.dart';
|
||||
import 'package:localsend_app/pages/debug/discovery_debug_page.dart';
|
||||
import 'package:localsend_app/provider/app_arguments_provider.dart';
|
||||
import 'package:localsend_app/provider/fingerprint_provider.dart';
|
||||
import 'package:localsend_app/widget/copyable_text.dart';
|
||||
@@ -48,9 +48,9 @@ class DebugPage extends ConsumerWidget {
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await context.push(() => const MulticastDebugPage());
|
||||
await context.push(() => const DiscoveryDebugPage());
|
||||
},
|
||||
child: const Text('Multicast'),
|
||||
child: const Text('Discovery Debugging'),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:localsend_app/provider/multicast_logs_provider.dart';
|
||||
import 'package:localsend_app/provider/discovery_logs_provider.dart';
|
||||
import 'package:localsend_app/provider/network/nearby_devices_provider.dart';
|
||||
import 'package:localsend_app/widget/copyable_text.dart';
|
||||
import 'package:localsend_app/widget/responsive_list_view.dart';
|
||||
|
||||
final _dateFormat = DateFormat.Hms();
|
||||
|
||||
class MulticastDebugPage extends ConsumerWidget {
|
||||
const MulticastDebugPage({Key? key}) : super(key: key);
|
||||
class DiscoveryDebugPage extends ConsumerWidget {
|
||||
const DiscoveryDebugPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final logs = ref.watch(multicastLogsProvider);
|
||||
final logs = ref.watch(discoveryLogsProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Multicast'),
|
||||
title: const Text('Discovery Debugging'),
|
||||
),
|
||||
body: ResponsiveListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
|
||||
@@ -29,7 +29,7 @@ class MulticastDebugPage extends ConsumerWidget {
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
ElevatedButton(
|
||||
onPressed: () => ref.read(multicastLogsProvider.notifier).clear(),
|
||||
onPressed: () => ref.read(discoveryLogsProvider.notifier).clear(),
|
||||
child: const Text('Clear'),
|
||||
),
|
||||
],
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:localsend_app/gen/strings.g.dart';
|
||||
import 'package:localsend_app/model/device.dart';
|
||||
@@ -20,12 +19,12 @@ import 'package:localsend_app/provider/selection/selected_sending_files_provider
|
||||
import 'package:localsend_app/provider/settings_provider.dart';
|
||||
import 'package:localsend_app/util/file_size_helper.dart';
|
||||
import 'package:localsend_app/util/native/file_picker.dart';
|
||||
import 'package:localsend_app/util/native/ios_network_permission_helper.dart';
|
||||
import 'package:localsend_app/util/native/platform_check.dart';
|
||||
import 'package:localsend_app/widget/big_button.dart';
|
||||
import 'package:localsend_app/widget/custom_icon_button.dart';
|
||||
import 'package:localsend_app/widget/dialogs/add_file_dialog.dart';
|
||||
import 'package:localsend_app/widget/dialogs/address_input_dialog.dart';
|
||||
import 'package:localsend_app/widget/dialogs/ios_network_permission_dialog.dart';
|
||||
import 'package:localsend_app/widget/dialogs/no_files_dialog.dart';
|
||||
import 'package:localsend_app/widget/dialogs/send_mode_help_dialog.dart';
|
||||
import 'package:localsend_app/widget/file_thumbnail.dart';
|
||||
@@ -46,37 +45,31 @@ class SendTab extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _SendTabState extends ConsumerState<SendTab> {
|
||||
static const iosCall = MethodChannel('localsend.localsend_app/iosCall');
|
||||
final options = FilePickerOption.getOptionsForPlatform();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// Automatically scan the network when visiting the scan tab
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
final devices = ref.read(nearbyDevicesProvider.select((state) => state.devices));
|
||||
if (devices.isEmpty) {
|
||||
await ref.read(scanProvider).startSmartScan().whenComplete(() async {
|
||||
if (devices.isEmpty) {
|
||||
// After the first complete scan, if devices aren't found on IOS a Network trigger is called
|
||||
if(checkPlatform([TargetPlatform.iOS])) {
|
||||
try {
|
||||
final bool granted = await iosCall.invokeMethod('triggerLocalNetworkDialog');
|
||||
if (!granted) {
|
||||
print("Local Network Permission denied");
|
||||
if(context.mounted) await context.pushBottomSheet(() => const IosLocalNetworkDialog());
|
||||
}
|
||||
} on PlatformException catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// Automatically scan the network when visiting the scan tab
|
||||
_init();
|
||||
});
|
||||
}
|
||||
|
||||
void _init() async {
|
||||
final devices = ref.read(nearbyDevicesProvider.select((state) => state.devices));
|
||||
if (devices.isEmpty) {
|
||||
await ref.read(scanProvider).startSmartScan(forceLegacy: false);
|
||||
if (devices.isEmpty) {
|
||||
// After the first complete scan, if devices aren't found on IOS a Network trigger is called
|
||||
if(checkPlatform([TargetPlatform.iOS]) && mounted) {
|
||||
checkIosNetworkPermission(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final sendMode = ref.watch(settingsProvider.select((s) => s.sendMode));
|
||||
@@ -200,7 +193,6 @@ class _SendTabState extends ConsumerState<SendTab> {
|
||||
const SizedBox(width: 10),
|
||||
_ScanButton(
|
||||
ips: networkInfo.localIps,
|
||||
onSelect: (ip) async => ref.read(scanProvider).startLegacySubnetScan(ip),
|
||||
),
|
||||
Tooltip(
|
||||
message: t.dialogs.addressInput.title,
|
||||
@@ -307,6 +299,8 @@ class _SendTabState extends ConsumerState<SendTab> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A button that opens a popup menu to select [T].
|
||||
/// This is used for the scan button and the send mode button.
|
||||
class _CircularPopupButton<T> extends StatelessWidget {
|
||||
final String tooltip;
|
||||
final PopupMenuItemBuilder<T> itemBuilder;
|
||||
@@ -344,26 +338,25 @@ class _CircularPopupButton<T> extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// The scan button that uses [_CircularPopupButton].
|
||||
class _ScanButton extends ConsumerWidget {
|
||||
final List<String> ips;
|
||||
final void Function(String ip) onSelect;
|
||||
|
||||
const _ScanButton({
|
||||
required this.ips,
|
||||
required this.onSelect,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final scanningIps = ref.watch(nearbyDevicesProvider.select((s) => s.runningIps));
|
||||
|
||||
if (ips.length <= 1) {
|
||||
if (ips.length <= ScanFacade.maxInterfaces) {
|
||||
return RotatingWidget(
|
||||
duration: const Duration(seconds: 2),
|
||||
spinning: scanningIps.isNotEmpty,
|
||||
reverse: true,
|
||||
child: CustomIconButton(
|
||||
onPressed: () => onSelect(ips.first),
|
||||
onPressed: () async => ref.read(scanProvider).startSmartScan(forceLegacy: true),
|
||||
child: const Icon(Icons.sync),
|
||||
),
|
||||
);
|
||||
@@ -371,7 +364,7 @@ class _ScanButton extends ConsumerWidget {
|
||||
|
||||
return _CircularPopupButton(
|
||||
tooltip: t.sendTab.scan,
|
||||
onSelected: (ip) => onSelect(ip),
|
||||
onSelected: (ip) async => ref.read(scanProvider).startLegacySubnetScan([ip]),
|
||||
itemBuilder: (_) {
|
||||
return [
|
||||
...ips.map(
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:localsend_app/model/log_entry.dart';
|
||||
|
||||
/// Contains the multicast logs for debugging purposes.
|
||||
final multicastLogsProvider = NotifierProvider<MulticastLogsNotifier, List<LogEntry>>(() {
|
||||
return MulticastLogsNotifier();
|
||||
/// Contains the discovery logs for debugging purposes.
|
||||
final discoveryLogsProvider = NotifierProvider<DiscoveryLogsNotifier, List<LogEntry>>(() {
|
||||
return DiscoveryLogsNotifier();
|
||||
});
|
||||
|
||||
class MulticastLogsNotifier extends Notifier<List<LogEntry>> {
|
||||
MulticastLogsNotifier();
|
||||
class DiscoveryLogsNotifier extends Notifier<List<LogEntry>> {
|
||||
DiscoveryLogsNotifier();
|
||||
|
||||
@override
|
||||
List<LogEntry> build() {
|
||||
@@ -9,8 +9,8 @@ import 'package:localsend_app/model/dto/multicast_dto.dart';
|
||||
import 'package:localsend_app/model/dto/register_dto.dart';
|
||||
import 'package:localsend_app/provider/device_info_provider.dart';
|
||||
import 'package:localsend_app/provider/dio_provider.dart';
|
||||
import 'package:localsend_app/provider/discovery_logs_provider.dart';
|
||||
import 'package:localsend_app/provider/fingerprint_provider.dart';
|
||||
import 'package:localsend_app/provider/multicast_logs_provider.dart';
|
||||
import 'package:localsend_app/provider/network/server/server_provider.dart';
|
||||
import 'package:localsend_app/provider/settings_provider.dart';
|
||||
import 'package:localsend_app/util/api_route_builder.dart';
|
||||
@@ -60,16 +60,15 @@ class MulticastService {
|
||||
final ip = datagram.address.address;
|
||||
final peer = dto.toDevice(ip, settings.port, settings.https);
|
||||
streamController.add(peer);
|
||||
_ref.read(multicastLogsProvider.notifier).addLog('Received UDP: ${dto.alias} ($ip)');
|
||||
if ((dto.announcement == true || dto.announce == true) && _ref.read(serverProvider) != null) {
|
||||
// only respond when server is running
|
||||
_answerAnnouncement(peer);
|
||||
}
|
||||
} catch (e) {
|
||||
_ref.read(multicastLogsProvider.notifier).addLog(e.toString());
|
||||
_ref.read(discoveryLogsProvider.notifier).addLog(e.toString());
|
||||
}
|
||||
});
|
||||
_ref.read(multicastLogsProvider.notifier).addLog('Bind UDP multicast port (ip: ${socket.interface.addresses.map((a) => a.address).toList()}, group: ${settings.multicastGroup}, port: ${settings.port})');
|
||||
_ref.read(discoveryLogsProvider.notifier).addLog('Bind UDP multicast port (ip: ${socket.interface.addresses.map((a) => a.address).toList()}, group: ${settings.multicastGroup}, port: ${settings.port})');
|
||||
}
|
||||
|
||||
// Tell everyone in the network that I am online
|
||||
@@ -88,13 +87,13 @@ class MulticastService {
|
||||
for (final wait in [100, 500, 2000]) {
|
||||
await sleepAsync(wait);
|
||||
|
||||
_ref.read(multicastLogsProvider.notifier).addLog('Sending announcement');
|
||||
_ref.read(discoveryLogsProvider.notifier).addLog('[ANNOUNCE/UDP]');
|
||||
for (final socket in sockets) {
|
||||
try {
|
||||
socket.socket.send(dto, InternetAddress(settings.multicastGroup), settings.port);
|
||||
socket.socket.close();
|
||||
} catch (e) {
|
||||
_ref.read(multicastLogsProvider.notifier).addLog(e.toString());
|
||||
_ref.read(discoveryLogsProvider.notifier).addLog(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,9 +109,10 @@ class MulticastService {
|
||||
ApiRoute.register.target(peer),
|
||||
data: _getRegisterDto().toJson(),
|
||||
);
|
||||
_ref.read(multicastLogsProvider.notifier).addLog('Responded to announcement of ${peer.alias} (${peer.ip}, model: ${peer.deviceModel}) via TCP successfully.');
|
||||
_ref.read(discoveryLogsProvider.notifier).addLog('[RESPONSE/TCP] Announcement of ${peer.alias} (${peer.ip}, model: ${peer.deviceModel}) via TCP');
|
||||
} catch (e) {
|
||||
// Fallback: Answer with UDP
|
||||
print('EEE: $e');
|
||||
final sockets = await _getSockets(settings.multicastGroup);
|
||||
final dto = _getMulticastDto(announcement: false);
|
||||
for (final socket in sockets) {
|
||||
@@ -120,10 +120,10 @@ class MulticastService {
|
||||
socket.socket.send(dto, InternetAddress(settings.multicastGroup), settings.port);
|
||||
socket.socket.close();
|
||||
} catch (e) {
|
||||
_ref.read(multicastLogsProvider.notifier).addLog(e.toString());
|
||||
_ref.read(discoveryLogsProvider.notifier).addLog(e.toString());
|
||||
}
|
||||
}
|
||||
_ref.read(multicastLogsProvider.notifier).addLog('Responded to announcement of ${peer.alias} (${peer.ip}, model: ${peer.deviceModel}) with UDP because TCP failed.');
|
||||
_ref.read(discoveryLogsProvider.notifier).addLog('[RESPONSE/UDP] Announcement of ${peer.alias} (${peer.ip}, model: ${peer.deviceModel}) with UDP because TCP failed');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,17 @@ import 'package:localsend_app/model/device.dart';
|
||||
import 'package:localsend_app/model/dto/info_dto.dart';
|
||||
import 'package:localsend_app/model/state/nearby_devices_state.dart';
|
||||
import 'package:localsend_app/provider/dio_provider.dart';
|
||||
import 'package:localsend_app/provider/discovery_logs_provider.dart';
|
||||
import 'package:localsend_app/provider/fingerprint_provider.dart';
|
||||
import 'package:localsend_app/provider/network/multicast_provider.dart';
|
||||
import 'package:localsend_app/util/api_route_builder.dart';
|
||||
import 'package:localsend_app/util/task_runner.dart';
|
||||
|
||||
/// This provider is responsible for:
|
||||
/// - Scanning the network for other LocalSend instances
|
||||
/// - Keeping track of all found devices (they are only stored in RAM)
|
||||
///
|
||||
/// Use [scanProvider] to have a high-level API to perform discovery operations.
|
||||
final nearbyDevicesProvider = NotifierProvider<NearbyDevicesNotifier, NearbyDevicesState>(() {
|
||||
return NearbyDevicesNotifier();
|
||||
});
|
||||
@@ -36,17 +42,23 @@ class NearbyDevicesNotifier extends Notifier<NearbyDevicesState> {
|
||||
);
|
||||
}
|
||||
|
||||
void startMulticastListener() {
|
||||
_multicastService.startListener().listen(registerDevice);
|
||||
/// Binds the UDP port and listens for incoming announcements.
|
||||
/// This should run forever as long as the app is running.
|
||||
void startMulticastListener() async {
|
||||
await for (final device in _multicastService.startListener()) {
|
||||
registerDevice(device);
|
||||
ref.read(discoveryLogsProvider.notifier).addLog('[DISCOVER/UDP] ${device.alias} (${device.ip}, model: ${device.deviceModel})');
|
||||
}
|
||||
}
|
||||
|
||||
/// It does not really "scan".
|
||||
/// It just sends an announcement which will cause a response on every other LocalSend member of the network.
|
||||
/// The responses have to be listened to by calling [startMulticastListener] first.
|
||||
void startMulticastScan() {
|
||||
unawaited(_multicastService.sendAnnouncement());
|
||||
_multicastService.sendAnnouncement(); // ignore: discarded_futures
|
||||
}
|
||||
|
||||
/// Scans one particular subnet with traditional HTTP/TCP discovery.
|
||||
/// This method awaits until the scan is finished.
|
||||
Future<void> startScan({required int port, required String localIp, required bool https}) async {
|
||||
if (state.runningIps.contains(localIp)) {
|
||||
// already running for the same localIp
|
||||
@@ -68,7 +80,7 @@ class NearbyDevicesNotifier extends Notifier<NearbyDevicesState> {
|
||||
_runners[networkInterface] = TaskRunner<Device?>(
|
||||
initialTasks: List.generate(
|
||||
ipList.length,
|
||||
(index) => () async => _doRequest(_dio, ipList[index], port, https, fingerprint),
|
||||
(index) => () async => _doRequest(ipList[index], port, https, fingerprint),
|
||||
),
|
||||
concurrency: 50,
|
||||
);
|
||||
@@ -81,23 +93,24 @@ class NearbyDevicesNotifier extends Notifier<NearbyDevicesState> {
|
||||
devices: {...state.devices}..update(device.ip, (_) => device, ifAbsent: () => device),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Device?> _doRequest(Dio dio, String currentIp, int port, bool https, String fingerprint) async {
|
||||
print('Requesting $currentIp');
|
||||
// We use the legacy route to make it less breaking for older versions
|
||||
final url = ApiRoute.info.targetRaw(currentIp, port, https, peerProtocolVersion);
|
||||
Device? device;
|
||||
try {
|
||||
final response = await dio.get(url, queryParameters: {
|
||||
'fingerprint': fingerprint,
|
||||
});
|
||||
final dto = InfoDto.fromJson(response.data);
|
||||
device = dto.toDevice(currentIp, port, https);
|
||||
} on DioError catch (_) {
|
||||
device = null;
|
||||
} catch (e) {
|
||||
device = null;
|
||||
Future<Device?> _doRequest(String currentIp, int port, bool https, String fingerprint) async {
|
||||
print('Requesting $currentIp');
|
||||
// We use the legacy route to make it less breaking for older versions
|
||||
final url = ApiRoute.info.targetRaw(currentIp, port, https, peerProtocolVersion);
|
||||
Device? device;
|
||||
try {
|
||||
final response = await _dio.get(url, queryParameters: {
|
||||
'fingerprint': fingerprint,
|
||||
});
|
||||
final dto = InfoDto.fromJson(response.data);
|
||||
device = dto.toDevice(currentIp, port, https);
|
||||
ref.read(discoveryLogsProvider.notifier).addLog('[DISCOVER/TCP] ${device.alias} (${device.ip}, model: ${device.deviceModel})');
|
||||
} on DioError catch (_) {
|
||||
device = null;
|
||||
} catch (e) {
|
||||
device = null;
|
||||
}
|
||||
return device;
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:localsend_app/provider/network/nearby_devices_provider.dart';
|
||||
import 'package:localsend_app/provider/network_info_provider.dart';
|
||||
@@ -11,29 +10,36 @@ final scanProvider = Provider((ref) => ScanFacade(ref));
|
||||
|
||||
/// Provides a simple API to make discovery operations.
|
||||
class ScanFacade {
|
||||
/// Maximum number of interfaces to scan.
|
||||
/// If there are more interfaces, the first ones will be used or the user needs to select one.
|
||||
static const maxInterfaces = 3;
|
||||
|
||||
final Ref _ref;
|
||||
|
||||
const ScanFacade(this._ref);
|
||||
|
||||
/// Scans the network via multicast first,
|
||||
/// if no devices has been found, try http-based discovery on the first subnet
|
||||
Future<void> startSmartScan() async {
|
||||
Future<void> startSmartScan({required bool forceLegacy}) async {
|
||||
// Try performant Multicast/UDP method first
|
||||
_ref.read(nearbyDevicesProvider.notifier).startMulticastScan();
|
||||
await sleepAsync(1000);
|
||||
|
||||
if (!forceLegacy) {
|
||||
await sleepAsync(1000);
|
||||
}
|
||||
|
||||
// If no devices has been found, then switch to legacy discovery mode
|
||||
// which is purely HTTP/TCP based.
|
||||
if (_ref.read(nearbyDevicesProvider).devices.isEmpty) {
|
||||
final localIp = _ref.read(networkStateProvider).localIps.firstOrNull;
|
||||
if (localIp != null) {
|
||||
await startLegacySubnetScan(localIp);
|
||||
if (forceLegacy || _ref.read(nearbyDevicesProvider).devices.isEmpty) {
|
||||
final networkInterfaces = _ref.read(networkStateProvider).localIps.take(maxInterfaces).toList();
|
||||
if (networkInterfaces.isNotEmpty) {
|
||||
await startLegacySubnetScan(networkInterfaces);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// HTTP based discovery on a single subnet.
|
||||
Future<void> startLegacySubnetScan(String localIp) async {
|
||||
/// HTTP based discovery on a fixed set of subnets.
|
||||
Future<void> startLegacySubnetScan(List<String> subnets) async {
|
||||
final settings = _ref.read(settingsProvider);
|
||||
final port = settings.port;
|
||||
final https = settings.https;
|
||||
@@ -41,6 +47,8 @@ class ScanFacade {
|
||||
// send announcement in parallel
|
||||
_ref.read(nearbyDevicesProvider.notifier).startMulticastScan();
|
||||
|
||||
await _ref.read(nearbyDevicesProvider.notifier).startScan(port: port, localIp: localIp, https: https);
|
||||
await Future.wait<void>([
|
||||
for (final subnet in subnets) _ref.read(nearbyDevicesProvider.notifier).startScan(port: port, localIp: subnet, https: https),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import 'package:localsend_app/model/state/server/receiving_file.dart';
|
||||
import 'package:localsend_app/pages/progress_page.dart';
|
||||
import 'package:localsend_app/pages/receive_page.dart';
|
||||
import 'package:localsend_app/provider/device_info_provider.dart';
|
||||
import 'package:localsend_app/provider/discovery_logs_provider.dart';
|
||||
import 'package:localsend_app/provider/network/nearby_devices_provider.dart';
|
||||
import 'package:localsend_app/provider/network/server/server_utils.dart';
|
||||
import 'package:localsend_app/provider/progress_provider.dart';
|
||||
@@ -147,6 +148,7 @@ class ReceiveController {
|
||||
|
||||
// Save device information
|
||||
server.ref.read(nearbyDevicesProvider.notifier).registerDevice(requestDto.toDevice(request.ip, port, https));
|
||||
server.ref.read(discoveryLogsProvider.notifier).addLog('[DISCOVER/TCP] Received "/register" HTTP request: ${requestDto.alias} (${request.ip})');
|
||||
|
||||
final deviceInfo = server.ref.read(deviceRawInfoProvider);
|
||||
|
||||
|
||||
20
lib/util/native/ios_network_permission_helper.dart
Normal file
20
lib/util/native/ios_network_permission_helper.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:localsend_app/widget/dialogs/ios_network_permission_dialog.dart';
|
||||
import 'package:routerino/routerino.dart';
|
||||
|
||||
const iosCall = MethodChannel('localsend.localsend_app/iosCall');
|
||||
|
||||
/// Checks if local network permission is granted on iOS.
|
||||
/// If not, a dialog will be shown.
|
||||
void checkIosNetworkPermission(BuildContext context) async {
|
||||
try {
|
||||
final bool granted = await iosCall.invokeMethod('triggerLocalNetworkDialog');
|
||||
if (!granted) {
|
||||
print("Local Network Permission denied");
|
||||
if(context.mounted) await context.pushBottomSheet(() => const IosLocalNetworkDialog());
|
||||
}
|
||||
} on PlatformException catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user