-
Notifications
You must be signed in to change notification settings - Fork 9
/
paginator.go
221 lines (186 loc) · 4.7 KB
/
paginator.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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package dgwidgets
import (
"fmt"
"strconv"
"sync"
"time"
"github.com/bwmarrin/discordgo"
)
// Paginator provides a method for creating a navigatable embed
type Paginator struct {
sync.Mutex
Pages []*discordgo.MessageEmbed
Index int
// Loop back to the beginning or end when on the first or last page.
Loop bool
Widget *Widget
Ses *discordgo.Session
DeleteMessageWhenDone bool
DeleteReactionsWhenDone bool
ColourWhenDone int
lockToUser bool
running bool
}
// NewPaginator returns a new Paginator
// ses : discordgo session
// channelID: channelID to spawn the paginator on
func NewPaginator(ses *discordgo.Session, channelID string) *Paginator {
p := &Paginator{
Ses: ses,
Pages: []*discordgo.MessageEmbed{},
Index: 0,
Loop: false,
DeleteMessageWhenDone: false,
DeleteReactionsWhenDone: false,
ColourWhenDone: -1,
Widget: NewWidget(ses, channelID, nil),
}
p.addHandlers()
return p
}
func (p *Paginator) addHandlers() {
p.Widget.Handle(NavBeginning, func(w *Widget, r *discordgo.MessageReaction) {
if err := p.Goto(0); err == nil {
p.Update()
}
})
p.Widget.Handle(NavLeft, func(w *Widget, r *discordgo.MessageReaction) {
if err := p.PreviousPage(); err == nil {
p.Update()
}
})
p.Widget.Handle(NavRight, func(w *Widget, r *discordgo.MessageReaction) {
if err := p.NextPage(); err == nil {
p.Update()
}
})
p.Widget.Handle(NavEnd, func(w *Widget, r *discordgo.MessageReaction) {
if err := p.Goto(len(p.Pages) - 1); err == nil {
p.Update()
}
})
p.Widget.Handle(NavNumbers, func(w *Widget, r *discordgo.MessageReaction) {
if msg, err := w.QueryInput("enter the page number you would like to open", r.UserID, 10*time.Second); err == nil {
if n, err := strconv.Atoi(msg.Content); err == nil {
p.Goto(n - 1)
p.Update()
}
}
})
}
// Spawn spawns the paginator in channel p.ChannelID
func (p *Paginator) Spawn() error {
if p.Running() {
return ErrAlreadyRunning
}
p.Lock()
p.running = true
p.Unlock()
defer func() {
p.Lock()
p.running = false
p.Unlock()
// Delete Message when done
if p.DeleteMessageWhenDone && p.Widget.Message != nil {
p.Ses.ChannelMessageDelete(p.Widget.Message.ChannelID, p.Widget.Message.ID)
} else if p.ColourWhenDone >= 0 {
if page, err := p.Page(); err == nil {
page.Color = p.ColourWhenDone
p.Update()
}
}
// Delete reactions when done
if p.DeleteReactionsWhenDone && p.Widget.Message != nil {
p.Ses.MessageReactionsRemoveAll(p.Widget.ChannelID, p.Widget.Message.ID)
}
}()
page, err := p.Page()
if err != nil {
return err
}
p.Widget.Embed = page
return p.Widget.Spawn()
}
// Add a page to the paginator
// embed: embed page to add.
func (p *Paginator) Add(embeds ...*discordgo.MessageEmbed) {
p.Pages = append(p.Pages, embeds...)
}
// Page returns the page of the current index
func (p *Paginator) Page() (*discordgo.MessageEmbed, error) {
p.Lock()
defer p.Unlock()
if p.Index < 0 || p.Index >= len(p.Pages) {
return nil, ErrIndexOutOfBounds
}
return p.Pages[p.Index], nil
}
// NextPage sets the page index to the next page
func (p *Paginator) NextPage() error {
p.Lock()
defer p.Unlock()
if p.Index+1 >= 0 && p.Index+1 < len(p.Pages) {
p.Index++
return nil
}
// Set the queue back to the beginning if Loop is enabled.
if p.Loop {
p.Index = 0
return nil
}
return ErrIndexOutOfBounds
}
// PreviousPage sets the current page index to the previous page.
func (p *Paginator) PreviousPage() error {
p.Lock()
defer p.Unlock()
if p.Index-1 >= 0 && p.Index-1 < len(p.Pages) {
p.Index--
return nil
}
// Set the queue back to the beginning if Loop is enabled.
if p.Loop {
p.Index = len(p.Pages) - 1
return nil
}
return ErrIndexOutOfBounds
}
// Goto jumps to the requested page index
// index: The index of the page to go to
func (p *Paginator) Goto(index int) error {
p.Lock()
defer p.Unlock()
if index < 0 || index >= len(p.Pages) {
return ErrIndexOutOfBounds
}
p.Index = index
return nil
}
// Update updates the message with the current state of the paginator
func (p *Paginator) Update() error {
if p.Widget.Message == nil {
return ErrNilMessage
}
page, err := p.Page()
if err != nil {
return err
}
_, err = p.Widget.UpdateEmbed(page)
return err
}
// Running returns the running status of the paginator
func (p *Paginator) Running() bool {
p.Lock()
running := p.running
p.Unlock()
return running
}
// SetPageFooters sets the footer of each embed to
// Be its page number out of the total length of the embeds.
func (p *Paginator) SetPageFooters() {
for index, embed := range p.Pages {
embed.Footer = &discordgo.MessageEmbedFooter{
Text: fmt.Sprintf("#[%d / %d]", index+1, len(p.Pages)),
}
}
}