bloat
database
+bloat.def.conf
--- /dev/null
+Here's a list of known bugs in bloat:
+
+- <frameset> and <frame> tags are not supported in HTML5
package model
type Settings struct {
- DefaultVisibility string `json:"default_visibility"`
- CopyScope bool `json:"copy_scope"`
- ThreadInNewTab bool `json:"thread_in_new_tab"`
- MaskNSFW bool `json:"mask_nfsw"`
- FluorideMode bool `json:"fluoride_mode"`
- DarkMode bool `json:"dark_mode"`
+ DefaultVisibility string `json:"default_visibility"`
+ CopyScope bool `json:"copy_scope"`
+ ThreadInNewTab bool `json:"thread_in_new_tab"`
+ MaskNSFW bool `json:"mask_nfsw"`
+ AutoRefreshNotifications bool `json:"auto_refresh_notifications"`
+ FluorideMode bool `json:"fluoride_mode"`
+ DarkMode bool `json:"dark_mode"`
}
func NewSettings() *Settings {
return &Settings{
- DefaultVisibility: "public",
- CopyScope: true,
- ThreadInNewTab: false,
- MaskNSFW: true,
- FluorideMode: false,
- DarkMode: false,
+ DefaultVisibility: "public",
+ CopyScope: true,
+ ThreadInNewTab: false,
+ MaskNSFW: true,
+ AutoRefreshNotifications: false,
+ FluorideMode: false,
+ DarkMode: false,
}
}
UserID string
}
-type HeaderData struct {
- Title string
- NotificationCount int
- CustomCSS string
- CSRFToken string
-}
-
-type NavbarData struct {
- User *mastodon.Account
- NotificationCount int
+type NavData struct {
+ CommonData *CommonData
+ User *mastodon.Account
+ PostContext model.PostContext
}
type CommonData struct {
- HeaderData *HeaderData
- NavbarData *NavbarData
-}
-
-func (c CommonData) IsCurrentUser(id string) bool {
- if c.NavbarData != nil &&
- c.NavbarData.User != nil &&
- c.NavbarData.User.ID == id {
- return true
- }
- return false
+ Title string
+ CustomCSS string
+ CSRFToken string
+ AutoRefresh bool
}
type ErrorData struct {
*CommonData
}
+type RootData struct {
+ Title string
+}
+
type TimelineData struct {
*CommonData
- Title string
- Statuses []*mastodon.Status
- NextLink string
- PrevLink string
- PostContext model.PostContext
+ Title string
+ Statuses []*mastodon.Status
+ NextLink string
+ PrevLink string
}
type ThreadData struct {
type NotificationData struct {
*CommonData
Notifications []*mastodon.Notification
+ UnreadCount int
+ ReadID string
NextLink string
- DarkMode bool
}
type UserData struct {
Users []*mastodon.Account
Statuses []*mastodon.Status
NextLink string
- DarkMode bool
}
type UserSearchData struct {
type Renderer interface {
RenderSigninPage(ctx *Context, writer io.Writer, data *SigninData) (err error)
RenderErrorPage(ctx *Context, writer io.Writer, data *ErrorData)
+ RenderRootPage(ctx *Context, writer io.Writer, data *RootData) (err error)
+ RenderNavPage(ctx *Context, writer io.Writer, data *NavData) (err error)
RenderTimelinePage(ctx *Context, writer io.Writer, data *TimelineData) (err error)
RenderThreadPage(ctx *Context, writer io.Writer, data *ThreadData) (err error)
RenderNotificationPage(ctx *Context, writer io.Writer, data *NotificationData) (err error)
return
}
+func (r *renderer) RenderNavPage(ctx *Context, writer io.Writer,
+ data *NavData) (err error) {
+ return r.template.ExecuteTemplate(writer, "nav.tmpl", WithContext(data, ctx))
+}
+
+func (r *renderer) RenderRootPage(ctx *Context, writer io.Writer,
+ data *RootData) (err error) {
+ return r.template.ExecuteTemplate(writer, "root.tmpl", WithContext(data, ctx))
+}
+
func (r *renderer) RenderTimelinePage(ctx *Context, writer io.Writer,
data *TimelineData) (err error) {
return r.template.ExecuteTemplate(writer, "timeline.tmpl", WithContext(data, ctx))
return s.Service.ServeSigninPage(ctx, c)
}
+func (s *as) ServeRootPage(ctx context.Context, c *model.Client) (err error) {
+ err = s.authenticateClient(ctx, c)
+ if err != nil {
+ return
+ }
+ return s.Service.ServeRootPage(ctx, c)
+}
+
+func (s *as) ServeNavPage(ctx context.Context, c *model.Client) (err error) {
+ err = s.authenticateClient(ctx, c)
+ if err != nil {
+ return
+ }
+ return s.Service.ServeNavPage(ctx, c)
+}
+
func (s *as) ServeTimelinePage(ctx context.Context, c *model.Client, tType string,
maxID string, minID string) (err error) {
err = s.authenticateClient(ctx, c)
}
return s.Service.Delete(ctx, c, id)
}
+
+func (s *as) ReadNotifications(ctx context.Context, c *model.Client,
+ maxID string) (err error) {
+ err = s.authenticateClient(ctx, c)
+ if err != nil {
+ return
+ }
+ err = checkCSRF(ctx, c)
+ if err != nil {
+ return
+ }
+ return s.Service.ReadNotifications(ctx, c, maxID)
+}
return s.Service.ServeSigninPage(ctx, c)
}
+func (s *ls) ServeRootPage(ctx context.Context, c *model.Client) (err error) {
+ defer func(begin time.Time) {
+ s.logger.Printf("method=%v, took=%v, err=%v\n",
+ "ServeRootPage", time.Since(begin), err)
+ }(time.Now())
+ return s.Service.ServeRootPage(ctx, c)
+}
+
+func (s *ls) ServeNavPage(ctx context.Context, c *model.Client) (err error) {
+ defer func(begin time.Time) {
+ s.logger.Printf("method=%v, took=%v, err=%v\n",
+ "ServeNavPage", time.Since(begin), err)
+ }(time.Now())
+ return s.Service.ServeNavPage(ctx, c)
+}
+
func (s *ls) ServeTimelinePage(ctx context.Context, c *model.Client, tType string,
maxID string, minID string) (err error) {
defer func(begin time.Time) {
}(time.Now())
return s.Service.Delete(ctx, c, id)
}
+
+func (s *ls) ReadNotifications(ctx context.Context, c *model.Client,
+ maxID string) (err error) {
+ defer func(begin time.Time) {
+ s.logger.Printf("method=%v, max_id=%v, took=%v, err=%v\n",
+ "ReadNotifications", maxID, time.Since(begin), err)
+ }(time.Now())
+ return s.Service.ReadNotifications(ctx, c, maxID)
+}
type Service interface {
ServeErrorPage(ctx context.Context, c *model.Client, err error)
ServeSigninPage(ctx context.Context, c *model.Client) (err error)
+ ServeRootPage(ctx context.Context, c *model.Client) (err error)
+ ServeNavPage(ctx context.Context, c *model.Client) (err error)
ServeTimelinePage(ctx context.Context, c *model.Client, tType string, maxID string, minID string) (err error)
ServeThreadPage(ctx context.Context, c *model.Client, id string, reply bool) (err error)
ServeLikedByPage(ctx context.Context, c *model.Client, id string) (err error)
MuteConversation(ctx context.Context, c *model.Client, id string) (err error)
UnMuteConversation(ctx context.Context, c *model.Client, id string) (err error)
Delete(ctx context.Context, c *model.Client, id string) (err error)
+ ReadNotifications(ctx context.Context, c *model.Client, maxID string) (err error)
}
type service struct {
}
func (svc *service) getCommonData(ctx context.Context, c *model.Client,
- title string) (data *renderer.CommonData, err error) {
-
- data = new(renderer.CommonData)
- data.HeaderData = &renderer.HeaderData{
- Title: title + " - " + svc.clientName,
- NotificationCount: 0,
- CustomCSS: svc.customCSS,
- }
-
- if c == nil || !c.Session.IsLoggedIn() {
- return
- }
-
- notifications, err := c.GetNotifications(ctx, nil)
- if err != nil {
- return nil, err
- }
-
- var notificationCount int
- for i := range notifications {
- if notifications[i].Pleroma != nil &&
- !notifications[i].Pleroma.IsSeen {
- notificationCount++
- }
- }
-
- u, err := c.GetAccountCurrentUser(ctx)
- if err != nil {
- return nil, err
+ title string) (data *renderer.CommonData) {
+ data = &renderer.CommonData{
+ Title: title + " - " + svc.clientName,
+ CustomCSS: svc.customCSS,
}
-
- data.NavbarData = &renderer.NavbarData{
- User: u,
- NotificationCount: notificationCount,
+ if c != nil && c.Session.IsLoggedIn() {
+ data.CSRFToken = c.Session.CSRFToken
}
-
- data.HeaderData.NotificationCount = notificationCount
- data.HeaderData.CSRFToken = c.Session.CSRFToken
-
return
}
errStr = err.Error()
}
- commonData, err := svc.getCommonData(ctx, nil, "error")
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, nil, "error")
data := &renderer.ErrorData{
CommonData: commonData,
Error: errStr,
func (svc *service) ServeSigninPage(ctx context.Context, c *model.Client) (
err error) {
- commonData, err := svc.getCommonData(ctx, nil, "signin")
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, nil, "signin")
data := &renderer.SigninData{
CommonData: commonData,
}
return svc.renderer.RenderSigninPage(rCtx, c.Writer, data)
}
+func (svc *service) ServeRootPage(ctx context.Context, c *model.Client) (err error) {
+ data := &renderer.RootData{
+ Title: svc.clientName,
+ }
+
+ rCtx := getRendererContext(c)
+ return svc.renderer.RenderRootPage(rCtx, c.Writer, data)
+}
+
+func (svc *service) ServeNavPage(ctx context.Context, c *model.Client) (err error) {
+ u, err := c.GetAccountCurrentUser(ctx)
+ if err != nil {
+ return
+ }
+
+ postContext := model.PostContext{
+ DefaultVisibility: c.Session.Settings.DefaultVisibility,
+ Formats: svc.postFormats,
+ }
+
+ commonData := svc.getCommonData(ctx, c, "Nav")
+ data := &renderer.NavData{
+ User: u,
+ CommonData: commonData,
+ PostContext: postContext,
+ }
+
+ rCtx := getRendererContext(c)
+ return svc.renderer.RenderNavPage(rCtx, c.Writer, data)
+}
+
func (svc *service) ServeTimelinePage(ctx context.Context, c *model.Client,
tType string, maxID string, minID string) (err error) {
nextLink = fmt.Sprintf("/timeline/%s?max_id=%s", tType, pg.MaxID)
}
- postContext := model.PostContext{
- DefaultVisibility: c.Session.Settings.DefaultVisibility,
- Formats: svc.postFormats,
- }
-
- commonData, err := svc.getCommonData(ctx, c, tType+" timeline ")
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, c, tType+" timeline ")
data := &renderer.TimelineData{
- Title: title,
- Statuses: statuses,
- NextLink: nextLink,
- PrevLink: prevLink,
- PostContext: postContext,
- CommonData: commonData,
+ Title: title,
+ Statuses: statuses,
+ NextLink: nextLink,
+ PrevLink: prevLink,
+ CommonData: commonData,
}
rCtx := getRendererContext(c)
addToReplyMap(replies, statuses[i].InReplyToID, statuses[i].ID, i+1)
}
- commonData, err := svc.getCommonData(ctx, c, "post by "+status.Account.DisplayName)
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, c, "post by "+status.Account.DisplayName)
data := &renderer.ThreadData{
Statuses: statuses,
PostContext: postContext,
return
}
- commonData, err := svc.getCommonData(ctx, c, "likes")
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, c, "likes")
data := &renderer.LikedByData{
CommonData: commonData,
Users: likers,
return
}
- commonData, err := svc.getCommonData(ctx, c, "retweets")
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, c, "retweets")
data := &renderer.RetweetedByData{
CommonData: commonData,
Users: retweeters,
var nextLink string
var unreadCount int
+ var readID string
var pg = mastodon.Pagination{
MaxID: maxID,
MinID: minID,
}
if unreadCount > 0 {
- err := c.ReadNotifications(ctx, notifications[0].ID)
- if err != nil {
- return err
- }
+ readID = notifications[0].ID
}
- if len(pg.MaxID) > 0 {
+ if len(notifications) == 20 && len(pg.MaxID) > 0 {
nextLink = "/notifications?max_id=" + pg.MaxID
}
- commonData, err := svc.getCommonData(ctx, c, "notifications")
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, c, "notifications")
+ commonData.AutoRefresh = c.Session.Settings.AutoRefreshNotifications
data := &renderer.NotificationData{
Notifications: notifications,
+ UnreadCount: unreadCount,
+ ReadID: readID,
NextLink: nextLink,
CommonData: commonData,
}
return errInvalidArgument
}
- commonData, err := svc.getCommonData(ctx, c, user.DisplayName)
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, c, user.DisplayName)
data := &renderer.UserData{
User: user,
- IsCurrent: commonData.IsCurrentUser(user.ID),
+ IsCurrent: c.Session.UserID == user.ID,
Type: pageType,
Users: users,
Statuses: statuses,
title += " \"" + q + "\""
}
- commonData, err := svc.getCommonData(ctx, c, title)
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, c, title)
data := &renderer.UserSearchData{
CommonData: commonData,
User: user,
}
func (svc *service) ServeAboutPage(ctx context.Context, c *model.Client) (err error) {
- commonData, err := svc.getCommonData(ctx, c, "about")
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, c, "about")
data := &renderer.AboutData{
CommonData: commonData,
}
}
func (svc *service) ServeEmojiPage(ctx context.Context, c *model.Client) (err error) {
- commonData, err := svc.getCommonData(ctx, c, "emojis")
- if err != nil {
- return
- }
-
emojis, err := c.GetInstanceEmojis(ctx)
if err != nil {
return
}
+ commonData := svc.getCommonData(ctx, c, "emojis")
data := &renderer.EmojiData{
Emojis: emojis,
CommonData: commonData,
title += " \"" + q + "\""
}
- commonData, err := svc.getCommonData(ctx, c, title)
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, c, title)
data := &renderer.SearchData{
CommonData: commonData,
Q: q,
}
func (svc *service) ServeSettingsPage(ctx context.Context, c *model.Client) (err error) {
- commonData, err := svc.getCommonData(ctx, c, "settings")
- if err != nil {
- return
- }
-
+ commonData := svc.getCommonData(ctx, c, "settings")
data := &renderer.SettingsData{
CommonData: commonData,
Settings: &c.Session.Settings,
id string) (err error) {
return c.DeleteStatus(ctx, id)
}
+
+func (svc *service) ReadNotifications(ctx context.Context, c *model.Client,
+ maxID string) (err error) {
+ return c.ReadNotifications(ctx, maxID)
+}
rootPage := func(w http.ResponseWriter, req *http.Request) {
sessionID, _ := req.Cookie("session_id")
-
- location := "/signin"
if sessionID != nil && len(sessionID.Value) > 0 {
- location = "/timeline/home"
+ c := newClient(w)
+ ctx := newCtxWithSesion(req)
+ err := s.ServeRootPage(ctx, c)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ s.ServeErrorPage(ctx, c, err)
+ return
+ }
+ } else {
+ w.Header().Add("Location", "/signin")
+ w.WriteHeader(http.StatusFound)
}
+ }
- w.Header().Add("Location", location)
- w.WriteHeader(http.StatusFound)
+ navPage := func(w http.ResponseWriter, req *http.Request) {
+ c := newClient(w)
+ ctx := newCtxWithSesion(req)
+ err := s.ServeNavPage(ctx, c)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ s.ServeErrorPage(ctx, c, err)
+ return
+ }
}
signinPage := func(w http.ResponseWriter, req *http.Request) {
return
}
- w.Header().Add("Location", "/timeline/home")
+ w.Header().Add("Location", "/")
w.WriteHeader(http.StatusFound)
}
return
}
- location := "/timeline/home" + "#status-" + id
+ location := req.Header.Get("Referer")
if len(replyToID) > 0 {
location = "/thread/" + replyToID + "#status-" + id
}
copyScope := req.FormValue("copy_scope") == "true"
threadInNewTab := req.FormValue("thread_in_new_tab") == "true"
maskNSFW := req.FormValue("mask_nsfw") == "true"
+ arn := req.FormValue("auto_refresh_notifications") == "true"
fluorideMode := req.FormValue("fluoride_mode") == "true"
darkMode := req.FormValue("dark_mode") == "true"
settings := &model.Settings{
- DefaultVisibility: visibility,
- CopyScope: copyScope,
- ThreadInNewTab: threadInNewTab,
- MaskNSFW: maskNSFW,
- FluorideMode: fluorideMode,
- DarkMode: darkMode,
+ DefaultVisibility: visibility,
+ CopyScope: copyScope,
+ ThreadInNewTab: threadInNewTab,
+ MaskNSFW: maskNSFW,
+ AutoRefreshNotifications: arn,
+ FluorideMode: fluorideMode,
+ DarkMode: darkMode,
}
err := s.SaveSettings(ctx, c, settings)
return
}
- w.Header().Add("Location", req.Header.Get("Referer"))
+ w.Header().Add("Location", "/")
w.WriteHeader(http.StatusFound)
}
w.WriteHeader(http.StatusFound)
}
+ readNotifications := func(w http.ResponseWriter, req *http.Request) {
+ c := newClient(w)
+ ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
+ maxID := req.URL.Query().Get("max_id")
+
+ err := s.ReadNotifications(ctx, c, maxID)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ s.ServeErrorPage(ctx, c, err)
+ return
+ }
+
+ w.Header().Add("Location", req.Header.Get("Referer"))
+ w.WriteHeader(http.StatusFound)
+ }
+
signout := func(w http.ResponseWriter, req *http.Request) {
// TODO remove session from database
http.SetCookie(w, &http.Cookie{
}
r.HandleFunc("/", rootPage).Methods(http.MethodGet)
+ r.HandleFunc("/nav", navPage).Methods(http.MethodGet)
r.HandleFunc("/signin", signinPage).Methods(http.MethodGet)
+ r.HandleFunc("//{type}", timelinePage).Methods(http.MethodGet)
r.HandleFunc("/timeline/{type}", timelinePage).Methods(http.MethodGet)
r.HandleFunc("/timeline", timelineOldPage).Methods(http.MethodGet)
r.HandleFunc("/thread/{id}", threadPage).Methods(http.MethodGet)
r.HandleFunc("/muteconv/{id}", muteConversation).Methods(http.MethodPost)
r.HandleFunc("/unmuteconv/{id}", unMuteConversation).Methods(http.MethodPost)
r.HandleFunc("/delete/{id}", delete).Methods(http.MethodPost)
+ r.HandleFunc("/notifications/read", readNotifications).Methods(http.MethodPost)
r.HandleFunc("/signout", signout).Methods(http.MethodGet)
r.HandleFunc("/fluoride/like/{id}", fLike).Methods(http.MethodPost)
r.HandleFunc("/fluoride/unlike/{id}", fUnlike).Methods(http.MethodPost)
margin-top: 6px;
}
+.notification-title-container>* {
+ display: inline;
+}
+
+.notification-title {
+ font-size: 18pt;
+ margin-right: 8px;
+}
+
+.notification-refresh {
+ margin-right: 8px;
+}
+
.dark {
background-color: #222222;
background-image: none;
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> About </div>
<div>
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> Emojis </div>
<div class="emoji-list-container">
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> Error </div>
<div class="error-text"> {{.Error}} </div>
<div>
<a href="/timeline/home">Home</a>
- <a href="/signin">Sign In</a>
+ <a href="/signin" target="_top">Sign In</a>
</div>
{{template "footer.tmpl"}}
{{if .CSRFToken}}
<meta name="csrf_token" content="{{.CSRFToken}}">
{{end}}
- <title>{{if gt .NotificationCount 0}}({{.NotificationCount}}) {{end}}{{.Title}}</title>
+ {{if .AutoRefresh}}
+ <meta http-equiv="refresh" content="30">
+ {{end}}
+ <title>{{.Title}}</title>
<link rel="stylesheet" href="/static/style.css">
{{if .CustomCSS}}
<link rel="stylesheet" href="{{.CustomCSS}}">
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> Liked By </div>
{{template "userlist.tmpl" (WithContext .Users $.Ctx)}}
--- /dev/null
+{{with .Data}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
+<div class="user-info">
+ <div class="user-info-img-container">
+ <a class="img-link" href="/timeline/home" title="home" target="main">
+ <img class="user-info-img" src="{{.User.AvatarStatic}}" alt="profile-avatar" />
+ </a>
+ </div>
+ <div class="user-info-details-container">
+ <div>
+ <span class="status-dname"> {{EmojiFilter .User.DisplayName .User.Emojis}} </span>
+ <a class="nav-link" href="/user/{{.User.ID}}" target="main">
+ <span class="status-uname"> {{.User.Acct}} </span>
+ </a>
+ </div>
+ <div>
+ <a class="nav-link" href="/timeline/home" target="main">home</a>
+ <a class="nav-link" href="/timeline/direct" target="main">direct</a>
+ <a class="nav-link" href="/timeline/local" target="main">local</a>
+ <a class="nav-link" href="/timeline/twkn" target="main">twkn</a>
+ <a class="nav-link" href="/search" target="main">search</a>
+ <a class="nav-link" href="/about" target="main">about</a>
+ </div>
+ <div>
+ <a class="nav-link" href="/settings" target="_top">settings</a>
+ <a class="nav-link" href="/signout" target="_top">sign out</a>
+ </div>
+ </div>
+</div>
+
+{{template "postform.tmpl" (WithContext .PostContext $.Ctx)}}
+
+{{template "footer.tmpl"}}
+{{end}}
+++ /dev/null
-{{with .Data}}
-<div class="user-info">
- <div class="user-info-img-container">
- <a class="img-link" href="/timeline/home" title="home">
- <img class="user-info-img" src="{{.User.AvatarStatic}}" alt="profile-avatar" />
- </a>
- </div>
- <div class="user-info-details-container">
- <div>
- <span class="status-dname"> {{EmojiFilter .User.DisplayName .User.Emojis}} </span>
- <a class="nav-link" href="/user/{{.User.ID}}">
- <span class="status-uname"> {{.User.Acct}} </span>
- </a>
- </div>
- <div>
- <a class="nav-link" href="/timeline/home">home</a>
- <a class="nav-link" href="/notifications">notifications{{if gt .NotificationCount 0}}({{.NotificationCount}}){{end}}</a>
- <a class="nav-link" href="/timeline/direct">direct</a>
- <a class="nav-link" href="/timeline/local">local</a>
- <a class="nav-link" href="/timeline/twkn">twkn</a>
- <a class="nav-link" href="/search">search</a>
- <a class="nav-link" href="/about">about</a>
- </div>
- <div>
- <a class="nav-link" href="/settings">settings</a>
- <a class="nav-link" href="/signout">sign out</a>
- </div>
- </div>
-</div>
-{{end}}
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
-<div class="page-title"> Notifications </div>
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
+<div class="notification-title-container">
+ <div class="notification-title">
+ Notifications
+ {{if gt .UnreadCount 0}}({{.UnreadCount }}){{end}}
+ </div>
+ <a class="notification-refresh" href="/notifications">refresh</a>
+ {{if .ReadID}}
+ <form action="/notifications/read?max_id={{.ReadID}}" method="post">
+ <input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
+ <input type="submit" value="read" class="btn-link">
+ </form>
+ {{end}}
+</div>
{{range .Notifications}}
<div class="notification-container {{if .Pleroma}}{{if not .Pleroma.IsSeen}}unread{{end}}{{end}}">
{{if eq .Type "follow"}}
<div class="notification-follow-container">
<div class="status-profile-img-container">
- <a class="img-link" href="/user/{{.Account.ID}}" >
+ <a class="img-link" href="/user/{{.Account.ID}}" target="main" >
<img class="status-profile-img" src="{{.Account.AvatarStatic}}" alt="profile-avatar" />
</a>
</div>
{{else if eq .Type "reblog"}}
<div class="retweet-info">
- <a class="img-link" href="/user/{{.Account.ID}}">
+ <a class="img-link" href="/user/{{.Account.ID}}" target="main">
<img class="status-profile-img" src="{{.Account.AvatarStatic}}" alt="avatar" />
</a>
<span class="status-dname"> {{EmojiFilter .Account.DisplayName .Account.Emojis}} </span>
{{else if eq .Type "favourite"}}
<div class="retweet-info">
- <a class="img-link" href="/user/{{.Account.ID}}">
+ <a class="img-link" href="/user/{{.Account.ID}}" target="main">
<img class="status-profile-img" src="{{.Account.AvatarStatic}}" alt="avatar" />
</a>
<span class="status-dname"> {{EmojiFilter .Account.DisplayName .Account.Emojis}} </span>
emoji list
</a>
<div class="post-form-content-container">
- <textarea id="post-content" name="content" class="post-content" cols="50" rows="5">{{if .ReplyContext}}{{.ReplyContext.ReplyContent}}{{end}}</textarea>
+ <textarea id="post-content" name="content" class="post-content" cols="34" rows="5">{{if .ReplyContext}}{{.ReplyContext.ReplyContent}}{{end}}</textarea>
</div>
<div>
{{if gt (len .Formats) 0}}
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> Retweeted By </div>
{{template "userlist.tmpl" (WithContext .Users $.Ctx)}}
--- /dev/null
+{{with .Data}}
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset='utf-8'>
+ <meta content='width=device-width, initial-scale=1' name='viewport'>
+ <title>{{.Title}}</title>
+</head>
+<frameset cols="30%,*">
+ <frameset rows="316px,*">
+ <frame name="nav" class="nav-frame" src="/nav" />
+ <frame name="notification" class="notification-frame" src="/notifications" />
+ </frameset>
+ <frame name="main" class="main-frame" src="/timeline/home" />
+</frameset>
+</html>
+{{end}}
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> Search </div>
<form class="search-form" action="/search" method="GET">
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> Settings </div>
<form id="settings-form" action="/settings" method="POST">
</div>
<div class="settings-form-field">
<input id="mask-nsfw" name="mask_nsfw" type="checkbox" value="true" {{if .Settings.MaskNSFW}}checked{{end}}>
- <label for="mask-nsfw"> Mask NSFW Attachments </label>
+ <label for="mask-nsfw"> Mask NSFW attachments </label>
+ </div>
+ <div class="settings-form-field">
+ <input id="auto-refresh-notifications" name="auto_refresh_notifications" type="checkbox" value="true" {{if .Settings.AutoRefreshNotifications}}checked{{end}}>
+ <label for="auto-refresh-notifications"> Auto refresh notifications </label>
</div>
<div class="settings-form-field">
<input id="fluoride-mode" name="fluoride_mode" type="checkbox" value="true" {{if .Settings.FluorideMode}}checked{{end}}>
- <label for="fluoride-mode"> Enable Fluoride Mode </label>
+ <label for="fluoride-mode"> Enable fluoride mode </label>
</div>
<div class="settings-form-field">
<input id="dark-mode" name="dark_mode" type="checkbox" value="true" {{if .Settings.DarkMode}}checked{{end}}>
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> Signin </div>
<form class="signin-form" action="/signin" method="post">
<div id="status-{{.ID}}" class="status-container-container">
{{if .Reblog}}
<div class="retweet-info">
- <a class="img-link" href="/user/{{.Account.ID}}">
+ <a class="img-link" href="/user/{{.Account.ID}}" target="main">
<img class="status-profile-img" src="{{.Account.AvatarStatic}}" alt="avatar" />
</a>
<span class="status-dname"> {{EmojiFilter .Account.DisplayName .Account.Emojis}} </span>
{{with $s := .Data}}
<div class="status-container status-{{.ID}}" data-id="{{.ID}}">
<div class="status-profile-img-container">
- <a class="img-link" href="/user/{{.Account.ID}}">
+ <a class="img-link" href="/user/{{.Account.ID}}" target="main">
<img class="status-profile-img" src="{{.Account.AvatarStatic}}" alt="avatar" />
</a>
</div>
<div class="status">
<div class="status-name">
<span class="status-dname"> {{EmojiFilter .Account.DisplayName .Account.Emojis}} </span>
- <a href="/user/{{.Account.ID}}" >
+ <a href="/user/{{.Account.ID}}" target="main">
<span class="status-uname"> {{.Account.Acct}} </span>
</a>
- <div class="more-container" title="more">
+ <div class="more-container">
<div class="remote-link">
{{.Visibility}}
</div>
<div class="more-content">
- <a class="more-link" href="{{.URL}}" target="_blank" title="source">
+ <a class="more-link" href="{{.URL}}" target="_blank">
source
</a>
{{if .Muted}}
<form action="/unmuteconv/{{.ID}}" method="post">
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
- <input type="submit" value="unmute" class="btn-link more-link" title="unmute">
+ <input type="submit" value="unmute" class="btn-link more-link">
</form>
{{else}}
<form action="/muteconv/{{.ID}}" method="post">
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
- <input type="submit" value="mute" class="btn-link more-link" title="mute">
+ <input type="submit" value="mute" class="btn-link more-link">
</form>
{{end}}
{{if eq $.Ctx.UserID .Account.ID}}
<form action="/delete/{{.ID}}" method="post">
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
- <input type="submit" value="delete" class="btn-link more-link" title="delete">
+ <input type="submit" value="delete" class="btn-link more-link">
</form>
{{end}}
</div>
<div class="status-reply-container">
{{if .InReplyToID}}
<div class="status-reply-to">
- <a class="status-reply-to-link" href="{{if not .ShowReplies}}/thread/{{.InReplyToID}}{{end}}#status-{{.InReplyToID}}">
+ <a class="status-reply-to-link" href="{{if not .ShowReplies}}/thread/{{.InReplyToID}}{{end}}#status-{{.InReplyToID}}" target="main">
reply to {{.Pleroma.InReplyToAccountAcct}}
</a>
</div>
{{end}}
<div class="status-action-container">
<div class="status-action">
- <a href="/thread/{{.ID}}?reply=true#status-{{.ID}}" title="reply">
+ <a href="/thread/{{.ID}}?reply=true#status-{{.ID}}" target="main">
reply
</a>
- <a class="status-reply-count" href="/thread/{{.ID}}#status-{{.ID}}" {{if $.Ctx.ThreadInNewTab}}target="_blank"{{end}}>
+ <a class="status-reply-count" href="/thread/{{.ID}}#status-{{.ID}}" target="{{if $.Ctx.ThreadInNewTab}}_blank{{else}}main{{end}}">
{{if .RepliesCount}} ({{DisplayInteractionCount .RepliesCount}}) {{end}}
</a>
</div>
<div class="status-action">
{{if or (eq .Visibility "private") (eq .Visibility "direct")}}
- <a class="status-retweet" title="this status cannot be retweeted">
+ <a class="status-retweet" href="" title="this status cannot be retweeted" target="main">
retweet
</a>
{{else}}
<form class="status-retweet" data-action="unretweet" action="/unretweet/{{.ID}}" method="post">
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
<input type="hidden" name="retweeted_by_id" value="{{.RetweetedByID}}">
- <input type="submit" value="unretweet" class="btn-link" title="unretweet">
+ <input type="submit" value="unretweet" class="btn-link">
</form>
{{else}}
<form class="status-retweet" data-action="retweet" action="/retweet/{{.ID}}" method="post">
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
<input type="hidden" name="retweeted_by_id" value="{{.RetweetedByID}}">
- <input type="submit" value="retweet" class="btn-link" title="retweet">
+ <input type="submit" value="retweet" class="btn-link">
</form>
{{end}}
{{end}}
- <a class="status-retweet-count" href="/retweetedby/{{.ID}}" title="click to see the the list">
+ <a class="status-retweet-count" href="/retweetedby/{{.ID}}" title="click to see the the list" target="main">
{{if .ReblogsCount}} ({{DisplayInteractionCount .ReblogsCount}}) {{end}}
</a>
</div>
<form class="status-like" data-action="unlike" action="/unlike/{{.ID}}" method="post">
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
<input type="hidden" name="retweeted_by_id" value="{{.RetweetedByID}}">
- <input type="submit" value="unlike" class="btn-link" title="unlike">
+ <input type="submit" value="unlike" class="btn-link">
</form>
{{else}}
<form class="status-like" data-action="like" action="/like/{{.ID}}" method="post">
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
<input type="hidden" name="retweeted_by_id" value="{{.RetweetedByID}}">
- <input type="submit" value="like" class="btn-link" title="like">
+ <input type="submit" value="like" class="btn-link">
</form>
{{end}}
- <a class="status-like-count" href="/likedby/{{.ID}}" title="click to see the the list">
+ <a class="status-like-count" href="/likedby/{{.ID}}" title="click to see the the list" target="main">
{{if .FavouritesCount}} ({{DisplayInteractionCount .FavouritesCount}}) {{end}}
</a>
</div>
<div class="status-action">
<a class="status-time" href="{{if not .ShowReplies}}/thread/{{.ID}}{{end}}#status-{{.ID}}"
- {{if $.Ctx.ThreadInNewTab}}target="_blank"{{end}}>
+ target="{{if $.Ctx.ThreadInNewTab}}_blank{{else}}main{{end}}">
<time datetime="{{FormatTimeRFC3339 .CreatedAt}}" title="{{FormatTimeRFC822 .CreatedAt}}">
{{TimeSince .CreatedAt}}
</time>
{{with $s := .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> Thread </div>
{{range .Statuses}}
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> {{.Title}} </div>
-{{template "postform.tmpl" (WithContext .PostContext $.Ctx)}}
-
{{range .Statuses}}
{{template "status.tmpl" (WithContext . $.Ctx)}}
{{end}}
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> User </div>
<div class="user-info-container">
{{with .Data}}
-{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
-{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
<div class="page-title"> Search {{EmojiFilter .User.DisplayName .User.Emojis}}'s statuses </div>
<form class="search-form" action="/usersearch/{{.User.ID}}" method="GET">