Add user search page
authorr <r@freesoftwareextremist.com>
Thu, 30 Jan 2020 15:32:37 +0000 (15:32 +0000)
committerr <r@freesoftwareextremist.com>
Thu, 30 Jan 2020 15:37:07 +0000 (15:37 +0000)
mastodon/status.go
renderer/model.go
renderer/renderer.go
service/auth.go
service/logging.go
service/service.go
service/transport.go
static/style.css
templates/search.tmpl
templates/user.tmpl
templates/usersearch.tmpl [new file with mode: 0644]

index 5be4f49b7cc73bb37d72232c15ad203a0d431b5b..22f042c182d6353b2f04b6d6d321e7c65ad9b6c8 100644 (file)
@@ -284,14 +284,17 @@ func (c *Client) DeleteStatus(ctx context.Context, id string) error {
 }
 
 // Search search content with query.
-func (c *Client) Search(ctx context.Context, q string, qType string, limit int, resolve bool, offset int) (*Results, error) {
+func (c *Client) Search(ctx context.Context, q string, qType string, limit int, resolve bool, offset int, accountID string) (*Results, error) {
+       var results Results
        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
+       if len(accountID) > 0 {
+               params.Set("account_id", accountID)
+       }
        err := c.doAPI(ctx, http.MethodGet, "/api/v2/search", params, &results, nil)
        if err != nil {
                return nil, err
index 8df64ab159f02323e4d3d18b926987b7269144e3..77e67048945faf93441c5ffa95e7ffd50f532924 100644 (file)
@@ -74,6 +74,14 @@ type UserData struct {
        DarkMode bool
 }
 
+type UserSearchData struct {
+       *CommonData
+       User     *mastodon.Account
+       Q        string
+       Statuses []*mastodon.Status
+       NextLink string
+}
+
 type AboutData struct {
        *CommonData
 }
index 2ad5ddffc16f39b10e203dbb410e241f30051d41..cbb6c73928f2950352eaa2c9f721bf01f2ab28ac 100644 (file)
@@ -23,6 +23,7 @@ type Renderer interface {
        RenderThreadPage(ctx *Context, writer io.Writer, data *ThreadData) (err error)
        RenderNotificationPage(ctx *Context, writer io.Writer, data *NotificationData) (err error)
        RenderUserPage(ctx *Context, writer io.Writer, data *UserData) (err error)
+       RenderUserSearchPage(ctx *Context, writer io.Writer, data *UserSearchData) (err error)
        RenderAboutPage(ctx *Context, writer io.Writer, data *AboutData) (err error)
        RenderEmojiPage(ctx *Context, writer io.Writer, data *EmojiData) (err error)
        RenderLikedByPage(ctx *Context, writer io.Writer, data *LikedByData) (err error)
@@ -87,6 +88,11 @@ func (r *renderer) RenderUserPage(ctx *Context, writer io.Writer,
        return r.template.ExecuteTemplate(writer, "user.tmpl", WithContext(data, ctx))
 }
 
+func (r *renderer) RenderUserSearchPage(ctx *Context, writer io.Writer, 
+       data *UserSearchData) (err error) {
+       return r.template.ExecuteTemplate(writer, "usersearch.tmpl", WithContext(data, ctx))
+}
+
 func (r *renderer) RenderAboutPage(ctx *Context, writer io.Writer,
        data *AboutData) (err error) {
        return r.template.ExecuteTemplate(writer, "about.tmpl", WithContext(data, ctx))
index 78934fdf79e468b0b53856cdf70f538f8a6ab342..f127f5cbf69c95ef2fa2146bd38e43b706d7ec33 100644 (file)
@@ -162,6 +162,15 @@ func (s *as) ServeSearchPage(ctx context.Context, c *model.Client, q string,
        return s.Service.ServeSearchPage(ctx, c, q, qType, offset)
 }
 
+func (s *as)  ServeUserSearchPage(ctx context.Context, c *model.Client,
+       id string, q string, offset int) (err error) {
+       err = s.authenticateClient(ctx, c)
+       if err != nil {
+               return
+       }
+       return s.Service.ServeUserSearchPage(ctx, c, id, q, offset)
+}
+
 func (s *as) ServeSettingsPage(ctx context.Context, c *model.Client) (err error) {
        err = s.authenticateClient(ctx, c)
        if err != nil {
index e4f8985d5069766bcb4d38d27718a0d7c20e99f2..edd1035c4a9ead6f4627dfcc85c3558b944720b8 100644 (file)
@@ -129,6 +129,15 @@ func (s *ls) ServeSearchPage(ctx context.Context, c *model.Client, q string,
        return s.Service.ServeSearchPage(ctx, c, q, qType, offset)
 }
 
+func (s *ls)  ServeUserSearchPage(ctx context.Context, c *model.Client,
+       id string, q string, offset int) (err error) {
+       defer func(begin time.Time) {
+               s.logger.Printf("method=%v, took=%v, err=%v\n",
+                       "ServeUserSearchPage", time.Since(begin), err)
+       }(time.Now())
+       return s.Service.ServeUserSearchPage(ctx, c, id, q, offset)
+}
+
 func (s *ls) ServeSettingsPage(ctx context.Context, c *model.Client) (err error) {
        defer func(begin time.Time) {
                s.logger.Printf("method=%v, took=%v, err=%v\n",
index fbf617a6faa52c102a2bf392a7dbeabf44139427..fe025b820dbe46423206bf5613c64b940f59ce4b 100644 (file)
@@ -32,6 +32,7 @@ type Service interface {
        ServeAboutPage(ctx context.Context, c *model.Client) (err error)
        ServeEmojiPage(ctx context.Context, c *model.Client) (err error)
        ServeSearchPage(ctx context.Context, c *model.Client, q string, qType string, offset int) (err error)
+       ServeUserSearchPage(ctx context.Context, c *model.Client, id string, q string, offset int) (err error)
        ServeSettingsPage(ctx context.Context, c *model.Client) (err error)
        NewSession(ctx context.Context, instance string) (redirectUrl string, sessionID string, err error)
        Signin(ctx context.Context, c *model.Client, sessionID string, code string) (token string, err error)
@@ -560,6 +561,48 @@ func (svc *service) ServeUserPage(ctx context.Context, c *model.Client,
        return svc.renderer.RenderUserPage(rCtx, c.Writer, data)
 }
 
+func (svc *service) ServeUserSearchPage(ctx context.Context, c *model.Client,
+       id string, q string, offset int) (err error) {
+
+       var nextLink string
+       var title = "search"
+
+       user, err := c.GetAccount(ctx, id)
+       if err != nil {
+               return
+       }
+
+       results, err := c.Search(ctx, q, "statuses", 20, true, offset, id)
+       if err != nil {
+               return
+       }
+
+       if len(results.Statuses) == 20 {
+               offset += 20
+               nextLink = fmt.Sprintf("/usersearch/%s?q=%s&offset=%d", id, q, offset)
+       }
+
+       if len(q) > 0 {
+               title += " \"" + q + "\""
+       }
+
+       commonData, err := svc.getCommonData(ctx, c, title)
+       if err != nil {
+               return
+       }
+
+       data := &renderer.UserSearchData{
+               CommonData: commonData,
+               User:       user,
+               Q:          q,
+               Statuses:   results.Statuses,
+               NextLink:   nextLink,
+       }
+
+       rCtx := getRendererContext(c)
+       return svc.renderer.RenderUserSearchPage(rCtx, c.Writer, data)
+}
+
 func (svc *service) ServeAboutPage(ctx context.Context, c *model.Client) (err error) {
        commonData, err := svc.getCommonData(ctx, c, "about")
        if err != nil {
@@ -600,7 +643,7 @@ func (svc *service) ServeSearchPage(ctx context.Context, c *model.Client,
        var nextLink string
        var title = "search"
 
-       results, err := c.Search(ctx, q, qType, 20, true, offset)
+       results, err := c.Search(ctx, q, qType, 20, true, offset, "")
        if err != nil {
                return
        }
index fbab2e55d254583cc08256bb4df26b65e426e722..8f6c54717213e009a07953e63cba052424b6522e 100644 (file)
@@ -188,6 +188,30 @@ func NewHandler(s Service, staticDir string) http.Handler {
                }
        }
 
+       userSearchPage := func(w http.ResponseWriter, req *http.Request) {
+               c := newClient(w)
+               ctx := newCtxWithSesion(req)
+               id, _ := mux.Vars(req)["id"]
+               q := req.URL.Query().Get("q")
+               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, c, err)
+                               return
+                       }
+               }
+
+               err = s.ServeUserSearchPage(ctx, c, id, q, offset)
+               if err != nil {
+                       s.ServeErrorPage(ctx, c, err)
+                       return
+               }
+       }
+
        aboutPage := func(w http.ResponseWriter, req *http.Request) {
                c := newClient(w)
                ctx := newCtxWithSesion(req)
@@ -545,6 +569,7 @@ func NewHandler(s Service, staticDir string) http.Handler {
        r.HandleFunc("/followers/{id}", followersPage).Methods(http.MethodGet)
        r.HandleFunc("/notifications", notificationsPage).Methods(http.MethodGet)
        r.HandleFunc("/user/{id}", userPage).Methods(http.MethodGet)
+       r.HandleFunc("/usersearch/{id}", userSearchPage).Methods(http.MethodGet)
        r.HandleFunc("/about", aboutPage).Methods(http.MethodGet)
        r.HandleFunc("/emojis", emojisPage).Methods(http.MethodGet)
        r.HandleFunc("/search", searchPage).Methods(http.MethodGet)
index 888fb5d4082325d872a20aeeb59a330bd58d47f0..67dbd77c343a12667b15d2fbc40b7b53774aac3b 100644 (file)
 }
 
 .page-title {
-       font-size: 23pt;
+       font-size: 18pt;
 }
 
 .post-form {
@@ -426,6 +426,10 @@ a:hover,
        margin: 0 8px 0 8px;
 }
 
+.search-form {
+       margin: 16px 0 0 0;
+}
+
 .dark {
        background-color: #222222;
        background-image: none;
index acbfbdd90dc33b16ee3be6dcdce27a36b1ec7159..96548b55d0dbc7c6744d039d45a7981a4b7974a5 100644 (file)
@@ -3,22 +3,20 @@
 {{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
 <div class="page-title"> Search </div>
 
-<div>
-       <form action="/search" method="GET">
-               <span class="post-form-field>
-                       <label for="query"> Query </label>
-                       <input id="query" name="q" value="{{.Q}}">
-               </span>
-               <span class="post-form-field>
-                       <label for="type"> Type </label>
-                       <select id="type" name="type">
-                               <option value="statuses" {{if eq .Type "statuses"}}selected{{end}}>Statuses</option>
-                               <option value="accounts" {{if eq .Type "accounts"}}selected{{end}}>Accounts</option>
-                       </select>
-               </span>
-               <button type="submit"> Search </button>
-       </form>
-</div>
+<form class="search-form" action="/search" method="GET">
+       <span class="post-form-field>
+               <label for="query"> Query </label>
+               <input id="query" name="q" value="{{.Q}}">
+       </span>
+       <span class="post-form-field>
+               <label for="type"> Type </label>
+               <select id="type" name="type">
+                       <option value="statuses" {{if eq .Type "statuses"}}selected{{end}}>Statuses</option>
+                       <option value="accounts" {{if eq .Type "accounts"}}selected{{end}}>Accounts</option>
+               </select>
+       </span>
+       <button type="submit"> Search </button>
+</form>
 
 {{if eq .Type "statuses"}}
 {{range .Statuses}}
index ca8a1ad276334cdc8ac5b5e1d4968ea6543d163a..e3cb7e8e3b177258f3a433a3c05ca2986a635738 100644 (file)
@@ -44,6 +44,9 @@
                        <a href="/following/{{.User.ID}}"> {{.User.FollowingCount}} following </a> - 
                        <a href="/followers/{{.User.ID}}"> {{.User.FollowersCount}} followers </a>
                </div>
+               <div>
+                       <a href="/usersearch/{{.User.ID}}"> search statuses </a>
+               </div>
        </div>
        <div class="user-profile-decription">
        {{.User.Note}}
diff --git a/templates/usersearch.tmpl b/templates/usersearch.tmpl
new file mode 100644 (file)
index 0000000..8e19fd1
--- /dev/null
@@ -0,0 +1,25 @@
+{{with .Data}}
+{{template "header.tmpl" (WithContext .HeaderData $.Ctx)}}
+{{template "navigation.tmpl" (WithContext .NavbarData $.Ctx)}}
+<div class="page-title"> Search {{EmojiFilter .User.DisplayName .User.Emojis}}'s statuses </div>
+
+<form class="search-form" action="/usersearch/{{.User.ID}}" method="GET">
+       <span class="post-form-field>
+               <label for="query"> Query </label>
+               <input id="query" name="q" value="{{.Q}}">
+       </span>
+       <button type="submit"> Search </button>
+</form>
+
+{{range .Statuses}}
+{{template "status.tmpl" (WithContext . $.Ctx)}}
+{{end}}
+
+<div class="pagination">
+       {{if .NextLink}}
+               <a href="{{.NextLink}}">next</a>
+       {{end}}
+</div>
+
+{{template "footer.tmpl"}}
+{{end}}