feat: add connect confirmation dialog

This commit is contained in:
Kingkor Roy Tirtho 2025-04-27 22:39:16 +06:00
parent 7c26d29d06
commit a06614bc5c
36 changed files with 471 additions and 42 deletions

View File

@ -426,5 +426,7 @@
"custom": "Custom", "custom": "Custom",
"add_custom_url": "Add custom URL", "add_custom_url": "Add custom URL",
"edit_port": "Edit port", "edit_port": "Edit port",
"port_helper_msg": "Default is -1 which indicates random number. If you've firewall configured, setting this is recommended." "port_helper_msg": "Default is -1 which indicates random number. If you've firewall configured, setting this is recommended.",
"connect_request": "Allow {client} to connect?",
"connection_request_denied": "Connection denied. User denied access."
} }

View File

@ -2716,6 +2716,18 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'** /// **'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'**
String get port_helper_msg; String get port_helper_msg;
/// No description provided for @connect_request.
///
/// In en, this message translates to:
/// **'Allow {client} to connect?'**
String connect_request(Object client);
/// No description provided for @connection_request_denied.
///
/// In en, this message translates to:
/// **'Connection denied. User denied access.'**
String get connection_request_denied;
} }
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> { class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsAr extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsBn extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsCa extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsCs extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsEs extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsEu extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsFa extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsFi extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsFr extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsHi extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsId extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsIt extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsJa extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsKa extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsKo extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsNe extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsNl extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsPl extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsPt extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsRu extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsTa extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsTh extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsTl extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsTr extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsUk extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsVi extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -1377,4 +1377,12 @@ class AppLocalizationsZh extends AppLocalizations {
@override @override
String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.'; String get port_helper_msg => 'Default is -1 which indicates random number. If you\'ve firewall configured, setting this is recommended.';
@override
String connect_request(Object client) {
return 'Allow $client to connect?';
}
@override
String get connection_request_denied => 'Connection denied. User denied access.';
} }

View File

@ -38,7 +38,23 @@ class SettingsPlaybackEditConnectPortDialog extends HookConsumerWidget {
validator: FormBuilderValidators.integer(radix: 10), validator: FormBuilderValidators.integer(radix: 10),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.digitsOnly, // Allow only signed integers
TextInputFormatter.withFunction(
(oldValue, newValue) {
if (newValue.text.isEmpty) {
return const TextEditingValue();
}
if (newValue.text.length == 1 && newValue.text == "-") {
return newValue;
}
final intValue = int.tryParse(newValue.text);
if (intValue == null) {
return oldValue;
}
return newValue;
},
),
], ],
), ),
const Gap(5), const Gap(5),

View File

