-
Notifications
You must be signed in to change notification settings - Fork 1
/
study.go
175 lines (155 loc) · 5.06 KB
/
study.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
// +build js
package studyhandler
import (
"context"
"net/url"
"time"
"github.com/flimzy/jqeventrouter"
"github.com/flimzy/log"
"github.com/gopherjs/gopherjs/js"
"github.com/gopherjs/jquery"
"github.com/pkg/errors"
"github.com/FlashbackSRS/flashback"
"github.com/FlashbackSRS/flashback/iframes"
"github.com/FlashbackSRS/flashback/model"
"github.com/FlashbackSRS/flashback/webclient/views/studyview"
)
var jQuery = jquery.NewJQuery
type cardState struct {
Card flashback.CardView
StartTime time.Time
Face int
}
var currentCard *cardState
var currentDeck string
// BeforeChange sets the deck if necessary
func BeforeChange() jqeventrouter.HandlerFunc {
return func(_ *jquery.Event, _ *js.Object, params url.Values) bool {
if len(params) == 0 {
return true
}
currentDeck = params.Get("deck")
js.Global.Get("jQuery").Get("mobile").Call("changePage", "study.html")
return false
}
}
// BeforeTransition prepares the page to study
func BeforeTransition(repo *model.Repo) jqeventrouter.HandlerFunc {
return func(event *jquery.Event, ui *js.Object, params url.Values) bool {
if _, err := repo.CurrentUser(); err != nil {
log.Printf("No user logged in: %s\n", err)
return false
}
go func() {
if err := ShowCard(repo, currentDeck); err != nil {
log.Printf("Error showing card: %v", err)
}
}()
return true
}
}
func ShowCard(repo *model.Repo, deck string) error {
if currentCard == nil {
log.Debug("Fetching card\n")
card, err := repo.GetCardToStudy(context.TODO(), deck)
if err != nil {
return errors.Wrap(err, "get card to study")
}
if card == nil {
return errors.New("got a nil card")
}
currentCard = &cardState{
Card: card,
}
}
log.Debugf("Card ID: %s\n", currentCard.Card.DocID())
body, err := currentCard.Card.Body(context.TODO(), currentCard.Face)
if err != nil {
return errors.Wrap(err, "fetching body")
}
iframe := js.Global.Get("document").Call("createElement", "iframe")
iframe.Call("setAttribute", "sandbox", "allow-scripts allow-forms")
iframe.Call("setAttribute", "seamless", nil)
ab := js.NewArrayBuffer([]byte(body))
b := js.Global.Get("Blob").New([]interface{}{ab}, map[string]string{"type": "text/html"})
iframeURL := js.Global.Get("URL").Call("createObjectURL", b)
iframe.Set("src", iframeURL)
respond, err := iframes.RegisterIframe(iframeURL.String(), currentCard.Card.DocID())
if err != nil {
return errors.Wrap(err, "failed to register iframe")
}
container := jQuery(":mobile-pagecontainer")
log.Debug("Setting up the buttons\n")
buttons := container.Find("#answer-buttons").Find(`[data-role="button"]`)
buttons.RemoveClass("ui-btn-active")
clickFunc := func(e *js.Object) {
go func() { // DB updates block
buttons.Off() // Make sure we don't accept other press events
buttonID := e.Get("currentTarget").Call("getAttribute", "data-id").String()
log.Debugf("Button %s was pressed!\n", buttonID)
respond("submit", buttonID)
}()
}
buttonAttrs, err := currentCard.Card.Buttons(currentCard.Face)
if err != nil {
return errors.Wrap(err, "failed to get buttons list")
}
for i := 0; i < buttons.Length; i++ {
button := jQuery(buttons.Underlying().Index(i))
id := button.Attr("data-id")
button.Call("button")
attr, _ := buttonAttrs[(studyview.Button(id))] // I can ignore the ok value, because the nil value for attr works the same
name := attr.Name
if name == "" {
name = " "
}
button.SetText(name)
if attr.Enabled {
button.Call("button", "enable")
button.On("click", clickFunc)
} else {
button.Call("button", "disable")
}
}
oldIframes := jQuery("#cardframe", container).Find("iframe").Underlying()
for i := 0; i < oldIframes.Length(); i++ {
oldIframeID := oldIframes.Index(i).Get("src").String()
if err := iframes.UnregisterIframe(oldIframeID); err != nil {
log.Printf("Failed to unregister old iframe '%s': %s\n", oldIframeID, err)
}
js.Global.Get("URL").Call("revokeObjectURL", oldIframeID)
}
jQuery("#cardframe", container).Empty().Append(iframe)
jQuery(".show-until-load", container).Hide()
jQuery(".hide-until-load", container).Show()
currentCard.StartTime = time.Now()
return nil
}
func StudyInit() {
log.Debug("Registering iframes listener\n")
iframes.RegisterListener("submit", handleSubmit())
log.Debug("Done registering iframes listener\n")
}
func handleSubmit() func(string, *js.Object, iframes.Respond) error {
return func(cardID string, payload *js.Object, _ iframes.Respond) error {
card := currentCard.Card
face := currentCard.Face
if card.DocID() != cardID {
return errors.Errorf("received submit for unexpected card. Got %s, expected %s", cardID, card.DocID())
}
done, err := card.Action(context.TODO(), ¤tCard.Face, currentCard.StartTime, payload)
if err != nil {
log.Printf("Error executing card action for face %d / %+v: %s", face, card, err)
}
if done {
currentCard = nil
} else {
if face == currentCard.Face {
log.Printf("face wasn't incremented!\n")
}
}
// FIXME: Don't hard code /app here
jQuery(":mobile-pagecontainer").Call("pagecontainer", "change", "study.html")
return nil
}
}