login_view.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package ui
  2. import (
  3. "context"
  4. "log"
  5. "github.com/ayntgl/discordo/config"
  6. "github.com/diamondburned/arikawa/v3/api"
  7. "github.com/gdamore/tcell/v2"
  8. "github.com/rivo/tview"
  9. "github.com/zalando/go-keyring"
  10. )
  11. const (
  12. emailViewPageName = "email"
  13. tokenViewPageName = "token"
  14. )
  15. func NewLoginView(c *Core) *tview.Pages {
  16. v := tview.NewPages()
  17. v.AddPage(emailViewPageName, newEmailView(c), true, true)
  18. v.AddPage(tokenViewPageName, newTokenView(c), true, true)
  19. // The email view is displayed on the screen first since it is the recommended method to login.
  20. v.SwitchToPage(emailViewPageName)
  21. v.SetTitle("Login")
  22. v.SetTitleAlign(tview.AlignLeft)
  23. v.SetBorder(true)
  24. v.SetBorderPadding(0, 0, 1, 1)
  25. v.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
  26. if event.Key() == tcell.KeyCtrlSpace {
  27. name, _ := v.GetFrontPage()
  28. switch name {
  29. case emailViewPageName:
  30. name = tokenViewPageName
  31. case tokenViewPageName:
  32. name = emailViewPageName
  33. }
  34. v.SwitchToPage(name)
  35. }
  36. return event
  37. })
  38. return v
  39. }
  40. type EmailView struct {
  41. *tview.Form
  42. core *Core
  43. }
  44. func newEmailView(c *Core) *EmailView {
  45. v := &EmailView{
  46. Form: tview.NewForm(),
  47. core: c,
  48. }
  49. v.AddInputField("Email", "", 0, nil, nil)
  50. v.AddPasswordField("Password", "", 0, 0, nil)
  51. v.AddButton("Login", v.onLoginButtonSelected)
  52. return v
  53. }
  54. func (v *EmailView) onLoginButtonSelected() {
  55. email := v.GetFormItem(0).(*tview.InputField).GetText()
  56. password := v.GetFormItem(1).(*tview.InputField).GetText()
  57. if email == "" || password == "" {
  58. return
  59. }
  60. // Make a scratch HTTP client without a token
  61. client := api.NewClient("").WithContext(context.Background())
  62. // Try to login without TOTP
  63. l, err := client.Login(email, password)
  64. if err != nil {
  65. log.Fatal(err)
  66. }
  67. if l.Token != "" && !l.MFA {
  68. err = v.core.Run(l.Token)
  69. if err != nil {
  70. log.Fatal(err)
  71. }
  72. v.core.Draw()
  73. go keyring.Set(config.Name, "token", l.Token)
  74. }
  75. // TODO: MFA login
  76. }
  77. type TokenView struct {
  78. *tview.Form
  79. core *Core
  80. }
  81. func newTokenView(c *Core) *TokenView {
  82. v := &TokenView{
  83. Form: tview.NewForm(),
  84. core: c,
  85. }
  86. v.AddPasswordField("Token", "", 0, 0, nil)
  87. v.AddButton("Login", v.onLoginButtonSelected)
  88. return v
  89. }
  90. func (v *TokenView) onLoginButtonSelected() {
  91. token := v.GetFormItem(0).(*tview.InputField).GetText()
  92. if token == "" {
  93. return
  94. }
  95. err := v.core.Run(token)
  96. if err != nil {
  97. log.Fatal(err)
  98. }
  99. v.core.Draw()
  100. go keyring.Set(config.Name, "token", token)
  101. }