diff --git a/app/controllers/emails_controller.rb b/app/controllers/emails_controller.rb index 55484de..b960f6b 100644 --- a/app/controllers/emails_controller.rb +++ b/app/controllers/emails_controller.rb @@ -1,6 +1,12 @@ class EmailsController < ApplicationController before_action :set_email, only: [:show, :mark_as_read, :body_preview] + def index + @emails = Email.where(mailbox_id: params[:mailbox_id]) + @emails = @emails.search(params[:q]) if params[:q].present? + render(layout: false) + end + def show @selected = params[:selected] @email.mark_as_read! unless @email.read? diff --git a/app/javascript/controllers/search_controller.js b/app/javascript/controllers/search_controller.js new file mode 100644 index 0000000..1ac5d68 --- /dev/null +++ b/app/javascript/controllers/search_controller.js @@ -0,0 +1,47 @@ +import { Controller } from "stimulus" + +export default class extends Controller { + static targets = [ "query", "output" ] + static values = { url: String } + + connect() { + this.requests = []; + } + + search() { + this.resetTimeout(); + this.cancelPreviousRequests(); + this.searchTimeout = setTimeout(() => { + + let abortController = new AbortController(); + this.requests.push(abortController) + + let signal = abortController.signal; + let url = this.urlValue + "&q=" + this.queryTarget.value; + + fetch(url, { signal }) + .then((response) => { + return response.text(); + }) + .then((html) => { + this.outputTarget.innerHTML = html; + }) + .catch(e => { + console.log(e) + }) + }, 75) + } + + resetTimeout() { + if(this.searchTimeout) { + clearTimeout(this.searchTimeout); + } + } + + cancelPreviousRequests() { + this.requests.forEach(request => { + request.abort(); + }); + this.requests = []; + } +} diff --git a/app/models/email.rb b/app/models/email.rb index 4e37377..1b88563 100644 --- a/app/models/email.rb +++ b/app/models/email.rb @@ -9,6 +9,10 @@ class Email < ApplicationRecord validates :rendered_html, presence: true validates :rendered_plain_text, presence: true + def self.search(query) + Search::EmailSearch.new.search(self, query) + end + def self.unread where(read_at: nil) end diff --git a/app/services/search/email_search.rb b/app/services/search/email_search.rb new file mode 100644 index 0000000..15d3958 --- /dev/null +++ b/app/services/search/email_search.rb @@ -0,0 +1,32 @@ +module Search + class EmailSearch + def call(collection, search_query) + collection.where(where_statement, + search_query_parts: search_query_parts(search_query)) + end + + alias search call + + private + + def search_query_parts(search_query) + return [] if search_query.blank? + search_query.split(/\s+/) + .map { |query| "%#{query}%" } + end + + def where_statement + <<~SQL + request_payload->'personalizations'->>personalization_id + ILIKE + ANY(ARRAY[:search_query_parts]) + OR + subject ILIKE ANY(ARRAY[:search_query_parts]) + OR + template_id ILIKE ANY(ARRAY[:search_query_parts]) + OR + rendered_plain_text ILIKE ANY(ARRAY[:search_query_parts]) + SQL + end + end +end diff --git a/app/views/emails/index.html.erb b/app/views/emails/index.html.erb new file mode 100644 index 0000000..c3517a6 --- /dev/null +++ b/app/views/emails/index.html.erb @@ -0,0 +1,18 @@ +<% if @emails.any? %> +
¯\_(ツ)_/¯
+We couldn't find any emails.
+- <%= link_to '🛈'.html_safe, - details_mailbox_path(@mailbox), - class: "button", - data: { action: 'click->remote-content#load'} %> +
+ <%= link_to '🛈'.html_safe, + details_mailbox_path(@mailbox), + class: "button", + data: { action: 'click->remote-content#load'} %> +
+ +¯\_(ツ)_/¯
-You don't have any emails yet.
-Use this api token to send emails with sendgrid
- -<% end %> +