Implement email search
This commit is contained in:
parent
7bd547141b
commit
c644ddbe8d
@ -1,6 +1,12 @@
|
|||||||
class EmailsController < ApplicationController
|
class EmailsController < ApplicationController
|
||||||
before_action :set_email, only: [:show, :mark_as_read, :body_preview]
|
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
|
def show
|
||||||
@selected = params[:selected]
|
@selected = params[:selected]
|
||||||
@email.mark_as_read! unless @email.read?
|
@email.mark_as_read! unless @email.read?
|
||||||
|
47
app/javascript/controllers/search_controller.js
Normal file
47
app/javascript/controllers/search_controller.js
Normal file
@ -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 = [];
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,10 @@ class Email < ApplicationRecord
|
|||||||
validates :rendered_html, presence: true
|
validates :rendered_html, presence: true
|
||||||
validates :rendered_plain_text, presence: true
|
validates :rendered_plain_text, presence: true
|
||||||
|
|
||||||
|
def self.search(query)
|
||||||
|
Search::EmailSearch.new.search(self, query)
|
||||||
|
end
|
||||||
|
|
||||||
def self.unread
|
def self.unread
|
||||||
where(read_at: nil)
|
where(read_at: nil)
|
||||||
end
|
end
|
||||||
|
32
app/services/search/email_search.rb
Normal file
32
app/services/search/email_search.rb
Normal file
@ -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
|
18
app/views/emails/index.html.erb
Normal file
18
app/views/emails/index.html.erb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<% if @emails.any? %>
|
||||||
|
<div class="email email--no-chrome">
|
||||||
|
<b class="email__from">
|
||||||
|
From
|
||||||
|
</b>
|
||||||
|
<b class="email__to">To</b>
|
||||||
|
<b class="email__subject">Subject</b>
|
||||||
|
<b class="email__created-at">Received At</b>
|
||||||
|
</div>
|
||||||
|
<% @emails.each do |email| %>
|
||||||
|
<%= render(EmailListEntryComponent.new(email: email)) %>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<div class="text-center">
|
||||||
|
<p>¯\_(ツ)_/¯</p>
|
||||||
|
<p>We couldn't find any emails.</p>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
@ -1,4 +1,5 @@
|
|||||||
<section>
|
<div data-controller="search" data-search-url-value="<%= emails_path(mailbox_id: @mailbox.id) %>">
|
||||||
|
<section>
|
||||||
<header class="header header--with-border">
|
<header class="header header--with-border">
|
||||||
<div class="header--justified-header">
|
<div class="header--justified-header">
|
||||||
<h1><%= @mailbox.name %> (<%= @mailbox.unread_count %>)</h1>
|
<h1><%= @mailbox.name %> (<%= @mailbox.unread_count %>)</h1>
|
||||||
@ -27,9 +28,18 @@
|
|||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</header>
|
</header>
|
||||||
</section>
|
</section>
|
||||||
|
<div class="form-group">
|
||||||
<% if @emails.any? %>
|
<input class="form-control"
|
||||||
|
data-search-target="query"
|
||||||
|
data-action="keyup->search#search"
|
||||||
|
placeholder="Search..."
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div data-search-target="output">
|
||||||
|
<% if @emails.any? %>
|
||||||
<div class="email email--no-chrome">
|
<div class="email email--no-chrome">
|
||||||
<b class="email__from">
|
<b class="email__from">
|
||||||
From
|
From
|
||||||
@ -41,7 +51,7 @@
|
|||||||
<% @emails.each do |email| %>
|
<% @emails.each do |email| %>
|
||||||
<%= render(EmailListEntryComponent.new(email: email)) %>
|
<%= render(EmailListEntryComponent.new(email: email)) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<p>¯\_(ツ)_/¯</p>
|
<p>¯\_(ツ)_/¯</p>
|
||||||
<p>You don't have any emails yet.</p>
|
<p>You don't have any emails yet.</p>
|
||||||
@ -53,4 +63,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -7,7 +7,7 @@ Rails.application.routes.draw do
|
|||||||
post 'clear_mailbox', to: 'mailboxes#clear_mailbox'
|
post 'clear_mailbox', to: 'mailboxes#clear_mailbox'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
resources :emails, only: [:show] do
|
resources :emails, only: [:index, :show] do
|
||||||
member do
|
member do
|
||||||
get '/preview', to: 'emails#body_preview'
|
get '/preview', to: 'emails#body_preview'
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user