@ -1,9 +1,13 @@
import 'dart:convert';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart' hide Consumer; import 'package:shadcn_flutter/shadcn_flutter.dart' hide Consumer;
import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/routes.gr.dart'; import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/models/connect/connect.dart';
import 'package:spotube/modules/player/player_queue.dart'; import 'package:spotube/modules/player/player_queue.dart';
import 'package:spotube/modules/player/volume_slider.dart'; import 'package:spotube/modules/player/volume_slider.dart';
import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/components/image/universal_image.dart';
@ -57,6 +61,7 @@ class ConnectControlPage extends HookConsumerWidget {
final resolvedService = final resolvedService =
ref.watch(connectClientsProvider).asData?.value.resolvedService; ref.watch(connectClientsProvider).asData?.value.resolvedService;
final connect = ref.watch(connectProvider);
final connectNotifier = ref.read(connectProvider.notifier); final connectNotifier = ref.read(connectProvider.notifier);
final playlist = ref.watch(queueProvider); final playlist = ref.watch(queueProvider);
final playing = ref.watch(playingProvider); final playing = ref.watch(playingProvider);
@ -69,12 +74,32 @@ class ConnectControlPage extends HookConsumerWidget {
} }
}); });
useEffect(() {
if (connect.asData?.value == null) return null;
final subscription = connect.asData?.value?.stream.listen((message) {
final event = WebSocketEvent.fromJson(
jsonDecode(message),
(data) => data,
);
event.onError((event) {
if (event.data != "Connection denied") return;
if (!context.mounted) return;
context.back();
});
});
return () {
subscription?.cancel();
};
}, [connect.asData?.value]);
return SafeArea( return SafeArea(
bottom: false, bottom: false,
child: Scaffold( child: Scaffold(
headers: [ headers: [
TitleBar( TitleBar(
title: Text(resolvedService!.name), title: Text(resolvedService?.name ?? ""),
) )
], ],
child: LayoutBuilder(builder: (context, constrains) { child: LayoutBuilder(builder: (context, constrains) {
@ -247,7 +272,8 @@ class ConnectControlPage extends HookConsumerWidget {
), ),
Tooltip( Tooltip(
tooltip: TooltipContainer( tooltip: TooltipContainer(
child: Text(context.l10n.next_track)).call, child: Text(context.l10n.next_track))
.call,
child: IconButton.ghost( child: IconButton.ghost(
icon: const Icon(SpotubeIcons.skipForward), icon: const Icon(SpotubeIcons.skipForward),
onPressed: playlist.activeTrack == null onPressed: playlist.activeTrack == null

View File

@ -1,6 +1,10 @@
import 'dart:convert'; import 'dart:convert';
import 'package:media_kit/media_kit.dart' hide Track; import 'package:media_kit/media_kit.dart' hide Track;
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/collections/routes.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/audio_player/state.dart'; import 'package:spotube/provider/audio_player/state.dart';
import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
@ -46,15 +50,17 @@ final volumeProvider = StateProvider<double>(
(ref) => 1.0, (ref) => 1.0,
); );
class ConnectNotifier extends AsyncNotifier<WebSocketChannel?> { typedef ConnectState = ({WebSocketChannel channel, Stream stream});
class ConnectNotifier extends AsyncNotifier<ConnectState?> {
@override @override
build() async { build() async {
try { try {
final connectClients = ref.watch(connectClientsProvider); final connectClients = await ref.watch(connectClientsProvider.future);
if (connectClients.asData?.value.resolvedService == null) return null; if (connectClients.resolvedService == null) return null;
final service = connectClients.asData!.value.resolvedService!; final service = connectClients.resolvedService!;
AppLogger.log.t( AppLogger.log.t(
'♾️ Connecting to ${service.name}: ws://${service.host}:${service.port}/ws', '♾️ Connecting to ${service.name}: ws://${service.host}:${service.port}/ws',
@ -70,7 +76,9 @@ class ConnectNotifier extends AsyncNotifier<WebSocketChannel?> {
'✅ Connected to ${service.name}: ws://${service.host}:${service.port}/ws', '✅ Connected to ${service.name}: ws://${service.host}:${service.port}/ws',
); );
final subscription = channel.stream.listen( final stream = channel.stream.asBroadcastStream();
final subscription = stream.listen(
(message) { (message) {
final event = final event =
WebSocketEvent.fromJson(jsonDecode(message), (data) => data); WebSocketEvent.fromJson(jsonDecode(message), (data) => data);
@ -102,6 +110,38 @@ class ConnectNotifier extends AsyncNotifier<WebSocketChannel?> {
event.onVolume((event) { event.onVolume((event) {
ref.read(volumeProvider.notifier).state = event.data; ref.read(volumeProvider.notifier).state = event.data;
}); });
event.onError((event) {
if (event.data == "Connection denied") {
ref.read(connectClientsProvider.notifier).clearResolvedService();
if (rootNavigatorKey.currentContext?.mounted == true) {
final theme = Theme.of(rootNavigatorKey.currentContext!);
showToast(
context: rootNavigatorKey.currentContext!,
location: ToastLocation.topRight,
dismissible: true,
builder: (context, overlay) {
return SurfaceCard(
fillColor: theme.colorScheme.destructive,
filled: true,
child: Basic(
leading: const Icon(SpotubeIcons.error),
title: Text(
context.l10n.connection_request_denied,
style: theme.typography.normal.copyWith(
color: theme.colorScheme.destructiveForeground,
),
),
leadingAlignment: Alignment.center,
),
);
},
);
}
}
});
}, },
onError: (error) { onError: (error) {
AppLogger.reportError(error, StackTrace.current); AppLogger.reportError(error, StackTrace.current);
@ -113,7 +153,7 @@ class ConnectNotifier extends AsyncNotifier<WebSocketChannel?> {
channel.sink.close(status.goingAway); channel.sink.close(status.goingAway);
}); });
return channel; return (channel: channel, stream: stream);
} catch (e, stack) { } catch (e, stack) {
AppLogger.reportError(e, stack); AppLogger.reportError(e, stack);
rethrow; rethrow;
@ -122,7 +162,7 @@ class ConnectNotifier extends AsyncNotifier<WebSocketChannel?> {
Future<void> emit(Object message) async { Future<void> emit(Object message) async {
if (state.value == null) return; if (state.value == null) return;
state.value?.sink.add( state.value?.channel.sink.add(
message is String ? message : (message as dynamic).toJson(), message is String ? message : (message as dynamic).toJson(),
); );
} }
@ -184,7 +224,6 @@ class ConnectNotifier extends AsyncNotifier<WebSocketChannel?> {
} }
} }
final connectProvider = final connectProvider = AsyncNotifierProvider<ConnectNotifier, ConnectState?>(
AsyncNotifierProvider<ConnectNotifier, WebSocketChannel?>(
() => ConnectNotifier(), () => ConnectNotifier(),
); );

View File

@ -3,9 +3,12 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shelf/shelf.dart'; import 'package:shelf/shelf.dart';
import 'package:shelf_web_socket/shelf_web_socket.dart'; import 'package:shelf_web_socket/shelf_web_socket.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/routes.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/connect/connect.dart'; import 'package:spotube/models/connect/connect.dart';
import 'package:spotube/provider/history/history.dart'; import 'package:spotube/provider/history/history.dart';
@ -43,6 +46,8 @@ class ServerConnectRoutes {
Stream<String> get connectClientStream => Stream<String> get connectClientStream =>
_connectClientStreamController.stream; _connectClientStreamController.stream;
final List<String> _allowedConnections = [];
FutureOr<Response> websocket(Request req) { FutureOr<Response> websocket(Request req) {
return webSocketHandler( return webSocketHandler(
( (
@ -54,6 +59,47 @@ class ServerConnectRoutes {
final origin = "${context?.remoteAddress.host}:${context?.remotePort}"; final origin = "${context?.remoteAddress.host}:${context?.remotePort}";
_connectClientStreamController.add(origin); _connectClientStreamController.add(origin);
// Confirm whether user allows to connect
if (rootNavigatorKey.currentContext?.mounted == true &&
_allowedConnections.contains(origin) == false) {
final confirmed = await showDialog<bool>(
context: rootNavigatorKey.currentContext!,
builder: (context) {
return AlertDialog(
title: Text(context.l10n.connect),
content: Text(
context.l10n.connect_request(origin),
),
actions: [
Button.secondary(
onPressed: () {
Navigator.of(context).pop(false);
},
child: Text(context.l10n.decline),
),
Button.primary(
onPressed: () {
Navigator.of(context).pop(true);
},
child: Text(context.l10n.accept),
),
],
);
},
) ??
false;
if (confirmed) {
_allowedConnections.add(origin);
} else {
channel.sink.addEvent(
WebSocketErrorEvent("Connection denied"),
);
await channel.sink.close();
return;
}
}
ref.listen( ref.listen(
audioPlayerProvider, audioPlayerProvider,
(previous, next) { (previous, next) {
@ -106,7 +152,7 @@ class ServerConnectRoutes {
}, },
), ),
channel.stream.listen( channel.stream.listen(
(message) { (message) async {
try { try {
final event = WebSocketEvent.fromJson( final event = WebSocketEvent.fromJson(
jsonDecode(message), jsonDecode(message),

View File

@ -1,141 +1,197 @@
{ {
"ar": [ "ar": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"bn": [ "bn": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"ca": [ "ca": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"cs": [ "cs": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"de": [ "de": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"es": [ "es": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"eu": [ "eu": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"fa": [ "fa": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"fi": [ "fi": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"fr": [ "fr": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"hi": [ "hi": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"id": [ "id": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"it": [ "it": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"ja": [ "ja": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"ka": [ "ka": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"ko": [ "ko": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"ne": [ "ne": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"nl": [ "nl": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"pl": [ "pl": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"pt": [ "pt": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"ru": [ "ru": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"ta": [ "ta": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"th": [ "th": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"tl": [ "tl": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"tr": [ "tr": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"uk": [ "uk": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"vi": [ "vi": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
], ],
"zh": [ "zh": [
"edit_port", "edit_port",
"port_helper_msg" "port_helper_msg",
"connect_request",
"connection_request_denied"
] ]
} }