diff --git a/internal/webserver/handler_search.go b/internal/webserver/handler_search.go index a46a8b2..9547449 100644 --- a/internal/webserver/handler_search.go +++ b/internal/webserver/handler_search.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "net/url" + "strconv" "strings" "gitlab.com/offline-twitter/twitter_offline_engine/pkg/persistence" @@ -29,6 +30,25 @@ func (app *Application) Search(w http.ResponseWriter, r *http.Request) { // Handle "@username" if search_text[0] == '@' { http.Redirect(w, r, fmt.Sprintf("/%s", search_text[1:]), 302) + return + } + + // Handle pasted URLs + maybe_url, err := url.Parse(search_text) + if err == nil && (maybe_url.Host == "twitter.com" || maybe_url.Host == "mobile.twitter.com") { + parts := strings.Split(strings.Trim(maybe_url.Path, "/"), "/") + if len(parts) == 3 && parts[1] == "status" { + id, err := strconv.Atoi(parts[2]) + if err == nil { + http.Redirect(w, r, fmt.Sprintf("/tweet/%d", id), 302) + return + } + } + + if len(parts) == 1 { + http.Redirect(w, r, fmt.Sprintf("/%s", parts[0]), 302) + return + } } c, err := persistence.NewCursorFromSearchQuery(search_text) diff --git a/internal/webserver/server_test.go b/internal/webserver/server_test.go index 95651bc..8c3af3f 100644 --- a/internal/webserver/server_test.go +++ b/internal/webserver/server_test.go @@ -216,6 +216,38 @@ func TestSearchRedirectOnUserHandle(t *testing.T) { assert.Equal(resp.Header.Get("Location"), "/somebody") } +func TestSearchRedirectOnTweetLink(t *testing.T) { + assert := assert.New(t) + + // Desktop URL + resp := do_request(httptest.NewRequest("GET", + fmt.Sprintf("/search/%s", url.PathEscape("https://twitter.com/wispem_wantex/status/1695221528617468324")), + nil)) + assert.Equal(resp.StatusCode, 302) + assert.Equal(resp.Header.Get("Location"), "/tweet/1695221528617468324") + + // Mobile URL + resp = do_request(httptest.NewRequest("GET", + fmt.Sprintf("/search/%s", url.PathEscape("https://mobile.twitter.com/wispem_wantex/status/1695221528617468324")), + nil)) + assert.Equal(resp.StatusCode, 302) + assert.Equal(resp.Header.Get("Location"), "/tweet/1695221528617468324") +} + +func TestSearchRedirectOnUserFeedLink(t *testing.T) { + assert := assert.New(t) + + // Desktop URL + resp := do_request(httptest.NewRequest("GET", fmt.Sprintf("/search/%s", url.PathEscape("https://twitter.com/agsdf")), nil)) + assert.Equal(resp.StatusCode, 302) + assert.Equal(resp.Header.Get("Location"), "/agsdf") + + // Mobile URL + resp = do_request(httptest.NewRequest("GET", fmt.Sprintf("/search/%s", url.PathEscape("https://mobile.twitter.com/agsdfhh")), nil)) + assert.Equal(resp.StatusCode, 302) + assert.Equal(resp.Header.Get("Location"), "/agsdfhh") +} + // Tweet Detail page // ----------------- diff --git a/internal/webserver/static/styles.css b/internal/webserver/static/styles.css index 05f77ee..14762ad 100644 --- a/internal/webserver/static/styles.css +++ b/internal/webserver/static/styles.css @@ -243,6 +243,7 @@ h3 { .posted-at-container { flex-grow: 1; + min-width: 5em; } p.posted-at { float: right; @@ -550,6 +551,7 @@ ul.dropdown-items { outline: 1px solid var(--color-outline-gray); border-radius: 0.3em; visibility: hidden; + z-index: 1; /* otherwise in quote-tweets, the dropdown button from quoting-tweet is on top of it */ } .dropdown-button:focus + .dropdown-items, .dropdown-items:hover { visibility: visible; diff --git a/internal/webserver/tpl/tweet_page_includes/single_tweet.tpl b/internal/webserver/tpl/tweet_page_includes/single_tweet.tpl index f69641b..d3c6e19 100644 --- a/internal/webserver/tpl/tweet_page_includes/single_tweet.tpl +++ b/internal/webserver/tpl/tweet_page_includes/single_tweet.tpl @@ -69,7 +69,9 @@ class="embedded-link rounded-gray-outline unstyled-link" target="_blank" href="{{.Text}}" - style="max-width: {{if (ne .ThumbnailWidth 0)}}{{.ThumbnailWidth}}px {{else}}fit-content {{end}}"> + style="max-width: {{if (ne .ThumbnailWidth 0)}}{{.ThumbnailWidth}}px {{else}}fit-content {{end}}" + hx-trigger="click consume" + >