@@ -182,6 +182,36 @@ fun OnboardingFlow(
182182
183183 val permissionState = rememberPermissionState(context = context, viewModel = viewModel)
184184
185+ fun connectToGatewayConfig (
186+ config : GatewayConnectConfig ,
187+ resetSetupAuth : Boolean ,
188+ ) {
189+ setupError = null
190+ attemptedGatewayName = null
191+ attemptedConnect = true
192+ connectAttemptStartedAtMs = SystemClock .elapsedRealtime()
193+ viewModel.saveGatewayConfigAndConnect(
194+ host = config.host,
195+ port = config.port,
196+ tls = config.tls,
197+ token = config.token,
198+ bootstrapToken = config.bootstrapToken,
199+ password = config.password,
200+ resetSetupAuth = resetSetupAuth,
201+ )
202+ step = OnboardingStep .Recovery
203+ }
204+
205+ fun resolveCurrentGatewayConfig (setupCodeValue : String = setupCode): GatewayConnectConfig ? =
206+ resolveOnboardingGatewayConnectConfig(
207+ setupCode = setupCodeValue,
208+ manualHost = manualHost,
209+ manualPort = manualPort,
210+ manualTls = manualTls,
211+ token = token,
212+ password = password,
213+ )
214+
185215 LaunchedEffect (startAtGatewaySetup) {
186216 if (startAtGatewaySetup) {
187217 step = OnboardingStep .Gateway
@@ -261,15 +291,26 @@ fun OnboardingFlow(
261291 .startScan()
262292 .addOnSuccessListener { barcode ->
263293 val scanned = resolveScannedSetupCodeResult(barcode.rawValue.orEmpty())
264- if (scanned.setupCode == null ) {
294+ val scannedSetupCode = scanned.setupCode
295+ if (scannedSetupCode == null ) {
265296 setupError =
266297 gatewayEndpointValidationMessage(
267298 scanned.error ? : GatewayEndpointValidationError .INVALID_URL ,
268299 GatewayEndpointInputSource .QR_SCAN ,
300+ )
301+ return @addOnSuccessListener
302+ }
303+ val config = resolveCurrentGatewayConfig(setupCodeValue = scannedSetupCode)
304+ if (config == null ) {
305+ setupError =
306+ gatewayEndpointValidationMessage(
307+ GatewayEndpointValidationError .INVALID_URL ,
308+ GatewayEndpointInputSource .QR_SCAN ,
269309 )
270310 return @addOnSuccessListener
271311 }
272- setupCode = scanned.setupCode
312+ setupCode = scannedSetupCode
313+ connectToGatewayConfig(config, resetSetupAuth = true )
273314 }.addOnFailureListener { setupError = " Could not open the scanner." }
274315 },
275316 onSetupCodeChange = {
@@ -296,34 +337,12 @@ fun OnboardingFlow(
296337 step = OnboardingStep .Recovery
297338 },
298339 onPair = {
299- val config =
300- resolveGatewayConfig(
301- setupCode = setupCode,
302- manualHost = manualHost,
303- manualPort = manualPort,
304- manualTls = manualTls,
305- token = token,
306- password = password,
307- )
340+ val config = resolveCurrentGatewayConfig()
308341 if (config == null ) {
309342 setupError = " Enter a setup code or a valid gateway URL."
310343 return @GatewaySetupScreen
311344 }
312-
313- setupError = null
314- attemptedGatewayName = null
315- attemptedConnect = true
316- connectAttemptStartedAtMs = SystemClock .elapsedRealtime()
317- viewModel.saveGatewayConfigAndConnect(
318- host = config.host,
319- port = config.port,
320- tls = config.tls,
321- token = config.token,
322- bootstrapToken = config.bootstrapToken,
323- password = config.password,
324- resetSetupAuth = true ,
325- )
326- step = OnboardingStep .Recovery
345+ connectToGatewayConfig(config, resetSetupAuth = true )
327346 },
328347 )
329348 OnboardingStep .Recovery ->
@@ -339,26 +358,8 @@ fun OnboardingFlow(
339358 connectSettling = recoveryNowMs - connectAttemptStartedAtMs < GATEWAY_CONNECT_SETTLING_MS ,
340359 onBack = { step = OnboardingStep .Gateway },
341360 onRetry = {
342- attemptedConnect = true
343- connectAttemptStartedAtMs = SystemClock .elapsedRealtime()
344- val config =
345- resolveGatewayConfig(
346- setupCode = setupCode,
347- manualHost = manualHost,
348- manualPort = manualPort,
349- manualTls = manualTls,
350- token = token,
351- password = password,
352- ) ? : return @GatewayRecoveryScreen
353- viewModel.saveGatewayConfigAndConnect(
354- host = config.host,
355- port = config.port,
356- tls = config.tls,
357- token = config.token,
358- bootstrapToken = config.bootstrapToken,
359- password = config.password,
360- resetSetupAuth = false ,
361- )
361+ val config = resolveCurrentGatewayConfig() ? : return @GatewayRecoveryScreen
362+ connectToGatewayConfig(config, resetSetupAuth = false )
362363 },
363364 onEdit = { step = OnboardingStep .Gateway },
364365 onContinue = { step = OnboardingStep .Permissions },
@@ -1151,59 +1152,28 @@ internal fun recoveryGatewayName(
11511152 ?.takeIf { it.isNotEmpty() }
11521153 ? : " Home Gateway"
11531154
1154- private data class GatewayConfig (
1155- val host : String ,
1156- val port : Int ,
1157- val tls : Boolean ,
1158- val bootstrapToken : String ,
1159- val token : String ,
1160- val password : String ,
1161- )
1162-
1163- /* * Resolves setup-code or manual fields into the gateway config used for first connect. */
1164- private fun resolveGatewayConfig (
1155+ /* * Resolves onboarding setup-code or manual fields into the gateway config used for connect. */
1156+ internal fun resolveOnboardingGatewayConnectConfig (
11651157 setupCode : String ,
11661158 manualHost : String ,
11671159 manualPort : String ,
11681160 manualTls : Boolean ,
11691161 token : String ,
11701162 password : String ,
1171- ): GatewayConfig ? {
1172- val setup = setupCode.takeIf { it.isNotBlank() }?.let (::decodeGatewaySetupCode)
1173- if (setup != null ) {
1174- val endpoint = parseGatewayEndpointResult(setup.url).config ? : return null
1175- val bootstrapToken = setup.bootstrapToken?.trim().orEmpty()
1176- // Bootstrap setup codes own first-pairing auth; fall back to typed token or
1177- // password only for non-bootstrap setup payloads.
1178- return GatewayConfig (
1179- host = endpoint.host,
1180- port = endpoint.port,
1181- tls = endpoint.tls,
1182- bootstrapToken = bootstrapToken,
1183- token =
1184- setup.token
1185- ?.trim()
1186- .orEmpty()
1187- .ifEmpty { if (bootstrapToken.isEmpty()) token.trim() else " " },
1188- password =
1189- setup.password
1190- ?.trim()
1191- .orEmpty()
1192- .ifEmpty { if (bootstrapToken.isEmpty()) password.trim() else " " },
1193- )
1194- }
1195-
1196- val manualUrl = composeGatewayManualUrl(manualHost, manualPort, manualTls) ? : return null
1197- val endpoint = parseGatewayEndpointResult(manualUrl).config ? : return null
1198- return GatewayConfig (
1199- host = endpoint.host,
1200- port = endpoint.port,
1201- tls = endpoint.tls,
1202- bootstrapToken = " " ,
1203- token = token.trim(),
1204- password = password.trim(),
1163+ ): GatewayConnectConfig ? =
1164+ resolveGatewayConnectConfig(
1165+ useSetupCode = setupCode.isNotBlank(),
1166+ setupCode = setupCode,
1167+ savedManualHost = manualHost,
1168+ savedManualPort = manualPort,
1169+ savedManualTls = manualTls,
1170+ manualHostInput = manualHost,
1171+ manualPortInput = manualPort,
1172+ manualTlsInput = manualTls,
1173+ fallbackBootstrapToken = " " ,
1174+ fallbackToken = token,
1175+ fallbackPassword = password,
12051176 )
1206- }
12071177
12081178/* * Selects the recovery detail line from endpoint metadata and transient gateway status. */
12091179internal fun recoveryGatewayDetail (
0 commit comments