@@ -357,6 +357,10 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
static const customDownloadPath = NullableStringPref('custom_download_path');
|
||||
static final customRelayUrl = NullableStringPref('custom_relay_url', transform: _trimEmptyAsNull);
|
||||
static const recentRooms = NullableStringPref('watch_together_recent_rooms');
|
||||
static final companionRemoteLastHostAddress = NullableStringPref(
|
||||
'companion_remote_last_host_address',
|
||||
transform: _trimEmptyAsNull,
|
||||
);
|
||||
|
||||
static final maxVolume = IntPref('max_volume', defaultValue: 100, transform: (v) => v.clamp(100, 300));
|
||||
static final subtitlePosition = IntPref('subtitle_position', defaultValue: 100, transform: (v) => v.clamp(0, 100));
|
||||
@@ -736,6 +740,7 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
selectedExternalPlayer,
|
||||
customExternalPlayers,
|
||||
customRelayUrl,
|
||||
companionRemoteLastHostAddress,
|
||||
];
|
||||
|
||||
Future<void> resetAllSettings() async {
|
||||
|
||||
@@ -16,6 +16,7 @@ import '../../profiles/active_profile_provider.dart';
|
||||
import '../../profiles/plex_home_service.dart';
|
||||
import '../../profiles/profile_connection_registry.dart';
|
||||
import '../../providers/companion_remote_provider.dart';
|
||||
import '../../services/settings_service.dart';
|
||||
import '../../utils/app_logger.dart';
|
||||
import '../loading_indicator_box.dart';
|
||||
|
||||
@@ -48,9 +49,20 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
|
||||
void initState() {
|
||||
super.initState();
|
||||
_provider = context.read<CompanionRemoteProvider>();
|
||||
unawaited(_loadSavedManualHostAddress());
|
||||
_initCryptoAndDiscover();
|
||||
}
|
||||
|
||||
Future<void> _loadSavedManualHostAddress() async {
|
||||
final settings = SettingsService.instanceOrNull ?? await SettingsService.getInstance();
|
||||
final hostAddress = settings.read(SettingsService.companionRemoteLastHostAddress);
|
||||
if (!mounted || hostAddress == null || _hostAddressController.text.isNotEmpty) return;
|
||||
setState(() {
|
||||
_hostAddressController.text = hostAddress;
|
||||
_showManualEntry = true;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _initCryptoAndDiscover() async {
|
||||
final connections = context.read<ConnectionRegistry>();
|
||||
final activeProfile = context.read<ActiveProfileProvider>();
|
||||
@@ -140,6 +152,26 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _saveManualHostAddress(String hostAddress) async {
|
||||
try {
|
||||
final settings = SettingsService.instanceOrNull ?? await SettingsService.getInstance();
|
||||
await settings.write(SettingsService.companionRemoteLastHostAddress, hostAddress);
|
||||
} catch (e) {
|
||||
appLogger.w('Failed to save companion remote host address', error: e);
|
||||
}
|
||||
}
|
||||
|
||||
void _submitManualHost() {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
final hostAddress = _hostAddressController.text.trim();
|
||||
unawaited(
|
||||
_connect(() async {
|
||||
await _provider.connectToManualHost(hostAddress);
|
||||
await _saveManualHostAddress(hostAddress);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
String _parseErrorMessage(String error) {
|
||||
if (error.contains('timeout') || error.contains('Timed out')) {
|
||||
return t.companionRemote.pairing.connectionTimedOut;
|
||||
@@ -340,10 +372,11 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
|
||||
prefixIcon: const Icon(Icons.computer),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
final hostAddress = value?.trim() ?? '';
|
||||
if (hostAddress.isEmpty) {
|
||||
return t.companionRemote.pairing.validationHostRequired;
|
||||
}
|
||||
if (value.split(':').length != 2) {
|
||||
if (hostAddress.split(':').length != 2) {
|
||||
return t.companionRemote.pairing.validationHostFormat;
|
||||
}
|
||||
return null;
|
||||
@@ -356,19 +389,9 @@ class _DiscoveryViewState extends State<DiscoveryView> with ControllerDisposerMi
|
||||
FocusableButton(
|
||||
focusNode: _connectFocusNode,
|
||||
onNavigateUp: _hostAddressFocusNode.requestFocus,
|
||||
onPressed: _isConnecting
|
||||
? null
|
||||
: () {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
_connect(() => _provider.connectToManualHost(_hostAddressController.text.trim()));
|
||||
},
|
||||
onPressed: _isConnecting ? null : _submitManualHost,
|
||||
child: FilledButton.icon(
|
||||
onPressed: _isConnecting
|
||||
? null
|
||||
: () {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
_connect(() => _provider.connectToManualHost(_hostAddressController.text.trim()));
|
||||
},
|
||||
onPressed: _isConnecting ? null : _submitManualHost,
|
||||
icon: _isConnecting ? const LoadingIndicatorBox(size: 16) : const Icon(Icons.link),
|
||||
label: Text(_isConnecting ? t.companionRemote.pairing.connecting : t.common.connect),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user