From 14bb18fbc7e6cdea57f27a44093d645d97bacb64 Mon Sep 17 00:00:00 2001 From: r Date: Thu, 26 Dec 2019 19:18:09 +0000 Subject: [PATCH] Add search page --- mastodon/mastodon.go | 2 +- mastodon/status.go | 7 ++++-- renderer/model.go | 32 +++++++++++++++++---------- renderer/renderer.go | 5 +++++ service/auth.go | 8 +++++++ service/logging.go | 8 +++++++ service/service.go | 46 +++++++++++++++++++++++++++++++++++++++ service/transport.go | 25 +++++++++++++++++++++ templates/navigation.tmpl | 1 + templates/search.tmpl | 37 +++++++++++++++++++++++++++++++ 10 files changed, 157 insertions(+), 14 deletions(-) create mode 100644 templates/search.tmpl diff --git a/mastodon/mastodon.go b/mastodon/mastodon.go index 8a8af6a..74fa0ff 100644 --- a/mastodon/mastodon.go +++ b/mastodon/mastodon.go @@ -336,7 +336,7 @@ type Emoji struct { type Results struct { Accounts []*Account `json:"accounts"` Statuses []*Status `json:"statuses"` - Hashtags []string `json:"hashtags"` + // Hashtags []string `json:"hashtags"` } // Pagination is a struct for specifying the get range. diff --git a/mastodon/status.go b/mastodon/status.go index edd88e9..816d092 100644 --- a/mastodon/status.go +++ b/mastodon/status.go @@ -284,12 +284,15 @@ func (c *Client) DeleteStatus(ctx context.Context, id string) error { } // Search search content with query. -func (c *Client) Search(ctx context.Context, q string, resolve bool) (*Results, error) { +func (c *Client) Search(ctx context.Context, q string, qType string, limit int, resolve bool, offset int) (*Results, error) { params := url.Values{} params.Set("q", q) + params.Set("type", qType) + params.Set("limit", fmt.Sprint(limit)) params.Set("resolve", fmt.Sprint(resolve)) + params.Set("offset", fmt.Sprint(offset)) var results Results - err := c.doAPI(ctx, http.MethodGet, "/api/v1/search", params, &results, nil) + err := c.doAPI(ctx, http.MethodGet, "/api/v2/search", params, &results, nil) if err != nil { return nil, err } diff --git a/renderer/model.go b/renderer/model.go index 20d11d8..ffeb2d1 100644 --- a/renderer/model.go +++ b/renderer/model.go @@ -61,10 +61,10 @@ type NotificationData struct { type UserData struct { *CommonData - User *mastodon.Account - Statuses []*mastodon.Status - HasNext bool - NextLink string + User *mastodon.Account + Statuses []*mastodon.Status + HasNext bool + NextLink string } type AboutData struct { @@ -73,19 +73,29 @@ type AboutData struct { type EmojiData struct { *CommonData - Emojis []*mastodon.Emoji + Emojis []*mastodon.Emoji } type LikedByData struct { *CommonData - Users []*mastodon.Account - HasNext bool - NextLink string + Users []*mastodon.Account + HasNext bool + NextLink string } type RetweetedByData struct { *CommonData - Users []*mastodon.Account - HasNext bool - NextLink string + Users []*mastodon.Account + HasNext bool + NextLink string +} + +type SearchData struct { + *CommonData + Q string + Type string + Users []*mastodon.Account + Statuses []*mastodon.Status + HasNext bool + NextLink string } diff --git a/renderer/renderer.go b/renderer/renderer.go index 061737c..5e5f005 100644 --- a/renderer/renderer.go +++ b/renderer/renderer.go @@ -23,6 +23,7 @@ type Renderer interface { RenderEmojiPage(ctx context.Context, writer io.Writer, data *EmojiData) (err error) RenderLikedByPage(ctx context.Context, writer io.Writer, data *LikedByData) (err error) RenderRetweetedByPage(ctx context.Context, writer io.Writer, data *RetweetedByData) (err error) + RenderSearchPage(ctx context.Context, writer io.Writer, data *SearchData) (err error) } type renderer struct { @@ -91,6 +92,10 @@ func (r *renderer) RenderRetweetedByPage(ctx context.Context, writer io.Writer, return r.template.ExecuteTemplate(writer, "retweetedby.tmpl", data) } +func (r *renderer) RenderSearchPage(ctx context.Context, writer io.Writer, data *SearchData) (err error) { + return r.template.ExecuteTemplate(writer, "search.tmpl", data) +} + func EmojiFilter(content string, emojis []mastodon.Emoji) string { var replacements []string for _, e := range emojis { diff --git a/service/auth.go b/service/auth.go index 0c2e7f6..431e093 100644 --- a/service/auth.go +++ b/service/auth.go @@ -149,6 +149,14 @@ func (s *authService) ServeRetweetedByPage(ctx context.Context, client io.Writer return s.Service.ServeRetweetedByPage(ctx, client, c, id) } +func (s *authService) ServeSearchPage(ctx context.Context, client io.Writer, c *model.Client, q string, qType string, offset int) (err error) { + c, err = s.getClient(ctx) + if err != nil { + return + } + return s.Service.ServeSearchPage(ctx, client, c, q, qType, offset) +} + func (s *authService) Like(ctx context.Context, client io.Writer, c *model.Client, id string) (err error) { c, err = s.getClient(ctx) if err != nil { diff --git a/service/logging.go b/service/logging.go index f5e6d66..a5ffd57 100644 --- a/service/logging.go +++ b/service/logging.go @@ -125,6 +125,14 @@ func (s *loggingService) ServeRetweetedByPage(ctx context.Context, client io.Wri return s.Service.ServeRetweetedByPage(ctx, client, c, id) } +func (s *loggingService) ServeSearchPage(ctx context.Context, client io.Writer, c *model.Client, q string, qType string, offset int) (err error) { + defer func(begin time.Time) { + s.logger.Printf("method=%v, q=%v, type=%v, offset=%v, took=%v, err=%v\n", + "ServeSearchPage", q, qType, offset, time.Since(begin), err) + }(time.Now()) + return s.Service.ServeSearchPage(ctx, client, c, q, qType, offset) +} + func (s *loggingService) Like(ctx context.Context, client io.Writer, c *model.Client, id string) (err error) { defer func(begin time.Time) { s.logger.Printf("method=%v, id=%v, took=%v, err=%v\n", diff --git a/service/service.go b/service/service.go index 2787079..157ddf8 100644 --- a/service/service.go +++ b/service/service.go @@ -39,6 +39,7 @@ type Service interface { ServeEmojiPage(ctx context.Context, client io.Writer, c *model.Client) (err error) ServeLikedByPage(ctx context.Context, client io.Writer, c *model.Client, id string) (err error) ServeRetweetedByPage(ctx context.Context, client io.Writer, c *model.Client, id string) (err error) + ServeSearchPage(ctx context.Context, client io.Writer, c *model.Client, q string, qType string, offset int) (err error) Like(ctx context.Context, client io.Writer, c *model.Client, id string) (err error) UnLike(ctx context.Context, client io.Writer, c *model.Client, id string) (err error) Retweet(ctx context.Context, client io.Writer, c *model.Client, id string) (err error) @@ -593,6 +594,51 @@ func (svc *service) ServeRetweetedByPage(ctx context.Context, client io.Writer, return } + +func (svc *service) ServeSearchPage(ctx context.Context, client io.Writer, c *model.Client, q string, qType string, offset int) (err error) { + var hasNext bool + var nextLink string + + results, err := c.Search(ctx, q, qType, 20, true, offset) + if err != nil { + return + } + + switch qType { + case "accounts": + hasNext = len(results.Accounts) == 20 + case "statuses": + hasNext = len(results.Statuses) == 20 + } + + if hasNext { + offset += 20 + nextLink = fmt.Sprintf("/search?q=%s&type=%s&offset=%d", q, qType, offset) + } + + commonData, err := svc.getCommonData(ctx, client, c) + if err != nil { + return + } + + data := &renderer.SearchData{ + CommonData: commonData, + Q: q, + Type: qType, + Users: results.Accounts, + Statuses: results.Statuses, + HasNext: hasNext, + NextLink: nextLink, + } + + err = svc.renderer.RenderSearchPage(ctx, client, data) + if err != nil { + return + } + + return +} + func (svc *service) getCommonData(ctx context.Context, client io.Writer, c *model.Client) (data *renderer.CommonData, err error) { data = new(renderer.CommonData) diff --git a/service/transport.go b/service/transport.go index 6541772..c42462f 100644 --- a/service/transport.go +++ b/service/transport.go @@ -6,6 +6,7 @@ import ( "mime/multipart" "net/http" "path" + "strconv" "github.com/gorilla/mux" ) @@ -280,6 +281,30 @@ func NewHandler(s Service, staticDir string) http.Handler { } }).Methods(http.MethodGet) + r.HandleFunc("/search", func(w http.ResponseWriter, req *http.Request) { + ctx := getContextWithSession(context.Background(), req) + + q := req.URL.Query().Get("q") + qType := req.URL.Query().Get("type") + offsetStr := req.URL.Query().Get("offset") + + var offset int + var err error + if len(offsetStr) > 1 { + offset, err = strconv.Atoi(offsetStr) + if err != nil { + s.ServeErrorPage(ctx, w, err) + return + } + } + + err = s.ServeSearchPage(ctx, w, nil, q, qType, offset) + if err != nil { + s.ServeErrorPage(ctx, w, err) + return + } + }).Methods(http.MethodGet) + r.HandleFunc("/signout", func(w http.ResponseWriter, req *http.Request) { // TODO remove session from database w.Header().Add("Set-Cookie", fmt.Sprintf("session_id=;max-age=0")) diff --git a/templates/navigation.tmpl b/templates/navigation.tmpl index bd97d2d..b82c7f5 100644 --- a/templates/navigation.tmpl +++ b/templates/navigation.tmpl @@ -16,6 +16,7 @@ notifications{{if gt .NotificationCount 0}}({{.NotificationCount}}){{end}} local twkn + search about
diff --git a/templates/search.tmpl b/templates/search.tmpl new file mode 100644 index 0000000..de80fac --- /dev/null +++ b/templates/search.tmpl @@ -0,0 +1,37 @@ +{{template "header.tmpl" .HeaderData}} +{{template "navigation.tmpl" .NavbarData}} +
Search
+ +
+
+ Query + + + Type + + + +
+
+ +{{if eq .Type "statuses"}} +{{range .Statuses}} +{{template "status.tmpl" .}} +{{end}} + +{{end}} +{{if eq .Type "accounts"}} +{{template "userlist.tmpl" .Users}} +{{end}} + + +{{template "footer.tmpl"}}