Enable search sort-orders
This commit is contained in:
parent
99ef17f820
commit
535f28c278
@ -9,8 +9,41 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence"
|
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence"
|
||||||
|
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/scraper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SearchPageData struct {
|
||||||
|
persistence.Feed
|
||||||
|
SearchText string
|
||||||
|
SortOrder persistence.SortOrder
|
||||||
|
SortOrderOptions []string
|
||||||
|
// TODO: fill out the search text in the search bar as well (needs modifying the base template)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSearchPageData() SearchPageData {
|
||||||
|
ret := SearchPageData{SortOrderOptions: []string{}}
|
||||||
|
for i := 0; i < 4; i++ { // Don't include "Liked At" option which is #4
|
||||||
|
ret.SortOrderOptions = append(ret.SortOrderOptions, persistence.SortOrder(i).String())
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t SearchPageData) Tweet(id scraper.TweetID) scraper.Tweet {
|
||||||
|
return t.Tweets[id]
|
||||||
|
}
|
||||||
|
func (t SearchPageData) User(id scraper.UserID) scraper.User {
|
||||||
|
return t.Users[id]
|
||||||
|
}
|
||||||
|
func (t SearchPageData) Retweet(id scraper.TweetID) scraper.Retweet {
|
||||||
|
return t.Retweets[id]
|
||||||
|
}
|
||||||
|
func (t SearchPageData) Space(id scraper.SpaceID) scraper.Space {
|
||||||
|
return t.Spaces[id]
|
||||||
|
}
|
||||||
|
func (t SearchPageData) FocusedTweetID() scraper.TweetID {
|
||||||
|
return scraper.TweetID(0)
|
||||||
|
}
|
||||||
|
|
||||||
func (app *Application) Search(w http.ResponseWriter, r *http.Request) {
|
func (app *Application) Search(w http.ResponseWriter, r *http.Request) {
|
||||||
app.traceLog.Printf("'Search' handler (path: %q)", r.URL.Path)
|
app.traceLog.Printf("'Search' handler (path: %q)", r.URL.Path)
|
||||||
|
|
||||||
@ -67,6 +100,11 @@ func (app *Application) Search(w http.ResponseWriter, r *http.Request) {
|
|||||||
app.error_400_with_message(w, "invalid cursor (must be a number)")
|
app.error_400_with_message(w, "invalid cursor (must be a number)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var is_ok bool
|
||||||
|
c.SortOrder, is_ok = persistence.SortOrderFromString(r.URL.Query().Get("sort-order"))
|
||||||
|
if !is_ok && r.URL.Query().Get("sort-order") != "" {
|
||||||
|
app.error_400_with_message(w, "Invalid sort order")
|
||||||
|
}
|
||||||
|
|
||||||
feed, err := app.Profile.NextPage(c, app.ActiveUser.ID)
|
feed, err := app.Profile.NextPage(c, app.ActiveUser.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -77,7 +115,10 @@ func (app *Application) Search(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data := UserProfileData{Feed: feed} // TODO: wrong struct
|
data := NewSearchPageData()
|
||||||
|
data.Feed = feed
|
||||||
|
data.SearchText = search_text
|
||||||
|
data.SortOrder = c.SortOrder
|
||||||
|
|
||||||
if r.Header.Get("HX-Request") == "true" && c.CursorPosition == persistence.CURSOR_MIDDLE {
|
if r.Header.Get("HX-Request") == "true" && c.CursorPosition == persistence.CURSOR_MIDDLE {
|
||||||
// It's a Show More request
|
// It's a Show More request
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/Masterminds/sprig/v3"
|
"github.com/Masterminds/sprig/v3"
|
||||||
|
|
||||||
|
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence"
|
||||||
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/scraper"
|
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/scraper"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -86,14 +88,15 @@ func (app *Application) buffered_render_tweet_page(w http.ResponseWriter, tpl_fi
|
|||||||
|
|
||||||
r := renderer{
|
r := renderer{
|
||||||
Funcs: func_map(template.FuncMap{
|
Funcs: func_map(template.FuncMap{
|
||||||
"tweet": data.Tweet,
|
"tweet": data.Tweet,
|
||||||
"user": data.User,
|
"user": data.User,
|
||||||
"retweet": data.Retweet,
|
"retweet": data.Retweet,
|
||||||
"space": data.Space,
|
"space": data.Space,
|
||||||
"active_user": app.get_active_user,
|
"active_user": app.get_active_user,
|
||||||
"focused_tweet_id": data.FocusedTweetID,
|
"focused_tweet_id": data.FocusedTweetID,
|
||||||
"get_entities": get_entities,
|
"get_entities": get_entities,
|
||||||
"get_tombstone_text": get_tombstone_text,
|
"get_tombstone_text": get_tombstone_text,
|
||||||
|
"cursor_to_query_params": cursor_to_query_params,
|
||||||
}),
|
}),
|
||||||
Filenames: append(partials, get_filepath(tpl_file)),
|
Filenames: append(partials, get_filepath(tpl_file)),
|
||||||
TplName: "base",
|
TplName: "base",
|
||||||
@ -121,14 +124,15 @@ func (app *Application) buffered_render_tweet_htmx(w http.ResponseWriter, tpl_na
|
|||||||
|
|
||||||
r := renderer{
|
r := renderer{
|
||||||
Funcs: func_map(template.FuncMap{
|
Funcs: func_map(template.FuncMap{
|
||||||
"tweet": data.Tweet,
|
"tweet": data.Tweet,
|
||||||
"user": data.User,
|
"user": data.User,
|
||||||
"retweet": data.Retweet,
|
"retweet": data.Retweet,
|
||||||
"space": data.Space,
|
"space": data.Space,
|
||||||
"active_user": app.get_active_user,
|
"active_user": app.get_active_user,
|
||||||
"focused_tweet_id": data.FocusedTweetID,
|
"focused_tweet_id": data.FocusedTweetID,
|
||||||
"get_entities": get_entities,
|
"get_entities": get_entities,
|
||||||
"get_tombstone_text": get_tombstone_text,
|
"get_tombstone_text": get_tombstone_text,
|
||||||
|
"cursor_to_query_params": cursor_to_query_params,
|
||||||
}),
|
}),
|
||||||
Filenames: partials,
|
Filenames: partials,
|
||||||
TplName: tpl_name,
|
TplName: tpl_name,
|
||||||
@ -233,3 +237,10 @@ func (r renderer) BufferedRender(w io.Writer) {
|
|||||||
_, err = buf.WriteTo(w)
|
_, err = buf.WriteTo(w)
|
||||||
panic_if(err)
|
panic_if(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cursor_to_query_params(c persistence.Cursor) string {
|
||||||
|
result := url.Values{}
|
||||||
|
result.Set("cursor", fmt.Sprint(c.CursorValue))
|
||||||
|
result.Set("sort-order", c.SortOrder.String())
|
||||||
|
return result.Encode()
|
||||||
|
}
|
||||||
|
@ -270,6 +270,41 @@ func TestSearchWithCursor(t *testing.T) {
|
|||||||
assert.Len(cascadia.QueryAll(root, selector(".timeline > .tweet")), 2)
|
assert.Len(cascadia.QueryAll(root, selector(".timeline > .tweet")), 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSearchWithSortOrder(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
resp := do_request(httptest.NewRequest("GET", "/search/think?sort-order=most%20likes", nil))
|
||||||
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
root, err := html.Parse(resp.Body)
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Contains(cascadia.Query(root, selector("select[name='sort-order'] option[selected]")).FirstChild.Data, "most likes")
|
||||||
|
|
||||||
|
tweets := cascadia.QueryAll(root, selector(".timeline > .tweet"))
|
||||||
|
txts := []string{
|
||||||
|
"Morally nuanced and complicated discussion",
|
||||||
|
"a lot of y’all embarrass yourselves on this",
|
||||||
|
"this is why the \"think tank mindset\" is a dead end",
|
||||||
|
"At this point what can we expect I guess",
|
||||||
|
"Idk if this is relevant to your department",
|
||||||
|
}
|
||||||
|
for i, txt := range txts {
|
||||||
|
assert.Contains(cascadia.Query(tweets[i], selector("p.text")).FirstChild.Data, txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = do_request(httptest.NewRequest("GET", "/search/think?sort-order=most%20likes&cursor=413", nil))
|
||||||
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
root, err = html.Parse(resp.Body)
|
||||||
|
require.NoError(err)
|
||||||
|
tweets = cascadia.QueryAll(root, selector(".timeline > .tweet"))
|
||||||
|
for i, txt := range txts[2:] {
|
||||||
|
assert.Contains(cascadia.Query(tweets[i], selector("p.text")).FirstChild.Data, txt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search bar pasted link redirects
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
func TestSearchRedirectOnUserHandle(t *testing.T) {
|
func TestSearchRedirectOnUserHandle(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
{{define "title"}}Search{{end}}
|
{{define "title"}}Search{{end}}
|
||||||
|
|
||||||
{{define "main"}}
|
{{define "main"}}
|
||||||
|
<div class="search-header">
|
||||||
|
<h2>Search results: {{.SearchText}}</h2>
|
||||||
|
<select name="sort-order" style="text-transform: capitalize;" hx-get="#" hx-target="body" hx-push-url="true">
|
||||||
|
{{range .SortOrderOptions}}
|
||||||
|
<option
|
||||||
|
value="{{.}}"
|
||||||
|
{{if (eq ($.SortOrder.String) .)}} selected {{end}}
|
||||||
|
style="text-transform: capitalize;"
|
||||||
|
>{{.}}</option>
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="timeline">
|
<div class="timeline">
|
||||||
{{template "timeline" .}}
|
{{template "timeline" .}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<p>End of feed</p>
|
<p>End of feed</p>
|
||||||
{{else}}
|
{{else}}
|
||||||
<button class="show-more"
|
<button class="show-more"
|
||||||
hx-get="?cursor={{.CursorBottom.CursorValue}}"
|
hx-get="?{{(cursor_to_query_params .CursorBottom)}}"
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
>Show more</button>
|
>Show more</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -19,6 +19,21 @@ const (
|
|||||||
SORT_ORDER_LIKED_AT
|
SORT_ORDER_LIKED_AT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (o SortOrder) String() string {
|
||||||
|
return []string{"newest", "oldest", "most likes", "most retweets", "liked at"}[o]
|
||||||
|
}
|
||||||
|
|
||||||
|
func SortOrderFromString(s string) (SortOrder, bool) {
|
||||||
|
result, is_ok := map[string]SortOrder{
|
||||||
|
"newest": SORT_ORDER_NEWEST,
|
||||||
|
"oldest": SORT_ORDER_OLDEST,
|
||||||
|
"most likes": SORT_ORDER_MOST_LIKES,
|
||||||
|
"most retweets": SORT_ORDER_MOST_RETWEETS,
|
||||||
|
"liked at": SORT_ORDER_LIKED_AT,
|
||||||
|
}[s]
|
||||||
|
return result, is_ok // Have to store as temporary variable b/c otherwise it interprets it as single-value and compile fails
|
||||||
|
}
|
||||||
|
|
||||||
func (o SortOrder) OrderByClause() string {
|
func (o SortOrder) OrderByClause() string {
|
||||||
switch o {
|
switch o {
|
||||||
case SORT_ORDER_NEWEST:
|
case SORT_ORDER_NEWEST:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user