Improve login flow

- on successful login, kick off scraping of logged-in user, their followees, and only-followed home timeline
- `--auto-open` flag now opens `/login` instead of home page
- add HTMX spinner for login page
- provide toast error notification if login is challenged (e.g., 2FA)
This commit is contained in:
Alessio 2024-03-03 15:04:19 -08:00
parent 2d14621cc0
commit 8255c4f65e
3 changed files with 52 additions and 6 deletions

View File

@ -42,12 +42,13 @@ func (app *Application) Login(w http.ResponseWriter, r *http.Request) {
form.Validate()
if len(form.FormErrors) == 0 {
api := scraper.NewGuestSession()
api.LogIn(form.Username, form.Password)
app.Profile.SaveSession(api)
if err := app.SetActiveUser(api.UserHandle); err != nil {
app.ErrorLog.Printf(err.Error())
challenge := api.LogIn(form.Username, form.Password)
if challenge != nil {
panic( // Middleware will trap this panic and return an HTMX error toast
"Twitter challenged your login. Make sure your username is your user handle (not email). " +
"If you're logging in from a new device, try doing it on the official Twitter site first, then try again here.")
}
http.Redirect(w, r, "/login", 303)
app.after_login(w, r, api)
}
return
}
@ -60,6 +61,46 @@ func (app *Application) Login(w http.ResponseWriter, r *http.Request) {
app.buffered_render_page(w, "tpl/login.tpl", PageGlobalData{}, &data)
}
func (app *Application) after_login(w http.ResponseWriter, r *http.Request, api scraper.API) {
app.Profile.SaveSession(api)
// Ensure the user is downloaded
user, err := scraper.GetUser(api.UserHandle)
if err != nil {
app.error_404(w)
return
}
panic_if(app.Profile.SaveUser(&user))
panic_if(app.Profile.DownloadUserContentFor(&user))
// Now that the user is scraped for sure, set them as the logged-in user
err = app.SetActiveUser(api.UserHandle)
panic_if(err)
// Scrape the user's feed
trove, err := scraper.GetHomeTimeline("", true)
if err != nil {
app.ErrorLog.Printf("Initial timeline scrape failed: %s", err.Error())
http.Redirect(w, r, "/", 303)
}
fmt.Println("Saving initial feed results...")
app.Profile.SaveTweetTrove(trove, false)
go app.Profile.SaveTweetTrove(trove, true)
// Scrape the user's followers
trove, err = scraper.GetFollowees(user.ID, 1000)
if err != nil {
app.ErrorLog.Printf("Failed to scrape followers: %s", err.Error())
http.Redirect(w, r, "/", 303)
}
app.Profile.SaveTweetTrove(trove, false)
app.Profile.SaveAsFolloweesList(user.ID, trove)
go app.Profile.SaveTweetTrove(trove, true)
// Redirect to Timeline
http.Redirect(w, r, "/", 303)
}
func (app *Application) ChangeSession(w http.ResponseWriter, r *http.Request) {
app.traceLog.Printf("'change-session' handler (path: %q)", r.URL.Path)
form := struct {

View File

@ -150,7 +150,7 @@ func (app *Application) Run(address string, should_auto_open bool) {
app.start_background()
if should_auto_open {
go openWebPage("http://" + address)
go openWebPage("http://" + address + "/login")
}
err := srv.ListenAndServe()
app.ErrorLog.Fatal(err)

View File

@ -35,6 +35,11 @@
<div class="field-container submit-container">
<input type='submit' value='Login'>
</div>
<div class="htmx-spinner-container">
<div class="htmx-spinner-background"></div>
<img class="svg-icon htmx-spinner" src="/static/icons/spinner.svg" />
</div>
</form>
</div>
{{end}}