Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
diamondburned
GitHub Repository: diamondburned/gtkcord4
Path: blob/main/internal/window/login/login.go
452 views
1
package login
2
3
import (
4
"context"
5
"log/slog"
6
7
"github.com/diamondburned/arikawa/v3/state"
8
"github.com/diamondburned/chatkit/kits/secret"
9
"github.com/diamondburned/gotk4/pkg/gtk/v4"
10
"github.com/diamondburned/gotkit/gtkutil"
11
"github.com/diamondburned/gotkit/gtkutil/cssutil"
12
"github.com/pkg/errors"
13
"libdb.so/dissent/internal/gtkcord"
14
)
15
16
// LoginController is the parent controller that Page controls.
17
type LoginController interface {
18
// Hook is called before the state is opened and before Ready is called. It
19
// is meant to be called for hooking the handlers.
20
Hook(*gtkcord.State)
21
// Ready is called once the user has fully logged in. The session given to
22
// the controller will have already been opened and have received the Ready
23
// event.
24
Ready(*gtkcord.State)
25
// PromptLogin is called by the login page if the user needs to log in
26
// again, either because their credentials are wrong or Discord returns a
27
// server error.
28
PromptLogin()
29
}
30
31
// Page is the page containing the login forms.
32
type Page struct {
33
*gtk.Box
34
Header *gtk.HeaderBar
35
Login *Component
36
37
ctx context.Context
38
ctrl LoginController
39
}
40
41
var pageCSS = cssutil.Applier("login-page", ``)
42
43
// NewPage creates a new Page.
44
func NewPage(ctx context.Context, ctrl LoginController) *Page {
45
p := Page{
46
ctx: ctx,
47
ctrl: ctrl,
48
}
49
50
p.Header = gtk.NewHeaderBar()
51
p.Header.AddCSSClass("login-page-header")
52
p.Header.SetShowTitleButtons(true)
53
54
p.Login = NewComponent(ctx, &p)
55
p.Login.SetVExpand(true)
56
p.Login.SetHExpand(true)
57
58
p.Box = gtk.NewBox(gtk.OrientationVertical, 0)
59
p.Box.Append(p.Header)
60
p.Box.Append(p.Login)
61
pageCSS(p)
62
63
return &p
64
}
65
66
// LoadKeyring loads the session from the keyring.
67
func (p *Page) LoadKeyring() {
68
p.asyncLoadFromSecrets(secret.KeyringDriver(p.ctx))
69
}
70
71
func (p *Page) asyncLoadFromSecrets(driver secret.Driver) {
72
p.Login.Loading.Show()
73
p.Login.SetSensitive(false)
74
75
done := func() {
76
p.Login.Loading.Hide()
77
p.Login.SetSensitive(true)
78
}
79
80
gtkutil.Async(p.ctx, func() func() {
81
b, err := driver.Get("account")
82
if err != nil {
83
slog.Info(
84
"account not found in keyring",
85
"err", err)
86
return done
87
}
88
89
return func() {
90
done()
91
p.asyncUseToken(string(b))
92
}
93
})
94
}
95
96
// asyncUseToken connects with the given token. If driver != nil, then the token
97
// is stored.
98
func (p *Page) asyncUseToken(token string) {
99
state := gtkcord.Wrap(state.New(token))
100
p.ctrl.Hook(state)
101
102
gtkutil.Async(p.ctx, func() func() {
103
if err := state.Open(p.ctx); err != nil {
104
return func() {
105
p.ctrl.PromptLogin()
106
p.Login.ShowError(errors.Wrap(err, "cannot open session"))
107
}
108
}
109
110
return func() {
111
p.ctrl.Ready(state)
112
}
113
})
114
}
115
116