Show unread notifications count bubble in web UI; add background scraping of notifications every 10s
This commit is contained in:
parent
72b547f6aa
commit
14ea626014
@ -118,6 +118,13 @@ func (app *Application) ChangeSession(w http.ResponseWriter, r *http.Request) {
|
|||||||
app.error_400_with_message(w, r, fmt.Sprintf("User not in database: %s", form.AccountName))
|
app.error_400_with_message(w, r, fmt.Sprintf("User not in database: %s", form.AccountName))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := NotificationBubbles{NumMessageNotifications: len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))}
|
app.LastReadNotificationSortIndex = 0 // Clear unread notifications
|
||||||
|
go app.background_notifications_scrape() // Update notifications info in background (avoid latency when switching users)
|
||||||
|
data := NotificationBubbles{
|
||||||
|
NumMessageNotifications: len(app.Profile.GetUnreadConversations(app.ActiveUser.ID)),
|
||||||
|
}
|
||||||
|
if app.LastReadNotificationSortIndex != 0 {
|
||||||
|
data.NumRegularNotifications = app.Profile.GetUnreadNotificationsCount(app.ActiveUser.ID, app.LastReadNotificationSortIndex)
|
||||||
|
}
|
||||||
app.buffered_render_htmx(w, "nav-sidebar", PageGlobalData{}, data)
|
app.buffered_render_htmx(w, "nav-sidebar", PageGlobalData{}, data)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,11 @@ func (app *Application) NavSidebarPollUpdates(w http.ResponseWriter, r *http.Req
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data := NotificationBubbles{NumMessageNotifications: len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))}
|
data := NotificationBubbles{
|
||||||
|
NumMessageNotifications: len(app.Profile.GetUnreadConversations(app.ActiveUser.ID)),
|
||||||
|
}
|
||||||
|
if app.LastReadNotificationSortIndex != 0 {
|
||||||
|
data.NumRegularNotifications = app.Profile.GetUnreadNotificationsCount(app.ActiveUser.ID, app.LastReadNotificationSortIndex)
|
||||||
|
}
|
||||||
app.buffered_render_htmx(w, "nav-sidebar", PageGlobalData{}, data)
|
app.buffered_render_htmx(w, "nav-sidebar", PageGlobalData{}, data)
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
|
|
||||||
type NotificationBubbles struct {
|
type NotificationBubbles struct {
|
||||||
NumMessageNotifications int
|
NumMessageNotifications int
|
||||||
|
NumRegularNotifications int
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this name sucks
|
// TODO: this name sucks
|
||||||
@ -99,6 +100,12 @@ func (app *Application) buffered_render_page(w http.ResponseWriter, tpl_file str
|
|||||||
partials := append(glob("tpl/includes/*.tpl"), glob("tpl/tweet_page_includes/*.tpl")...)
|
partials := append(glob("tpl/includes/*.tpl"), glob("tpl/tweet_page_includes/*.tpl")...)
|
||||||
|
|
||||||
global_data.NotificationBubbles.NumMessageNotifications = len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))
|
global_data.NotificationBubbles.NumMessageNotifications = len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))
|
||||||
|
if app.LastReadNotificationSortIndex != 0 {
|
||||||
|
global_data.NotificationBubbles.NumRegularNotifications = app.Profile.GetUnreadNotificationsCount(
|
||||||
|
app.ActiveUser.ID,
|
||||||
|
app.LastReadNotificationSortIndex,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
r := renderer{
|
r := renderer{
|
||||||
Funcs: app.make_funcmap(global_data),
|
Funcs: app.make_funcmap(global_data),
|
||||||
|
@ -28,10 +28,11 @@ type Application struct {
|
|||||||
|
|
||||||
Middlewares []Middleware
|
Middlewares []Middleware
|
||||||
|
|
||||||
Profile persistence.Profile
|
Profile persistence.Profile
|
||||||
ActiveUser scraper.User
|
ActiveUser scraper.User
|
||||||
IsScrapingDisabled bool
|
IsScrapingDisabled bool
|
||||||
API scraper.API
|
API scraper.API
|
||||||
|
LastReadNotificationSortIndex int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewApp(profile persistence.Profile) Application {
|
func NewApp(profile persistence.Profile) Application {
|
||||||
|
@ -945,8 +945,12 @@ main {
|
|||||||
* Notifications
|
* Notifications
|
||||||
******************************************************/
|
******************************************************/
|
||||||
|
|
||||||
|
.notifications-header {
|
||||||
|
border-bottom: 1px solid var(--color-outline-gray);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifications module
|
* Notification module
|
||||||
*/
|
*/
|
||||||
.notification {
|
.notification {
|
||||||
.notification__users {
|
.notification__users {
|
||||||
|
@ -118,6 +118,41 @@ func (app *Application) background_dm_polling_scrape() {
|
|||||||
fmt.Println("Scraping DMs succeeded.")
|
fmt.Println("Scraping DMs succeeded.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *Application) background_notifications_scrape() {
|
||||||
|
// Avoid crashing the thread if a scrape fails
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
// TODO
|
||||||
|
fmt.Println("Background notifications thread: panicked!")
|
||||||
|
if err, ok := r.(error); ok {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
} else {
|
||||||
|
fmt.Println(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Println("Starting notifications scrape...")
|
||||||
|
|
||||||
|
// Do nothing if scraping is currently disabled
|
||||||
|
if app.IsScrapingDisabled {
|
||||||
|
fmt.Println("Skipping notifications scrape!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Scraping user notifications...")
|
||||||
|
trove, last_unread_notification_sort_index, err := app.API.GetNotifications(1) // Just 1 page
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// Jot down the unread notifs info in the application object (to render notification count bubble)
|
||||||
|
app.LastReadNotificationSortIndex = last_unread_notification_sort_index
|
||||||
|
fmt.Println("Saving notification results...")
|
||||||
|
app.Profile.SaveTweetTrove(trove, false, &app.API)
|
||||||
|
go app.Profile.SaveTweetTrove(trove, true, &app.API)
|
||||||
|
fmt.Println("Scraping notification succeeded.")
|
||||||
|
}
|
||||||
|
|
||||||
func (app *Application) start_background() {
|
func (app *Application) start_background() {
|
||||||
fmt.Println("Starting background")
|
fmt.Println("Starting background")
|
||||||
|
|
||||||
@ -163,4 +198,16 @@ func (app *Application) start_background() {
|
|||||||
app.background_dm_polling_scrape()
|
app.background_dm_polling_scrape()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Scrape notifications every 10 seconds
|
||||||
|
go func() {
|
||||||
|
app.background_notifications_scrape()
|
||||||
|
|
||||||
|
interval := 10 * time.Second
|
||||||
|
timer := time.NewTicker(interval)
|
||||||
|
defer timer.Stop()
|
||||||
|
for range timer.C {
|
||||||
|
app.background_notifications_scrape()
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,11 @@
|
|||||||
</a>
|
</a>
|
||||||
{{if (not (eq (active_user).Handle "[nobody]"))}}
|
{{if (not (eq (active_user).Handle "[nobody]"))}}
|
||||||
<a href="/notifications">
|
<a href="/notifications">
|
||||||
<li class="button labelled-icon">
|
<li class="nav-sidebar__notifications button labelled-icon">
|
||||||
<img class="svg-icon" src="/static/icons/notifications.svg" width="24" height="24" />
|
<img class="svg-icon" src="/static/icons/notifications.svg" width="24" height="24" />
|
||||||
|
{{if .NumRegularNotifications}}
|
||||||
|
<span class="nav-sidebar__notifications-count">{{.NumRegularNotifications}}</span>
|
||||||
|
{{end}}
|
||||||
<label class="nav-sidebar__button-label">Notifications</label>
|
<label class="nav-sidebar__button-label">Notifications</label>
|
||||||
</li>
|
</li>
|
||||||
</a>
|
</a>
|
||||||
|
@ -2,7 +2,18 @@
|
|||||||
|
|
||||||
{{define "main"}}
|
{{define "main"}}
|
||||||
<div class="notifications-header">
|
<div class="notifications-header">
|
||||||
<h2>Notifications</h2>
|
<div class="row row--spread">
|
||||||
|
<div class="dummy"></div> {{/* Extra div to take up a slot in the `row` */}}
|
||||||
|
<h1>Notifications</h1>
|
||||||
|
<div class="row">
|
||||||
|
<a class="button" target="_blank" href="https://twitter.com/notifications" title="Open on twitter.com">
|
||||||
|
<img class="svg-icon" src="/static/icons/external-link.svg" width="24" height="24" />
|
||||||
|
</a>
|
||||||
|
<a class="button" hx-get="?scrape" hx-target="body" hx-indicator=".search-header" title="Refresh">
|
||||||
|
<img class="svg-icon" src="/static/icons/refresh.svg" width="24" height="24" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="timeline">
|
<div class="timeline">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user