components_quickConnect_QuickConnectDialog.bs

import "pkg:/source/api/baseRequest.bs"
import "pkg:/source/api/userAuth.bs"
import "pkg:/source/roku_modules/log/LogMixin.brs"
import "pkg:/source/translationKeys.bs"
import "pkg:/source/utils/config.bs"
import "pkg:/source/utils/misc.bs"
import "pkg:/source/utils/session.bs"
import "pkg:/source/utils/translate.bs"

' Dialog has two phases:
'   "waiting" - QC code shown, polling server for user approval
'   "prompt"  - auth succeeded in-memory; asking whether to persist credentials
sub init()
  m.log = new log.Logger("QuickConnectDialog")
  m.phase = "waiting"
  m.quickConnectTimer = m.top.findNode("quickConnectTimer")
  m.quickConnectTimer.observeField("fire", "quickConnectStatus")
  m.quickConnectTimer.control = "start"
  m.top.observeFieldScoped("buttonSelected", "onButtonSelected")
end sub

sub quickConnectStatus()
  m.quickConnectTimer.control = "stop"
  m.checkTask = CreateObject("roSGNode", "QuickConnect")
  m.checkTask.secret = m.top.quickConnectJson.secret
  m.checkTask.observeField("authenticated", "onAuthenticated")
  m.checkTask.control = "run"
end sub

sub onAuthenticated()
  m.checkTask.control = "stop"
  m.checkTask.unobserveField("authenticated")

  authenticated = m.checkTask.authenticated
  if authenticated < 0
    ' Not yet approved; resume polling.
    m.checkTask = invalid
    m.quickConnectTimer.control = "start"
  else if authenticated > 0
    ' Auth succeeded; user.Login() already hydrated in-memory state with
    ' saveCredentials=false. Now ask whether to persist to registry.
    m.checkTask = invalid
    showSaveCredentialsPrompt()
  end if
end sub

' Mutates the open dialog in place to show a Yes/No prompt.
' Keeps the same dialog node so there is no visual flicker.
sub showSaveCredentialsPrompt()
  m.log.info("Quick Connect authenticated - prompting to save credentials")
  m.phase = "prompt"
  m.top.message = [translate(translationKeys.MessageQuickConnectAuthenticatedSaveCredentials)]
  m.top.buttons = [translate(translationKeys.ButtonYes), translate(translationKeys.ButtonNo)]
  ' Replacing the buttons array destroys and recreates the dialog's internal
  ' button group; focus does not automatically follow, so re-acquire it here.
  ' Without this, remote input routes back to the underlying scene and the
  ' dialog becomes visually present but non-interactive.
  m.top.setFocus(true)
end sub

sub onButtonSelected()
  if m.phase = "waiting"
    ' Cancel button in the waiting phase - abort before login completes.
    quickConnectCancelled()
  else if m.phase = "prompt"
    ' Yes = index 0 (save), No = index 1 (don't save).
    finishQuickConnect(m.top.buttonSelected = 0)
  end if
end sub

' User abandoned Quick Connect before server approval. Do NOT signal
' success - showScenes sees the dialog close and treats it as a cancel.
sub quickConnectCancelled()
  m.log.info("Quick Connect cancelled")
  m.quickConnectTimer.control = "stop"
  if isValid(m.checkTask)
    m.checkTask.control = "stop"
    m.checkTask.unobserveField("authenticated")
    m.checkTask = invalid
  end if
  m.top.close = true
end sub

' Phase 2 exit. Conditionally persist credentials, then signal success so
' showScenes can complete the login flow.
sub finishQuickConnect(saveCredentials as boolean)
  if saveCredentials
    m.log.info("Credentials saved")
    persistCurrentUserCredentials()
  else
    m.log.info("Credentials not saved")
  end if
  m.top.isAuthenticated = true
  m.top.close = true
end sub

' Writes authToken / username / primaryImageTag to the current user's
' registry section. The QuickConnect task called user.Login(user, false)
' earlier, which left m.global.user hydrated without touching the registry.
sub persistCurrentUserCredentials()
  globalUser = m.global.user
  if isValidAndNotEmpty(globalUser.authToken)
    setUserSetting("authToken", globalUser.authToken)
  end if
  if isValidAndNotEmpty(globalUser.name)
    setUserSetting("username", globalUser.name)
  end if
  if isValidAndNotEmpty(globalUser.primaryImageTag)
    setUserSetting("primaryImageTag", globalUser.primaryImageTag)
  end if
end sub

function onKeyEvent(key as string, press as boolean) as boolean
  if not press then return false

  ' "OK" does not route here - handled via onButtonSelected above.
  if key = "back"
    if m.phase = "waiting"
      quickConnectCancelled()
    else if m.phase = "prompt"
      ' Back on the save prompt = decline to persist; login already succeeded.
      finishQuickConnect(false)
    end if
    return true
  end if

  return false
end function