Add notifications page
This commit is contained in:
parent
1f392f5240
commit
665e6a31dd
@ -118,6 +118,6 @@ 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 := Notifications{NumMessageNotifications: len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))}
|
data := NotificationBubbles{NumMessageNotifications: len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))}
|
||||||
app.buffered_render_htmx(w, "nav-sidebar", PageGlobalData{}, data)
|
app.buffered_render_htmx(w, "nav-sidebar", PageGlobalData{}, data)
|
||||||
}
|
}
|
||||||
|
11
internal/webserver/handler_notifications.go
Normal file
11
internal/webserver/handler_notifications.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package webserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (app *Application) Notifications(w http.ResponseWriter, r *http.Request) {
|
||||||
|
feed := app.Profile.GetNotificationsForUser(app.ActiveUser.ID, 0)
|
||||||
|
|
||||||
|
app.buffered_render_page(w, "tpl/notifications.tpl", PageGlobalData{TweetTrove: feed.TweetTrove}, feed)
|
||||||
|
}
|
@ -13,6 +13,6 @@ func (app *Application) NavSidebarPollUpdates(w http.ResponseWriter, r *http.Req
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data := Notifications{NumMessageNotifications: len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))}
|
data := NotificationBubbles{NumMessageNotifications: len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))}
|
||||||
app.buffered_render_htmx(w, "nav-sidebar", PageGlobalData{}, data)
|
app.buffered_render_htmx(w, "nav-sidebar", PageGlobalData{}, data)
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/scraper"
|
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/scraper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Notifications struct {
|
type NotificationBubbles struct {
|
||||||
NumMessageNotifications int
|
NumMessageNotifications int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,8 +24,8 @@ type PageGlobalData struct {
|
|||||||
scraper.TweetTrove
|
scraper.TweetTrove
|
||||||
SearchText string
|
SearchText string
|
||||||
FocusedTweetID scraper.TweetID
|
FocusedTweetID scraper.TweetID
|
||||||
Notifications
|
|
||||||
Toasts []Toast
|
Toasts []Toast
|
||||||
|
NotificationBubbles
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d PageGlobalData) Tweet(id scraper.TweetID) scraper.Tweet {
|
func (d PageGlobalData) Tweet(id scraper.TweetID) scraper.Tweet {
|
||||||
@ -40,6 +40,9 @@ func (d PageGlobalData) Retweet(id scraper.TweetID) scraper.Retweet {
|
|||||||
func (d PageGlobalData) Space(id scraper.SpaceID) scraper.Space {
|
func (d PageGlobalData) Space(id scraper.SpaceID) scraper.Space {
|
||||||
return d.Spaces[id]
|
return d.Spaces[id]
|
||||||
}
|
}
|
||||||
|
func (d PageGlobalData) Notification(id scraper.NotificationID) scraper.Notification {
|
||||||
|
return d.Notifications[id]
|
||||||
|
}
|
||||||
func (d PageGlobalData) Message(id scraper.DMMessageID) scraper.DMMessage {
|
func (d PageGlobalData) Message(id scraper.DMMessageID) scraper.DMMessage {
|
||||||
return d.Messages[id]
|
return d.Messages[id]
|
||||||
}
|
}
|
||||||
@ -95,7 +98,7 @@ func (r renderer) BufferedRender(w io.Writer) {
|
|||||||
func (app *Application) buffered_render_page(w http.ResponseWriter, tpl_file string, global_data PageGlobalData, tpl_data interface{}) {
|
func (app *Application) buffered_render_page(w http.ResponseWriter, tpl_file string, global_data PageGlobalData, tpl_data interface{}) {
|
||||||
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.Notifications.NumMessageNotifications = len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))
|
global_data.NotificationBubbles.NumMessageNotifications = len(app.Profile.GetUnreadConversations(app.ActiveUser.ID))
|
||||||
|
|
||||||
r := renderer{
|
r := renderer{
|
||||||
Funcs: app.make_funcmap(global_data),
|
Funcs: app.make_funcmap(global_data),
|
||||||
@ -127,6 +130,7 @@ func (app *Application) make_funcmap(global_data PageGlobalData) template.FuncMa
|
|||||||
"user": global_data.User,
|
"user": global_data.User,
|
||||||
"retweet": global_data.Retweet,
|
"retweet": global_data.Retweet,
|
||||||
"space": global_data.Space,
|
"space": global_data.Space,
|
||||||
|
"notification": global_data.Notification,
|
||||||
"dm_message": global_data.Message,
|
"dm_message": global_data.Message,
|
||||||
"chat_room": global_data.ChatRoom,
|
"chat_room": global_data.ChatRoom,
|
||||||
"focused_tweet_id": global_data.GetFocusedTweetID,
|
"focused_tweet_id": global_data.GetFocusedTweetID,
|
||||||
|
@ -132,6 +132,8 @@ func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.StripPrefix("/lists", http.HandlerFunc(app.Lists)).ServeHTTP(w, r)
|
http.StripPrefix("/lists", http.HandlerFunc(app.Lists)).ServeHTTP(w, r)
|
||||||
case "bookmarks":
|
case "bookmarks":
|
||||||
app.Bookmarks(w, r)
|
app.Bookmarks(w, r)
|
||||||
|
case "notifications":
|
||||||
|
app.Notifications(w, r)
|
||||||
case "messages":
|
case "messages":
|
||||||
http.StripPrefix("/messages", http.HandlerFunc(app.Messages)).ServeHTTP(w, r)
|
http.StripPrefix("/messages", http.HandlerFunc(app.Messages)).ServeHTTP(w, r)
|
||||||
case "nav-sidebar-poll-updates":
|
case "nav-sidebar-poll-updates":
|
||||||
|
@ -888,3 +888,22 @@ func TestMessagesPaginate(t *testing.T) {
|
|||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
assert.Len(cascadia.QueryAll(root, selector(".dm-message")), 2)
|
assert.Len(cascadia.QueryAll(root, selector(".dm-message")), 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNotifications(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
// Boilerplate for setting an active user
|
||||||
|
app := webserver.NewApp(profile)
|
||||||
|
app.IsScrapingDisabled = true
|
||||||
|
app.ActiveUser = scraper.User{ID: 1488963321701171204, Handle: "Offline_Twatter"} // Simulate a login
|
||||||
|
|
||||||
|
// Notifications page
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
req := httptest.NewRequest("GET", "/notifications", nil)
|
||||||
|
app.ServeHTTP(recorder, req)
|
||||||
|
resp := recorder.Result()
|
||||||
|
root, err := html.Parse(resp.Body)
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Len(cascadia.QueryAll(root, selector(".notification")), 6)
|
||||||
|
}
|
||||||
|
@ -543,7 +543,7 @@ main {
|
|||||||
/**
|
/**
|
||||||
* Tweet module
|
* Tweet module
|
||||||
*/
|
*/
|
||||||
.tweet {
|
.tweet, .notification {
|
||||||
padding: 0 1.5em;
|
padding: 0 1.5em;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 0; /* Dunno why, but without it, hovering a tweet with a Poll hides the poll fill bars */
|
z-index: 0; /* Dunno why, but without it, hovering a tweet with a Poll hides the poll fill bars */
|
||||||
@ -940,6 +940,21 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************
|
||||||
|
* Notifications
|
||||||
|
******************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifications module
|
||||||
|
*/
|
||||||
|
.notification {
|
||||||
|
.notification__users {
|
||||||
|
font-size: 0.8em; /* Make the profile images smaller */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/******************************************************
|
/******************************************************
|
||||||
* Navigation and base page
|
* Navigation and base page
|
||||||
******************************************************/
|
******************************************************/
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</header>
|
</header>
|
||||||
{{template "nav-sidebar" (global_data).Notifications}}
|
{{template "nav-sidebar" (global_data).NotificationBubbles}}
|
||||||
<main>
|
<main>
|
||||||
{{template "main" .}}
|
{{template "main" .}}
|
||||||
</main>
|
</main>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</a>
|
</a>
|
||||||
{{if (not (eq (active_user).Handle "[nobody]"))}}
|
{{if (not (eq (active_user).Handle "[nobody]"))}}
|
||||||
<a href="#">
|
<a href="/notifications">
|
||||||
<li class="button labelled-icon">
|
<li class="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" />
|
||||||
<label class="nav-sidebar__button-label">Notifications</label>
|
<label class="nav-sidebar__button-label">Notifications</label>
|
||||||
|
11
internal/webserver/tpl/notifications.tpl
Normal file
11
internal/webserver/tpl/notifications.tpl
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{{define "title"}}Notifications{{end}}
|
||||||
|
|
||||||
|
{{define "main"}}
|
||||||
|
<div class="notifications-header">
|
||||||
|
<h2>Notifications</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="timeline">
|
||||||
|
{{template "timeline" .}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
60
internal/webserver/tpl/tweet_page_includes/notification.tpl
Normal file
60
internal/webserver/tpl/tweet_page_includes/notification.tpl
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{{define "notification"}}
|
||||||
|
{{$notification := (notification .NotificationID)}}
|
||||||
|
|
||||||
|
<div class="notification">
|
||||||
|
<div class="notification__header">
|
||||||
|
{{if (not (eq $notification.ActionUserID 0))}}
|
||||||
|
<div class="notification__users">
|
||||||
|
{{template "circle-profile-img" (user $notification.ActionUserID)}}
|
||||||
|
{{/*template "author-info" (user $notification.ActionUserID)*/}}
|
||||||
|
{{if (gt (len $notification.UserIDs) 1)}}
|
||||||
|
{{$max_display_users := 10}}
|
||||||
|
{{range $i, $user_id := $notification.UserIDs}}
|
||||||
|
{{if (ne $user_id $notification.ActionUserID)}} {{/* don't duplicate main user */}}
|
||||||
|
{{/* Only render the first 10-ish users */}}
|
||||||
|
{{if (lt $i $max_display_users)}}
|
||||||
|
{{template "circle-profile-img" (user $user_id)}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if (gt (len $notification.UserIDs) (add $max_display_users 1))}}
|
||||||
|
<span class="ellipsis">...</span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<div class="notification__text">
|
||||||
|
{{if (eq $notification.Type 1)}} {{/* LIKE */}}
|
||||||
|
<b>{{(user $notification.ActionUserID).DisplayName}} liked your tweet</b>
|
||||||
|
{{else if (eq $notification.Type 2)}} {{/* RETWEET */}}
|
||||||
|
<b>{{(user $notification.ActionUserID).DisplayName}} retweeted you</b>
|
||||||
|
{{else if (eq $notification.Type 3)}} {{/* QUOTE_TWEET */}}
|
||||||
|
<b>{{(user $notification.ActionUserID).DisplayName}} quote-tweeted you</b>
|
||||||
|
{{else if (eq $notification.Type 4)}} {{/* REPLY */}}
|
||||||
|
<b>{{(user $notification.ActionUserID).DisplayName}} replied to you</b>
|
||||||
|
{{else if (eq $notification.Type 5)}} {{/* FOLLOW */}}
|
||||||
|
<b>{{(user $notification.ActionUserID).DisplayName}} followed you!</b>
|
||||||
|
{{else if (eq $notification.Type 6)}} {{/* MENTION */}}
|
||||||
|
<b>{{(user $notification.ActionUserID).DisplayName}} mentioned you</b>
|
||||||
|
{{else if (eq $notification.Type 7)}} {{/* USER_IS_LIVE */}}
|
||||||
|
<b>{{(user $notification.ActionUserID).DisplayName}} is live</b>
|
||||||
|
{{else if (eq $notification.Type 8)}} {{/* POLL_ENDED */}}
|
||||||
|
<b>Poll ended.</b>
|
||||||
|
{{else if (eq $notification.Type 9)}} {{/* LOGIN */}}
|
||||||
|
<b>New login on your account.</b>
|
||||||
|
{{else if (eq $notification.Type 10)}} {{/* COMMUNITY_PINNED_POST */}}
|
||||||
|
<b>{{(user $notification.ActionUserID).DisplayName}} posted in community</b>
|
||||||
|
{{else if (eq $notification.Type 11)}} {{/* RECOMMENDED_POST */}}
|
||||||
|
<b>You've been recommended a post from {{(user $notification.ActionUserID).DisplayName}}</b>
|
||||||
|
{{else}}
|
||||||
|
<b>{{"<<UNKNOWN ID>>: "}}{{$notification.Type}}</b>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{if (ne .TweetID 0)}}
|
||||||
|
{{template "tweet" .}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
@ -1,7 +1,11 @@
|
|||||||
{{define "timeline"}}
|
{{define "timeline"}}
|
||||||
{{range .Items}}
|
{{range .Items}}
|
||||||
|
{{if .NotificationID}}
|
||||||
|
{{template "notification" .}}
|
||||||
|
{{else}}
|
||||||
{{template "tweet" .}}
|
{{template "tweet" .}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<div class="show-more">
|
<div class="show-more">
|
||||||
{{if .CursorBottom.CursorPosition.IsEnd}}
|
{{if .CursorBottom.CursorPosition.IsEnd}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user