-
Notifications
You must be signed in to change notification settings - Fork 80
/
connected_accounts_component.go
175 lines (148 loc) · 5.67 KB
/
connected_accounts_component.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package gui
import (
"github.com/coyim/coyim/i18n"
"github.com/coyim/gotk3adapter/glibi"
"github.com/coyim/gotk3adapter/gtki"
)
// This is a component that can be used from other dialogs in order to make it easy to have a
// list of all connected accounts, and having it updated automatically and so on
type connectedAccountsComponent struct {
accountsModel gtki.ListStore
accountsInput gtki.ComboBox
accountsList []*account
accounts map[string]*account
currentlyActive int
onDestroyFunc func()
errorNotifications canNotifyErrors
onNoAccounts func()
onAccountsUpdated func(*account)
}
// disableAccountInput should ONLY be called from the UI thread
func (cac *connectedAccountsComponent) disableAccountInput() {
cac.accountsInput.SetSensitive(false)
}
// enableAccountInput should ONLY be called from the UI thread
func (cac *connectedAccountsComponent) enableAccountInput() {
cac.accountsInput.SetSensitive(true)
}
// onDestroy should ONLY be called from the UI thread
func (cac *connectedAccountsComponent) onDestroy() {
if cac.onDestroyFunc != nil {
cac.onDestroyFunc()
}
}
// currentAccount is safe to call from the UI thread, or from outside the UI thread
func (cac *connectedAccountsComponent) currentAccount() *account {
if cac.currentlyActive != -1 && len(cac.accountsList) > 0 && cac.currentlyActive < len(cac.accountsList) {
return cac.accountsList[cac.currentlyActive]
}
return nil
}
func (cac *connectedAccountsComponent) setCurrentAccount(ca *account) {
if a, _ := cac.accounts[ca.ID()]; a != ca {
return
}
previousActive := cac.currentlyActive
for ix, a := range cac.accountsList {
if a == ca {
cac.currentlyActive = ix
}
}
if cac.currentlyActive != previousActive {
cac.accountsInput.SetActive(cac.currentlyActive)
cac.onAccountsUpdated(cac.currentAccount())
}
}
// hasAccounts is safe to call from the UI thread, or from outside the UI thread
func (cac *connectedAccountsComponent) hasAccounts() bool {
return len(cac.accounts) > 0
}
// hasAccountValue is safe to call from the UI thread, or from outside the UI thread
func (cac *connectedAccountsComponent) hasAccountValue() bool {
return cac.currentAccount() != nil
}
// initOrReplaceAccounts should ONLY be called from the UI thread
// We don't need to do any locking here, since it's already in the UI thread
// so no other things can happen to the UI in the meantime
func (cac *connectedAccountsComponent) initOrReplaceAccounts(accounts []*account) {
if !cac.hasAccounts() {
cac.errorNotifications.notifyOnError(i18n.Local("There are no connected accounts"))
}
currentlyActive := 0
oldActive := cac.accountsInput.GetActive()
if cac.accounts != nil && oldActive >= 0 {
currentlyActiveAccount := cac.accountsList[oldActive]
for ix, a := range accounts {
if currentlyActiveAccount == a {
currentlyActive = ix
cac.currentlyActive = currentlyActive
}
}
cac.accountsModel.Clear()
}
cac.accountsList = accounts
cac.accounts = make(map[string]*account)
for _, acc := range accounts {
iter := cac.accountsModel.Append()
_ = cac.accountsModel.SetValue(iter, 0, acc.Account())
_ = cac.accountsModel.SetValue(iter, 1, acc.ID())
cac.accounts[acc.ID()] = acc
}
if len(accounts) > 0 {
cac.accountsInput.SetActive(currentlyActive)
cac.errorNotifications.clearErrors()
cac.onAccountsUpdated(cac.currentAccount())
} else {
go cac.onNoAccounts()
}
}
// createConnectedAccountsComponent will create a component for managing a combo box of connected accounts.
// It should ONLY be called from the UI thread
// it needs the combo box to populate. It will create its own model to populate with accounts.
// The errorNot argument is anything that can notify on errors, and clear those notifications. The onAccountsUpdated callback
// will be called when the the user moves to a different account in the list. The onNoAccounts callback will be called if
// there are no connected accounts available at all.
// Remember to call onDestroy() on the component when the window is destroyed, to not create to many update notifications that lead
// nowhere.
// The onAccountsUpdated function will NOT be called from the UI thread.
// The onNoAccounts function will NOT be called from the UI thread
func (u *gtkUI) createConnectedAccountsComponent(input gtki.ComboBox, errorNot canNotifyErrors, onAccountsUpdated func(*account), onNoAccounts func()) *connectedAccountsComponent {
result := &connectedAccountsComponent{}
result.errorNotifications = errorNot
result.onNoAccounts = onNoAccounts
onAccountsUpdatedFinal := onAccountsUpdated
if onAccountsUpdatedFinal == nil {
onAccountsUpdatedFinal = func(*account) {}
}
result.onAccountsUpdated = onAccountsUpdatedFinal
var e error
// These two arguments are: account name and account id
result.accountsModel, e = g.gtk.ListStoreNew(glibi.TYPE_STRING, glibi.TYPE_STRING)
if e != nil {
panic(e)
}
result.accountsInput = input
result.accountsInput.SetModel(result.accountsModel)
result.initOrReplaceAccounts(u.getAllConnectedAccounts())
accountsObserverToken := u.onChangeOfConnectedAccounts(func() {
doInUIThread(func() {
result.initOrReplaceAccounts(u.getAllConnectedAccounts())
})
})
_, _ = result.accountsInput.Connect("changed", func() {
act := result.accountsInput.GetActive()
if act >= 0 && act < len(result.accountsList) && act != result.currentlyActive {
result.currentlyActive = act
go result.onAccountsUpdated(result.currentAccount())
}
})
result.currentlyActive = -1
if len(result.accountsList) > 0 {
result.currentlyActive = 0
result.onAccountsUpdated(result.currentAccount())
}
result.onDestroyFunc = func() {
u.removeConnectedAccountsObserver(accountsObserverToken)
}
return result
}