Add marking notifications as read
This commit is contained in:
parent
212c1b4e50
commit
faac7e9b16
@ -83,7 +83,7 @@ func main() {
|
||||
if len(args) < 2 {
|
||||
if len(args) == 1 && (args[0] == "webserver" || args[0] == "fetch_timeline" ||
|
||||
args[0] == "fetch_timeline_following_only" || args[0] == "fetch_inbox" || args[0] == "get_bookmarks" ||
|
||||
args[0] == "get_notifications") {
|
||||
args[0] == "get_notifications" || args[0] == "mark_notifications_as_read") {
|
||||
// Doesn't need a target, so create a fake second arg
|
||||
args = append(args, "")
|
||||
} else {
|
||||
@ -195,6 +195,8 @@ func main() {
|
||||
fetch_timeline(true)
|
||||
case "get_notifications":
|
||||
get_notifications(*how_many)
|
||||
case "mark_notifications_as_read":
|
||||
mark_notification_as_read()
|
||||
case "download_tweet_content":
|
||||
download_tweet_content(target)
|
||||
case "search":
|
||||
@ -664,3 +666,10 @@ func get_notifications(how_many int) {
|
||||
len(trove.Notifications), len(trove.Tweets), len(trove.Users),
|
||||
), nil)
|
||||
}
|
||||
|
||||
func mark_notification_as_read() {
|
||||
if err := api.MarkNotificationsAsRead(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
happy_exit("Notifications marked as read", nil)
|
||||
}
|
||||
|
@ -3,9 +3,17 @@ package webserver
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (app *Application) Notifications(w http.ResponseWriter, r *http.Request) {
|
||||
app.traceLog.Printf("'Notifications' handler (path: %q)", r.URL.Path)
|
||||
parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
|
||||
if parts[0] == "mark-all-as-read" {
|
||||
app.NotificationsMarkAsRead(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
cursor_val := 0
|
||||
cursor_param := r.URL.Query().Get("cursor")
|
||||
if cursor_param != "" {
|
||||
@ -26,3 +34,16 @@ func (app *Application) Notifications(w http.ResponseWriter, r *http.Request) {
|
||||
app.buffered_render_page(w, "tpl/notifications.tpl", PageGlobalData{TweetTrove: feed.TweetTrove}, feed)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *Application) NotificationsMarkAsRead(w http.ResponseWriter, r *http.Request) {
|
||||
err := app.API.MarkNotificationsAsRead()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.toast(w, r, Toast{
|
||||
Title: "Success",
|
||||
Message: `Notifications marked as "read"`,
|
||||
Type: "success",
|
||||
AutoCloseDelay: 2000,
|
||||
})
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
case "bookmarks":
|
||||
app.Bookmarks(w, r)
|
||||
case "notifications":
|
||||
app.Notifications(w, r)
|
||||
http.StripPrefix("/notifications", http.HandlerFunc(app.Notifications)).ServeHTTP(w, r)
|
||||
case "messages":
|
||||
http.StripPrefix("/messages", http.HandlerFunc(app.Messages)).ServeHTTP(w, r)
|
||||
case "nav-sidebar-poll-updates":
|
||||
|
@ -6,17 +6,28 @@
|
||||
<div class="dummy"></div> {{/* Extra div to take up a slot in the `row` */}}
|
||||
<h1>Notifications</h1>
|
||||
<div class="row">
|
||||
<a class="button" hx-post="/notifications/mark-all-as-read" hx-indicator=".notifications-timeline" title="Mark all as read">
|
||||
<img class="svg-icon" src="/static/icons/eye.svg" width="24" height="24" />
|
||||
</a>
|
||||
<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">
|
||||
<a class="button" hx-get="?scrape" hx-target="body" hx-indicator=".notifications-timeline" title="Refresh">
|
||||
<img class="svg-icon" src="/static/icons/refresh.svg" width="24" height="24" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="timeline">
|
||||
{{template "timeline" .}}
|
||||
<div class="notifications-timeline">
|
||||
<div class="htmx-spinner">
|
||||
<div class="htmx-spinner__fullscreen-forcer">
|
||||
<div class="htmx-spinner__background"></div>
|
||||
<img class="svg-icon htmx-spinner__icon" src="/static/icons/spinner.svg" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline">
|
||||
{{template "timeline" .}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -526,6 +526,17 @@ func (t *TweetResponse) GetCursor() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *TweetResponse) GetCursorTop() string {
|
||||
for _, instr := range t.Timeline.Instructions {
|
||||
for _, entry := range instr.AddEntries.Entries {
|
||||
if strings.Contains(entry.EntryID, "cursor-top") {
|
||||
return entry.Content.Operation.Cursor.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for one case of end-of-feed. Cursor increments on each request for some reason, but
|
||||
* there's no new content. This seems to happen when there's a pinned tweet.
|
||||
|
@ -63,6 +63,25 @@ func (api *API) GetNotifications(how_many int) (TweetTrove, int64, error) {
|
||||
return trove, resp.CheckUnreadNotifications(), nil
|
||||
}
|
||||
|
||||
func (api *API) MarkNotificationsAsRead() error {
|
||||
resp, err := api.GetNotificationsPage("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cursor := resp.GetCursorTop()
|
||||
if cursor == "" {
|
||||
panic(fmt.Sprintf("No top cursor found: \n%#v", resp))
|
||||
}
|
||||
rslt := struct {
|
||||
Cursor string `json:"cursor"`
|
||||
}{}
|
||||
api.do_http_POST("https://twitter.com/i/api/2/notifications/all/last_seen_cursor.json", "cursor=" + cursor, &rslt)
|
||||
if rslt.Cursor == "" {
|
||||
panic("got blank cursor back...?")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check a Notifications result for unread notifications. Returns `0` if there are none.
|
||||
func (t TweetResponse) CheckUnreadNotifications() int64 {
|
||||
for _, instr := range t.Timeline.Instructions {
|
||||
|
@ -163,6 +163,9 @@ func TestParseNotificationsPage(t *testing.T) {
|
||||
bottom_cursor := resp.GetCursor()
|
||||
assert.Equal("DAACDAABCgABFKncQJGVgAQIAAIAAAABCAADSQ3bEQgABIsN6BEACwACAAAAC0FaRkxRSXFNLTJJAAA", bottom_cursor)
|
||||
assert.False(resp.IsEndOfFeed())
|
||||
|
||||
// Test cursor-top
|
||||
assert.Equal(resp.GetCursorTop(), "DAABDAABCgABFKncQJGVgAQIAAIAAAABCAADSQ3bEQgABIsN6BEACwACAAAAC0FaR0lLcUhkUVhrAAA")
|
||||
}
|
||||
|
||||
func TestParseNotificationsEndOfFeed(t *testing.T) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user