diff --git a/mastodon/filter.go b/mastodon/filter.go new file mode 100644 index 0000000..55beac1 --- /dev/null +++ b/mastodon/filter.go @@ -0,0 +1,50 @@ +package mastodon + +import ( + "context" + "fmt" + "net/http" + "net/url" + "strconv" + "time" +) + +type Filter struct { + ID string `json:"id"` + Phrase string `json:"phrase"` + Context []string `json:"context"` + WholeWord bool `json:"whole_word"` + ExpiresAt *time.Time `json:"expires_at"` + Irreversible bool `json:"irreversible"` +} + +func (c *Client) GetFilters(ctx context.Context) ([]*Filter, error) { + var filters []*Filter + err := c.doAPI(ctx, http.MethodGet, "/api/v1/filters", nil, &filters, nil) + if err != nil { + return nil, err + } + return filters, nil +} + +func (c *Client) AddFilter(ctx context.Context, phrase string, context []string, irreversible bool, wholeWord bool, expiresIn *time.Time) error { + params := url.Values{} + params.Set("phrase", phrase) + for i := range context { + params.Add("context[]", context[i]) + } + params.Set("irreversible", strconv.FormatBool(irreversible)) + params.Set("whole_word", strconv.FormatBool(wholeWord)) + if expiresIn != nil { + params.Set("expires_in", expiresIn.Format(time.RFC3339)) + } + err := c.doAPI(ctx, http.MethodPost, "/api/v1/filters", params, nil, nil) + if err != nil { + return err + } + return nil +} + +func (c *Client) RemoveFilter(ctx context.Context, id string) error { + return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/filters/%s", id), nil, nil, nil) +} diff --git a/renderer/model.go b/renderer/model.go index 6c3ba90..aed444a 100644 --- a/renderer/model.go +++ b/renderer/model.go @@ -127,3 +127,8 @@ type SettingsData struct { Settings *model.Settings PostFormats []model.PostFormat } + +type FiltersData struct { + *CommonData + Filters []*mastodon.Filter +} diff --git a/renderer/renderer.go b/renderer/renderer.go index 3d4685f..067632f 100644 --- a/renderer/renderer.go +++ b/renderer/renderer.go @@ -29,6 +29,7 @@ const ( RetweetedByPage = "retweetedby.tmpl" SearchPage = "search.tmpl" SettingsPage = "settings.tmpl" + FiltersPage = "filters.tmpl" ) type TemplateData struct { diff --git a/service/service.go b/service/service.go index ce689fd..8149f33 100644 --- a/service/service.go +++ b/service/service.go @@ -641,6 +641,22 @@ func (s *service) SettingsPage(c *client) (err error) { return s.renderer.Render(rCtx, c, renderer.SettingsPage, data) } +func (svc *service) FiltersPage(c *client) (err error) { + filters, err := c.GetFilters(ctx) + if err != nil { + return + } + + commonData := svc.getCommonData(c, "filters") + data := &renderer.FiltersData{ + CommonData: commonData, + Filters: filters, + } + + rCtx := getRendererContext(c) + return svc.renderer.Render(rCtx, c, renderer.FiltersPage, data) +} + func (s *service) SingleInstance() (instance string, ok bool) { if len(s.singleInstance) > 0 { instance = s.singleInstance @@ -908,3 +924,12 @@ func (s *service) UnBookmark(c *client, id string) (err error) { _, err = c.Unbookmark(ctx, id) return } + +func (svc *service) Filter(c *client, phrase string, wholeWord bool) (err error) { + fctx := []string{"home", "notifications", "public", "thread"} + return c.AddFilter(ctx, phrase, fctx, true, wholeWord, nil) +} + +func (svc *service) UnFilter(c *client, id string) (err error) { + return c.RemoveFilter(ctx, id) +} diff --git a/service/transport.go b/service/transport.go index 096b44e..1180f6c 100644 --- a/service/transport.go +++ b/service/transport.go @@ -262,6 +262,10 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler { return s.SettingsPage(c) }, SESSION, HTML) + filtersPage := handle(func(c *client) error { + return s.FiltersPage(c) + }, SESSION, HTML) + signin := handle(func(c *client) error { instance := c.Req.FormValue("instance") url, sid, err := s.NewSession(instance) @@ -589,6 +593,27 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler { return nil }, CSRF, HTML) + filter := handle(func(c *client) error { + phrase := c.Req.FormValue("phrase") + wholeWord := c.Req.FormValue("whole_word") == "true" + err := s.Filter(c, phrase, wholeWord) + if err != nil { + return err + } + redirect(c, c.Req.FormValue("referrer")) + return nil + }, CSRF, HTML) + + unFilter := handle(func(c *client) error { + id, _ := mux.Vars(c.Req)["id"] + err := s.UnFilter(c, id) + if err != nil { + return err + } + redirect(c, c.Req.FormValue("referrer")) + return nil + }, CSRF, HTML) + signout := handle(func(c *client) error { s.Signout(c) setSessionCookie(c, "", 0) @@ -648,6 +673,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler { r.HandleFunc("/emojis", emojisPage).Methods(http.MethodGet) r.HandleFunc("/search", searchPage).Methods(http.MethodGet) r.HandleFunc("/settings", settingsPage).Methods(http.MethodGet) + r.HandleFunc("/filters", filtersPage).Methods(http.MethodGet) r.HandleFunc("/signin", signin).Methods(http.MethodPost) r.HandleFunc("/oauth_callback", oauthCallback).Methods(http.MethodGet) r.HandleFunc("/post", post).Methods(http.MethodPost) @@ -673,6 +699,8 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler { r.HandleFunc("/notifications/read", readNotifications).Methods(http.MethodPost) r.HandleFunc("/bookmark/{id}", bookmark).Methods(http.MethodPost) r.HandleFunc("/unbookmark/{id}", unBookmark).Methods(http.MethodPost) + r.HandleFunc("/filter", filter).Methods(http.MethodPost) + r.HandleFunc("/unfilter/{id}", unFilter).Methods(http.MethodPost) r.HandleFunc("/signout", signout).Methods(http.MethodPost) r.HandleFunc("/fluoride/like/{id}", fLike).Methods(http.MethodPost) r.HandleFunc("/fluoride/unlike/{id}", fUnlike).Methods(http.MethodPost) diff --git a/static/style.css b/static/style.css index 1921f5e..b51b439 100644 --- a/static/style.css +++ b/static/style.css @@ -552,6 +552,14 @@ kbd { font-size: 10pt; } +.filters { + margin: 10px 0; +} + +.filters td { + padding: 2px 4px; +} + .dark { background-color: #222222; background-image: none; diff --git a/templates/filters.tmpl b/templates/filters.tmpl new file mode 100644 index 0000000..ef7c024 --- /dev/null +++ b/templates/filters.tmpl @@ -0,0 +1,40 @@ +{{with .Data}} +{{template "header.tmpl" (WithContext .CommonData $.Ctx)}} +
Filters
+ +{{if .Filters}} + + {{range .Filters}} + + + + + {{end}} +
{{.Phrase}}{{if not .WholeWord}}*{{end}} +
+ + + +
+
+{{else}} +
No filters added
+{{end}} + +
Add filter
+
+ + + + + + + + + + + +
+ +{{template "footer.tmpl"}} +{{end}} diff --git a/templates/user.tmpl b/templates/user.tmpl index af6a8d1..7aaefa7 100644 --- a/templates/user.tmpl +++ b/templates/user.tmpl @@ -119,6 +119,7 @@ {{end}}
search statuses + {{if .IsCurrent}} - filters {{end}}