diff options
| author | AKP <tom@tdpain.net> | 2022-04-29 21:15:52 +0100 |
|---|---|---|
| committer | AKP <tom@tdpain.net> | 2022-04-29 21:18:54 +0100 |
| commit | dbb99a4aaad2c3ddd88acd1ae5d7ebccc1973ec8 (patch) | |
| tree | be39793840c180d131f52df8564034bc80c97d80 | |
| parent | ac98ff7320d131b5c883d0d447bfbd2d8162f9de (diff) | |
Add progress indicator to test email button
Signed-off-by: AKP <tom@tdpain.net>
| -rw-r--r-- | walrss/internal/http/http.go | 1 | ||||
| -rw-r--r-- | walrss/internal/http/testEmail.go | 62 | ||||
| -rw-r--r-- | walrss/internal/http/views/main.qtpl.html | 27 | ||||
| -rw-r--r-- | walrss/internal/http/views/main.qtpl.html.go | 53 | ||||
| -rw-r--r-- | walrss/internal/rss/processor.go | 40 | ||||
| -rw-r--r-- | walrss/internal/urls/urls.go | 3 |
6 files changed, 170 insertions, 16 deletions
diff --git a/walrss/internal/http/http.go b/walrss/internal/http/http.go index 3909772..36255bc 100644 --- a/walrss/internal/http/http.go +++ b/walrss/internal/http/http.go @@ -90,6 +90,7 @@ func (s *Server) registerHandlers() { s.app.Post(urls.NewFeedItem, s.newFeedItem) s.app.Post(urls.SendTestEmail, s.sendTestEmail) + s.app.Get(urls.TestEmailStatus, s.testEmailStatus) s.app.Get(urls.ExportAsOPML, s.exportAsOPML) s.app.Post(urls.ImportFromOPML, s.importFromOPML) diff --git a/walrss/internal/http/testEmail.go b/walrss/internal/http/testEmail.go index 1aab514..5070cbf 100644 --- a/walrss/internal/http/testEmail.go +++ b/walrss/internal/http/testEmail.go @@ -2,10 +2,15 @@ package http import ( "github.com/codemicro/walrss/walrss/internal/core" + "github.com/codemicro/walrss/walrss/internal/http/views" "github.com/codemicro/walrss/walrss/internal/rss" "github.com/codemicro/walrss/walrss/internal/urls" "github.com/gofiber/fiber/v2" "github.com/rs/zerolog/log" + "strconv" + "strings" + "sync" + "time" ) func (s *Server) sendTestEmail(ctx *fiber.Ctx) error { @@ -19,12 +24,63 @@ func (s *Server) sendTestEmail(ctx *fiber.Ctx) error { return err } + testEmailStatesLock.Lock() + testEmailStates[currentUserID] = testEmailStates[currentUserID] + "Starting at " + time.Now().UTC().Format(time.RFC1123Z) + "\n" + testEmailStatesLock.Unlock() + go func() { - if err := rss.ProcessUserFeed(s.state, user); err != nil { + status := make(chan string, 50) + + var err error + go func() { + err = rss.ProcessUserFeed(s.state, user, status) + }() + + for statusAddition := range status { + testEmailStatesLock.Lock() + testEmailStates[currentUserID] = testEmailStates[currentUserID] + statusAddition + "\n" + testEmailStatesLock.Unlock() + } + + if err != nil { log.Error().Err(err).Str("location", "test email").Str("user", user.ID).Send() } }() - fragmentEmitSuccess(ctx) - return nil + return s.testEmailStatus(ctx) +} + +var ( + testEmailStates = make(map[string]string) + testEmailStatesLock sync.RWMutex +) + +func (s *Server) testEmailStatus(ctx *fiber.Ctx) error { + currentUserID := getCurrentUserID(ctx) + if currentUserID == "" { + return requestFragmentSignIn(ctx, urls.Index) + } + + end, _ := strconv.ParseBool(ctx.Query("end", "false")) + + testEmailStatesLock.RLock() + var content string + if end { + delete(testEmailStates, currentUserID) + } else { + content = testEmailStates[currentUserID] + } + defer testEmailStatesLock.RUnlock() + + if end { + ctx.Set("HX-Refresh", "true") + return nil + } + + var endOnNext bool + if strings.HasSuffix(strings.TrimSpace(content), "Done!") { + endOnNext = true + fragmentEmitSuccess(ctx) + } + return ctx.SendString(views.RenderTestEmailBox(content, endOnNext)) } diff --git a/walrss/internal/http/views/main.qtpl.html b/walrss/internal/http/views/main.qtpl.html index 7fc1638..191f8f7 100644 --- a/walrss/internal/http/views/main.qtpl.html +++ b/walrss/internal/http/views/main.qtpl.html @@ -1,6 +1,7 @@ {% import "github.com/codemicro/walrss/walrss/internal/db" %} {% import "github.com/codemicro/walrss/walrss/internal/urls" %} {% import "sort" %} +{% import "strings" %} {% import "github.com/lithammer/shortuuid/v4" %} {% code type MainPage struct { @@ -243,9 +244,9 @@ {% func (p *MainPage) RenderScheduleCard() %} <div class="card mt-3" id="scheduleCard" hx-target="this" hx-swap="outerHTML"> <div class="card-header"> - Schedule + Email settings </div> - <div class="card-body"> + <div class="card-body" id="scheduleCardBody"> <div class="mb-2 row row-cols-lg-auto align-items-center"> <div class="col-12"> @@ -329,8 +330,28 @@ </div> </form> - <button class="mt-2 btn btn-primary btn-sm" hx-post="{%s= urls.SendTestEmail %}" hx-swap="none">Send test email</button> + <button class="mt-2 btn btn-primary btn-sm" hx-post="{%s= urls.SendTestEmail %}" hx-target="#scheduleCardBody">Send test email</button> </div> </div> +{% endfunc %} + +{% func RenderTestEmailBox(content string, endOnNext bool) %} +{% code +url := urls.TestEmailStatus +if endOnNext { + url += "?end=true" +} +parts := strings.Split(strings.TrimSpace(content), "\n") +if len(parts) > 7 { + parts = parts[len(parts)-7:] +} +%} +<div class="card-body" hx-get="{%s= url %}" hx-trigger="load delay:1s" hx-target="this" hx-swap="outerHTML"> + <h3>Test status</h3> + + {% for _, line := range parts %} + <span>{%s line %}</span><br> + {% endfor %} +</div> {% endfunc %}
\ No newline at end of file diff --git a/walrss/internal/http/views/main.qtpl.html.go b/walrss/internal/http/views/main.qtpl.html.go index 7eaac9b..e078f7d 100644 --- a/walrss/internal/http/views/main.qtpl.html.go +++ b/walrss/internal/http/views/main.qtpl.html.go @@ -9,6 +9,8 @@ import "github.com/codemicro/walrss/walrss/internal/urls" import "sort" +import "strings" + import "github.com/lithammer/shortuuid/v4" import ( @@ -415,9 +417,9 @@ func (p *MainPage) StreamRenderScheduleCard(qw422016 *qt422016.Writer) { qw422016.N().S(` <div class="card mt-3" id="scheduleCard" hx-target="this" hx-swap="outerHTML"> <div class="card-header"> - Schedule + Email settings </div> - <div class="card-body"> + <div class="card-body" id="scheduleCardBody"> <div class="mb-2 row row-cols-lg-auto align-items-center"> <div class="col-12"> @@ -550,7 +552,7 @@ func (p *MainPage) StreamRenderScheduleCard(qw422016 *qt422016.Writer) { <button class="mt-2 btn btn-primary btn-sm" hx-post="`) qw422016.N().S(urls.SendTestEmail) - qw422016.N().S(`" hx-swap="none">Send test email</button> + qw422016.N().S(`" hx-target="#scheduleCardBody">Send test email</button> </div> </div> @@ -570,3 +572,48 @@ func (p *MainPage) RenderScheduleCard() string { qt422016.ReleaseByteBuffer(qb422016) return qs422016 } + +func StreamRenderTestEmailBox(qw422016 *qt422016.Writer, content string, endOnNext bool) { + qw422016.N().S(` +`) + url := urls.TestEmailStatus + if endOnNext { + url += "?end=true" + } + parts := strings.Split(strings.TrimSpace(content), "\n") + if len(parts) > 7 { + parts = parts[len(parts)-7:] + } + + qw422016.N().S(` +<div class="card-body" hx-get="`) + qw422016.N().S(url) + qw422016.N().S(`" hx-trigger="load delay:1s" hx-target="this" hx-swap="outerHTML"> + <h3>Test status</h3> + + `) + for _, line := range parts { + qw422016.N().S(` + <span>`) + qw422016.E().S(line) + qw422016.N().S(`</span><br> + `) + } + qw422016.N().S(` +</div> +`) +} + +func WriteRenderTestEmailBox(qq422016 qtio422016.Writer, content string, endOnNext bool) { + qw422016 := qt422016.AcquireWriter(qq422016) + StreamRenderTestEmailBox(qw422016, content, endOnNext) + qt422016.ReleaseWriter(qw422016) +} + +func RenderTestEmailBox(content string, endOnNext bool) string { + qb422016 := qt422016.AcquireByteBuffer() + WriteRenderTestEmailBox(qb422016, content, endOnNext) + qs422016 := string(qb422016.B) + qt422016.ReleaseByteBuffer(qb422016) + return qs422016 +} diff --git a/walrss/internal/rss/processor.go b/walrss/internal/rss/processor.go index af6f902..d5c5045 100644 --- a/walrss/internal/rss/processor.go +++ b/walrss/internal/rss/processor.go @@ -40,7 +40,7 @@ func ProcessFeeds(st *state.State, day db.SendDay, hour int) error { } for _, ur := range u { - if err := ProcessUserFeed(st, ur); err != nil { + if err := ProcessUserFeed(st, ur, nil); err != nil { log.Warn().Err(err).Str("user", ur.ID).Msg("could not process feeds for user") } } @@ -48,14 +48,30 @@ func ProcessFeeds(st *state.State, day db.SendDay, hour int) error { return nil } -func ProcessUserFeed(st *state.State, user *db.User) error { +func reportProgress(channel chan string, msg string) { + if channel == nil { + return + } + channel <- msg +} + +func ProcessUserFeed(st *state.State, user *db.User, progressChannel chan string) error { + defer func() { + if progressChannel == nil { + return + } + close(progressChannel) // This is important! There's a chance that if this is not done before ProcessUserFeed + // exits, the caller completely hang on this thread. + }() + + reportProgress(progressChannel, "Fetching feed list") userFeeds, err := core.GetFeedsForUser(st, user.ID) if err != nil { return err } var interval time.Duration - if user.Schedule.Day == db.SendDaily { + if user.Schedule.Day == db.SendDaily || user.Schedule.Day == db.SendDayNever { interval = time.Hour * 24 } else { interval = time.Hour * 24 * 7 @@ -65,31 +81,43 @@ func ProcessUserFeed(st *state.State, user *db.User) error { startTime := time.Now().UTC() - for _, f := range userFeeds { + reportProgress(progressChannel, "Fetching feed content") + + for i, f := range userFeeds { + reportProgress(progressChannel, fmt.Sprintf("Fetching feed %d of %d: %s", i+1, len(userFeeds), f.Name)) pf := new(processedFeed) pf.Name = f.Name rawFeed, err := getFeedContent(f.URL) if err != nil { pf.Error = err + reportProgress(progressChannel, "Failed to fetch: "+err.Error()) } else { - pf.Items = filterFeedContent(rawFeed, time.Now().UTC().Add(-interval)) + pf.Items = filterFeedContent(rawFeed, time.Now().Add(-time.Hour*24*3).UTC().Add(-interval)) } processedFeeds = append(processedFeeds, pf) } + reportProgress(progressChannel, "Finished fetching feed content\nGenerating email") + plainContent, htmlContent, err := generateEmail(st, processedFeeds, interval, time.Now().UTC().Sub(startTime)) if err != nil { return err } - return sendEmail( + reportProgress(progressChannel, "Sending email") + + err = sendEmail( st, plainContent, htmlContent, user.Email, "RSS digest for "+time.Now().UTC().Format(dateFormat), ) + + reportProgress(progressChannel, "Done!") + + return err } var ( diff --git a/walrss/internal/urls/urls.go b/walrss/internal/urls/urls.go index 6452aab..5cf166b 100644 --- a/walrss/internal/urls/urls.go +++ b/walrss/internal/urls/urls.go @@ -27,7 +27,8 @@ const ( New = "/new" NewFeedItem = New + "/feed" - SendTestEmail = "/send/test" + SendTestEmail = "/send/test" + TestEmailStatus = "/send/test/status" Statics = "/statics" ) |
