Enable searching through the search bar
This commit is contained in:
parent
b8d0d6be7a
commit
72110e6558
58
internal/webserver/handler_search.go
Normal file
58
internal/webserver/handler_search.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package webserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (app *Application) Search(w http.ResponseWriter, r *http.Request) {
|
||||||
|
app.traceLog.Printf("'Search' handler (path: %q)", r.URL.Path)
|
||||||
|
|
||||||
|
search_text := strings.Trim(r.URL.Path, "/")
|
||||||
|
if search_text == "" {
|
||||||
|
// Redirect GET param "q" to use a URL param instead
|
||||||
|
search_text = r.URL.Query().Get("q")
|
||||||
|
if search_text == "" {
|
||||||
|
app.error_400_with_message(w, "Empty search query")
|
||||||
|
return
|
||||||
|
// TODO: return an actual page
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, fmt.Sprintf("/search/%s", url.PathEscape(search_text)), 302)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := persistence.NewCursorFromSearchQuery(search_text)
|
||||||
|
if err != nil {
|
||||||
|
app.error_400_with_message(w, err.Error())
|
||||||
|
return
|
||||||
|
// TODO: return actual page
|
||||||
|
}
|
||||||
|
err = parse_cursor_value(&c, r)
|
||||||
|
if err != nil {
|
||||||
|
app.error_400_with_message(w, "invalid cursor (must be a number)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
feed, err := app.Profile.NextPage(c)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, persistence.ErrEndOfFeed) {
|
||||||
|
// TODO
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data := UserProfileData{Feed: feed} // TODO: wrong struct
|
||||||
|
|
||||||
|
if r.Header.Get("HX-Request") == "true" && c.CursorPosition == persistence.CURSOR_MIDDLE {
|
||||||
|
// It's a Show More request
|
||||||
|
app.buffered_render_tweet_htmx(w, "timeline", data)
|
||||||
|
} else {
|
||||||
|
app.buffered_render_tweet_page(w, "tpl/search.tpl", data)
|
||||||
|
}
|
||||||
|
}
|
@ -118,6 +118,8 @@ func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
app.UserFollow(w, r)
|
app.UserFollow(w, r)
|
||||||
case "unfollow":
|
case "unfollow":
|
||||||
app.UserUnfollow(w, r)
|
app.UserUnfollow(w, r)
|
||||||
|
case "search":
|
||||||
|
http.StripPrefix("/search", http.HandlerFunc(app.Search)).ServeHTTP(w, r)
|
||||||
default:
|
default:
|
||||||
app.UserFeed(w, r)
|
app.UserFeed(w, r)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package webserver_test
|
package webserver_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/andybalholm/cascadia"
|
"github.com/andybalholm/cascadia"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -158,6 +161,53 @@ func TestTimelineWithCursorBadNumber(t *testing.T) {
|
|||||||
require.Equal(resp.StatusCode, 400)
|
require.Equal(resp.StatusCode, 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search page
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
func TestSearchQueryStringRedirect(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
// With a cursor but it sucks
|
||||||
|
resp := do_request(httptest.NewRequest("GET", "/search?q=asdf", nil))
|
||||||
|
assert.Equal(resp.StatusCode, 302)
|
||||||
|
assert.Equal(resp.Header.Get("Location"), "/search/asdf")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearch(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
resp := do_request(httptest.NewRequest("GET", fmt.Sprintf("/search/%s", url.PathEscape("to:spacex to:covfefeanon")), nil))
|
||||||
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
|
||||||
|
root, err := html.Parse(resp.Body)
|
||||||
|
require.NoError(err)
|
||||||
|
title_node := cascadia.Query(root, selector("title"))
|
||||||
|
assert.Equal(title_node.FirstChild.Data, "Offline Twitter | Search")
|
||||||
|
|
||||||
|
tweet_nodes := cascadia.QueryAll(root, selector(".timeline > .tweet"))
|
||||||
|
assert.Len(tweet_nodes, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearchWithCursor(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
// First, without the cursor
|
||||||
|
resp := do_request(httptest.NewRequest("GET", "/search/who%20are", nil))
|
||||||
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
root, err := html.Parse(resp.Body)
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Len(cascadia.QueryAll(root, selector(".timeline > .tweet")), 3)
|
||||||
|
|
||||||
|
// Add a cursor with the 1st tweet's posted_at time
|
||||||
|
resp = do_request(httptest.NewRequest("GET", "/search/who%20are?cursor=1628979529", nil))
|
||||||
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
root, err = html.Parse(resp.Body)
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Len(cascadia.QueryAll(root, selector(".timeline > .tweet")), 2)
|
||||||
|
}
|
||||||
|
|
||||||
// Tweet Detail page
|
// Tweet Detail page
|
||||||
// -----------------
|
// -----------------
|
||||||
|
|
||||||
|
@ -381,6 +381,10 @@ svg {
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
.top-bar form {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
.search-bar {
|
.search-bar {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
<a onclick="window.history.back()" class="back-button quick-link">
|
<a onclick="window.history.back()" class="back-button quick-link">
|
||||||
<img class="svg-icon" src="/static/icons/back.svg" />
|
<img class="svg-icon" src="/static/icons/back.svg" />
|
||||||
</a>
|
</a>
|
||||||
<input class="search-bar" placeholder="Search" type="text" />
|
<form hx-get="/search" hx-push-url="true" hx-target="body">
|
||||||
|
<input name="q" class="search-bar" placeholder="Search" type="text" />
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{{template "nav-sidebar"}}
|
{{template "nav-sidebar"}}
|
||||||
<main>
|
<main>
|
||||||
|
7
internal/webserver/tpl/search.tpl
Normal file
7
internal/webserver/tpl/search.tpl
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{{define "title"}}Search{{end}}
|
||||||
|
|
||||||
|
{{define "main"}}
|
||||||
|
<div class="timeline">
|
||||||
|
{{template "timeline" .}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
Loading…
x
Reference in New Issue
Block a user