jes notes Index Gallery . Signed Distance Functions Shaft passers Snap issues

2025-07-23

Last modified: 2025-07-23 22:13:09

< 2025-07-22 2025-07-24 >

Webmail

What is wrong with the AI Test User emails?

  1. shows CSS in the body area
  2. has its own "panels" that seem to overlap the borders of the body area

OK, it's because for some reason this is the body_text rather than body_html.

So simple_format is not preventing XSS.

If I print <%= @email.body_text %> it works correctly, but <%= simple_format @email.body_text %> or sanitize or auto_link all pass through HTML tags that they shouldn't. Baffling, especially that sanitize is not sanitising.

OK, sanitize is meant to strip dangerous HTML, not all HTML. So it is doing the right thing. You want h to escape all html.

So that solves the main problem, of being able to sneak through unescaped html by calling it text/plain. But why are the AI Test User emails not looking like html?

It's because it's not a multitype email! It's a single-part email where the Content-Type is text/html.

So I need to check the Content-Type of the email and display it accordingly. Good, done.

Next up: pipelining. I saw in the documentation at https://ruby-doc.org/3.3.0/gems/net-imap/Net/IMAP.html that you can invoke multiple commands simultaneously on a single connection, by using Thread.start.

Let's try that?

Doesn't work. Although you can do multiple commands simultaneously, the selected mailbox is a property of the session rather than the command. I may have to hack it to somehow do proper pipelining.

OK, let's make EmailContacts now. So this is a table that maps an email to all of the contents that are in it. So an EmailContact has:

So if it is to me, it would have me with field="to", etc.

OK, so Rails has a cool thing for this, you make your EmailContact model, and then in Email you say:

has_many :contacts, through: :email_contacts

And then for some Email object e, you have e.contacts which is all of the associated Contact objects, and also e.email_contacts which is the associated EmailContact objects, which has the metadata from the extra fields. Super cool.

Update: you need to also explicitly add:

has_many :email_contacts

And then when I parse an email how do I populate these tables? Done.

Trying to open a Stripe link I'm getting an error from Firefox about "cross-origin opener policies"?? Never heard of that before. It means I'm missing rel="noopener" on the link. But I do have rel="noopener" on the link!

You need sandbox="allow-popups-to-escape-sandbox" on the iframe, fixed now.

Now I guess I want to delete the to and from fields on the Email model, and grab them from EmailContacts instead.

OK, seems to be working. One issue is that it now only shows one contact per email in the email view.

If I use where instead of find_by then I get all of them instead of just one.

It is now taking 700ms to render the index page, presumably because it has to do so many extra queries to grab out the contacts from different tables. How do I make it get them all in one go with a JOIN?

You can do @emails = current_user.emails.includes(:email_contacts), but it doesn't seem to be any faster. Am I missing an index?

It's because email.to uses email_contacts.where which always hits the database. I should use email_contacts.select instead.

Now it is faster, but still has to look up the email address for every email contact, so probably I want to add :contacts to the includes() list?

Yes, another improvement.

And finally it is also looking up attachments for every email, so let's include that as well. Need to include :attachments_attachments for that.

Now it's 100ms to render the index page. I think I want an index on the date field of emails.

An index on (user_id,date) is what I want, works well now, about 75ms, of which 55ms is in "Views" and 10ms in "ActiveRecord". What are my Views doing slow? Or is that just normal? Just normal according to ChatGPT.

I guess now I want to render the contacts on the email page using some sort of "component" that I can reuse across all the contacts in all the fields. It should emphasise their display name but leave the email address visible. Apparently "Rails Components" are a thing, or you can also render "partials".

Components gives you a way to add controller-like code to the component. I think a partial is enough for now.

Seems to work OK, I have managed to make it look awfully ugly though.

Auto-link doesn't put target="_blank" rel="noopener noreferer".

The Screener

OK, let's look at the feature I'm doing all of this for: The Screener. Copied from hey.com.

When a new email comes in, if it's not from a contact we have "screened in", then the email goes into The Screener instead of the inbox. The main view will still only be the inbox. If there are unseen emails in the Screener then we'll have a label at the top saying so. If we click it then we go into The Screener, which shows us unseen emails from the last 7 days from non-screened-in contacts. At the bottom we can maybe "Load more" if we want to go back further, but by default they're not shown.

For each email in The Screener we can: view it, dismiss it (mark seen), or screen the person in.

We maybe kind of want the mailbox view to be a "component" that takes a filter and sort order and shows us emails according to those.

One issue is what if an email has multiple "From" values? Consult ChatGPT on that.

What is the simplest way to start?

Good, is working.

Mark as seen

Emails should be marked as seen as soon as they are viewed, unfortunately we can't do that in the GET controller because Hotwire prefetches the page. ChatGPT suggests using a "Stimulus Controller" to POST an update to mark it as seen.

I have done what it suggested, passing in the URL to fetch with data-mark-seen-url-value and grabbing it out with this.data.get("url"), but it is coming out as null, hmm.

OK, it is just totally different, working now, I consulted https://stimulus.hotwired.dev/reference/values

By having mark_seen and mark_unseen return "204 No Content", they don't replace the page view with anything. However we do need the "Mark as unseen" button text to change to "Mark as seen" once it succeeds.

Todo

< 2025-07-22 2025-07-24 >