rails_mail 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +170 -0
- data/Rakefile +8 -0
- data/app/assets/images/rails_mail/rails-mail.png +0 -0
- data/app/channels/rails_mail/application_cable/channel.rb +6 -0
- data/app/channels/rails_mail/application_cable/connection.rb +17 -0
- data/app/channels/rails_mail/emails_channel.rb +7 -0
- data/app/controllers/rails_mail/base_controller.rb +13 -0
- data/app/controllers/rails_mail/emails_controller.rb +40 -0
- data/app/controllers/rails_mail/frontends_controller.rb +51 -0
- data/app/frontend/rails_mail/application.js +15 -0
- data/app/frontend/rails_mail/modules/auto_submit.js +30 -0
- data/app/frontend/rails_mail/modules/consumer.js +3 -0
- data/app/frontend/rails_mail/modules/email_highlight_controller.js +48 -0
- data/app/frontend/rails_mail/modules/emails_channel.js +12 -0
- data/app/frontend/rails_mail/modules/timeago.js +41 -0
- data/app/frontend/rails_mail/style.css +0 -0
- data/app/frontend/rails_mail/vendor/action_cable.js +512 -0
- data/app/frontend/rails_mail/vendor/date-fns.js +8 -0
- data/app/frontend/rails_mail/vendor/stimulus.js +2408 -0
- data/app/frontend/rails_mail/vendor/tailwind/tailwind.min.js +83 -0
- data/app/frontend/rails_mail/vendor/turbo.js +6697 -0
- data/app/helpers/rails_mail/emails_helper.rb +4 -0
- data/app/helpers/rails_mail/turbo_helper.rb +41 -0
- data/app/jobs/rails_mail/trim_emails_job.rb +40 -0
- data/app/models/rails_mail/email.rb +45 -0
- data/app/views/layouts/rails_mail/application.html.erb +72 -0
- data/app/views/rails_mail/emails/_email.html.erb +7 -0
- data/app/views/rails_mail/emails/_empty_state.html.erb +5 -0
- data/app/views/rails_mail/emails/_form.html.erb +22 -0
- data/app/views/rails_mail/emails/_show.html.erb +50 -0
- data/app/views/rails_mail/emails/edit.html.erb +12 -0
- data/app/views/rails_mail/emails/index.html.erb +16 -0
- data/app/views/rails_mail/emails/index.turbo_stream.erb +7 -0
- data/app/views/rails_mail/emails/new.html.erb +11 -0
- data/app/views/rails_mail/emails/show.html.erb +43 -0
- data/app/views/rails_mail/shared/_email.html.erb +25 -0
- data/app/views/rails_mail/shared/_email_sidebar.html.erb +9 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20250118201227_create_rails_mail_emails.rb +9 -0
- data/lib/generators/rails_mail/install/install_generator.rb +15 -0
- data/lib/generators/rails_mail/install/templates/initializer.rb +23 -0
- data/lib/rails_mail/configuration.rb +35 -0
- data/lib/rails_mail/delivery_method.rb +22 -0
- data/lib/rails_mail/engine.rb +11 -0
- data/lib/rails_mail/version.rb +3 -0
- data/lib/rails_mail.rb +40 -0
- data/lib/tasks/rails_mail_tasks.rake +10 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f203c591ad81cc5cc52b5aa4b9f88f7ba4cb54bda733f7f67650229cf5e4dc73
|
4
|
+
data.tar.gz: 6522e79faa67890b74d33a1a0529085627dbd0630d028b461e6af78143e80be5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 385b93bbbed99ad5f1909ddc87b3fdfccc7e4563d810c912bff197a9780abd62c07bbec4d426a30a3ce7967a5f5894e85b3236018d5e9ebafb9a065b28a38e90
|
7
|
+
data.tar.gz: 0da762085610c6dc626763b1610133fd826e915e15a20d329c924bc301763f969b0f6457b470fab597953ff368dbb964ff044f82873487e6250b03c90031148b
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright Peter Philips
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
# RailsMail
|
2
|
+
RailsMail is a Rails engine that provides a database-backed delivery method for Action Mailer, primarily intended for local development and staging environments. It captures emails sent through your Rails application and provides a web interface to view them.
|
3
|
+
|
4
|
+
RailsMail saves all outgoing emails to your database instead of actually sending them. This is particularly useful for:
|
5
|
+
- Local development to inspect emails without setting up a real mail server
|
6
|
+
- Staging environments where you want to prevent actual email delivery
|
7
|
+
- Testing email templates and layouts
|
8
|
+
|
9
|
+

|
10
|
+
|
11
|
+
### Features
|
12
|
+
* Implements delivery_method for ActionMailer to catch emails and store them in the database
|
13
|
+
* Real-time updates using Turbo and ActionCable
|
14
|
+
* Search functionality across email fields (subject, from, to, cc, bcc)
|
15
|
+
* Clean, responsive UI for viewing email contents
|
16
|
+
* Optional authentication support
|
17
|
+
* Trimming emails older than a specified duration or a maximum number of emails
|
18
|
+
* Ability to manually clear emails out and turn on/off that functionality based on environment (eg, so that in Staging, other stakeholders can't clear emails out, but in dev sometimes you want a clean slate)
|
19
|
+
* Dynamic time ago in words using date-fns
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
To install RailsMail, follow these steps:
|
24
|
+
|
25
|
+
1. **Add the gem to your application's Gemfile:**
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
gem "rails_mail"
|
29
|
+
```
|
30
|
+
|
31
|
+
2. **Run `bundle install` to install the gem:**
|
32
|
+
|
33
|
+
```bash
|
34
|
+
$ bundle install
|
35
|
+
```
|
36
|
+
|
37
|
+
3. **Generate the necessary database migration:**
|
38
|
+
|
39
|
+
Run the following command to create the migration for storing emails:
|
40
|
+
|
41
|
+
```bash
|
42
|
+
$ rake rails_mail:install:migrations
|
43
|
+
```
|
44
|
+
|
45
|
+
4. **Run the migration:**
|
46
|
+
|
47
|
+
Apply the migration to your database:
|
48
|
+
|
49
|
+
```bash
|
50
|
+
$ rails db:migrate
|
51
|
+
```
|
52
|
+
|
53
|
+
5. **Start your Rails server and access the RailsMail interface:**
|
54
|
+
|
55
|
+
Visit `http://localhost:3000/rails_mail` to view captured emails.
|
56
|
+
|
57
|
+
|
58
|
+
## Usage
|
59
|
+
|
60
|
+
To use RailsMail in your application:
|
61
|
+
|
62
|
+
1. **Configure Action Mailer to use RailsMail as the delivery method:**
|
63
|
+
|
64
|
+
Add the following configuration to your `config/environments/development.rb` (or `staging.rb`):
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
config.action_mailer.delivery_method = :rails_mail
|
68
|
+
```
|
69
|
+
|
70
|
+
2. **Mount the engine in your routes:**
|
71
|
+
|
72
|
+
Add the following line to your `config/routes.rb`:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
mount RailsMail::Engine => "/rails_mail"
|
76
|
+
```
|
77
|
+
|
78
|
+
3. **Configure the initializer**
|
79
|
+
See the [Configuration](#configuration) section for more details.
|
80
|
+
|
81
|
+
4. **Visit `/rails_mail` in your browser to view all captured emails.**
|
82
|
+
|
83
|
+
### Configuration
|
84
|
+
|
85
|
+
RailsMail can be configured through an initializer:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
# config/initializers/rails_mail.rb
|
89
|
+
RailsMail.configure do |config|
|
90
|
+
# Optional authentication callback
|
91
|
+
# (if using Authlogic. If using Devise see the Authentication section)
|
92
|
+
config.authentication_callback do
|
93
|
+
user_session = UserSession.find
|
94
|
+
raise ActionController::RoutingError.new('Not Found') unless user_session&.user&.admin?
|
95
|
+
end
|
96
|
+
|
97
|
+
# Delete emails older than the specified duration
|
98
|
+
config.trim_emails_older_than = 30.days
|
99
|
+
|
100
|
+
# Keep only the most recent N emails
|
101
|
+
config.trim_emails_max_count = 1000
|
102
|
+
|
103
|
+
# Control whether trimming runs synchronously (:now) or asynchronously (:later)
|
104
|
+
config.sync_via = :later
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
### Configuration Options
|
109
|
+
|
110
|
+
- `authentication_callback`: A block that will be called before accessing RailsMail routes
|
111
|
+
- `trim_emails_older_than`: Accepts an ActiveSupport::Duration object (e.g., 30.days). Emails older than this duration will be deleted.
|
112
|
+
- `trim_emails_max_count`: Keeps only the N most recent emails, deleting older ones.
|
113
|
+
- `sync_via`: Controls whether the trimming job runs synchronously (:now) or asynchronously (:later)
|
114
|
+
|
115
|
+
## Authentication
|
116
|
+
Authentication is optional, but recommended and will depend on your application's authentication setup. This gem provides an `authentication_callback` that you can configure in the initializer which is helpful for Authlogic. If you are using Devise, you can simply wrap the mount point of the engine.
|
117
|
+
|
118
|
+
### Authlogic
|
119
|
+
|
120
|
+
If you're using Authlogic, configure the authentication in the initializer:
|
121
|
+
```ruby
|
122
|
+
# config/initializers/rails_mail.rb
|
123
|
+
RailsMail.configure do |config|
|
124
|
+
config.authentication_callback do
|
125
|
+
user_session = UserSession.find
|
126
|
+
# Provide a more helpful message in development
|
127
|
+
msg = Rails.env.development? ? 'Forbidden - make sure you have the correct permission in config/initializers/rails_mail.rb' : 'Not Found'
|
128
|
+
raise ActionController::RoutingError.new(msg) unless user_session&.user&.admin?
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
### Devise
|
135
|
+
|
136
|
+
If you're using Devise, you can simply wrap the mount point of the engine using Devise's `authenticate` route helper.
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
# config/routes.rb
|
140
|
+
authenticate :user, ->(user) { user.admin? } do
|
141
|
+
mount RailsMail::Engine => "/rails_mail"
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
## Real-time updates
|
146
|
+
|
147
|
+
RailsMail uses Turbo, TurboStreams, and ActionCable to provide real-time updates in the ui when emails are delivered. When you send an email, the new email will be displayed in the list of emails. There may be gotchas depending on your setup and environment.
|
148
|
+
|
149
|
+
## Gotchas
|
150
|
+
|
151
|
+
- In development environment, the typical default for ActionCable (cable.yml) is to use the async adapter which is an in-memory adapter. If you try to send an email from the rails console, it will not auto-update the ui. You can change the adapter to the development adapter by running `cable.yml` to use something like the redis, postgresql adapter, or solidcable.
|
152
|
+
- In staging environments, the same idea typically applies that you need to use a multi-process adapter like redis, postgresql, or solidcable.
|
153
|
+
|
154
|
+
## Future work / ideas
|
155
|
+
|
156
|
+
- Implement infinite scroll rather than loading all emails at once
|
157
|
+
- Implement adapters to support real-time updates without ActionCable (polling or SSE)
|
158
|
+
- Implement attachments support
|
159
|
+
- Implement introspection of application notifiers and allow manual delivery/inspection of emails
|
160
|
+
- Need to introspect the arguments of the notifier and see if the arguments can be paired with active record models or to allow a mapping of argument type to sample data / fixtures.
|
161
|
+
- Implement read/unread functionality
|
162
|
+
- Implement individual email delete
|
163
|
+
- Implement multi-part (text/html) email support
|
164
|
+
|
165
|
+
## Contributing
|
166
|
+
Contribution directions go here.
|
167
|
+
|
168
|
+
## License
|
169
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
170
|
+
|
data/Rakefile
ADDED
Binary file
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RailsMail
|
2
|
+
module ApplicationCable
|
3
|
+
class Connection < ActionCable::Connection::Base
|
4
|
+
# identified_by :current_user
|
5
|
+
|
6
|
+
def connect
|
7
|
+
# self.current_user = find_verified_user
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def find_verified_user
|
13
|
+
# TODO: Implement user verification
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module RailsMail
|
2
|
+
class BaseController < ActionController::Base
|
3
|
+
layout "rails_mail/application"
|
4
|
+
helper TurboHelper if defined?(TurboHelper)
|
5
|
+
before_action :authenticate!
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def authenticate!
|
10
|
+
instance_eval(&RailsMail.authentication_callback) if RailsMail.authentication_callback.kind_of?(Proc)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module RailsMail
|
2
|
+
class EmailsController < BaseController
|
3
|
+
# GET /emails
|
4
|
+
def index
|
5
|
+
@emails = Email.all
|
6
|
+
@emails = @emails.search(params[:q]) if params[:q].present?
|
7
|
+
@emails = @emails.order(created_at: :desc)
|
8
|
+
@email = params[:id] ? Email.find(params[:id]) : Email.last
|
9
|
+
|
10
|
+
respond_to do |format|
|
11
|
+
format.html
|
12
|
+
format.turbo_stream if params.key?(:q)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# GET /emails/1
|
17
|
+
def show
|
18
|
+
@emails = Email.order(created_at: :desc)
|
19
|
+
@email = Email.find(params[:id])
|
20
|
+
if request.headers["Turbo-Frame"]
|
21
|
+
render partial: "rails_mail/emails/show", locals: { email: @email }
|
22
|
+
else
|
23
|
+
render :index
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def destroy_all
|
28
|
+
# Email.destroy_all
|
29
|
+
respond_to do |format|
|
30
|
+
format.html { redirect_to emails_path }
|
31
|
+
format.turbo_stream {
|
32
|
+
render turbo_stream: [
|
33
|
+
turbo_stream.update("email-sidebar", ""),
|
34
|
+
turbo_stream.update("email_content", partial: "empty_state")
|
35
|
+
]
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module RailsMail
|
2
|
+
class FrontendsController < ActionController::Base # rubocop:disable Rails/ApplicationController
|
3
|
+
STATIC_ASSETS = {
|
4
|
+
css: {
|
5
|
+
# tailwind: RailsMail::Engine.root.join("app", "frontend", "rails_mail", "vendor", "tailwind", "tailwind.min.css"),
|
6
|
+
style: RailsMail::Engine.root.join("app", "frontend", "rails_mail", "style.css")
|
7
|
+
},
|
8
|
+
js: {
|
9
|
+
tailwind: RailsMail::Engine.root.join("app", "frontend", "rails_mail", "vendor", "tailwind", "tailwind.min.js")
|
10
|
+
}
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
# Additional JS modules that don't live in app/frontend/rails_mail/modules
|
14
|
+
MODULE_OVERRIDES = {
|
15
|
+
application: RailsMail::Engine.root.join("app", "frontend", "rails_mail", "application.js"),
|
16
|
+
stimulus: RailsMail::Engine.root.join("app", "frontend", "rails_mail", "vendor", "stimulus.js"),
|
17
|
+
turbo: RailsMail::Engine.root.join("app", "frontend", "rails_mail", "vendor", "turbo.js"),
|
18
|
+
action_cable: RailsMail::Engine.root.join("app", "frontend", "rails_mail", "vendor", "action_cable.js"),
|
19
|
+
"date-fns": RailsMail::Engine.root.join("app", "frontend", "rails_mail", "vendor", "date-fns.js")
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
def self.js_modules
|
23
|
+
if Rails.env.production?
|
24
|
+
@_js_modules ||= load_js_modules
|
25
|
+
else
|
26
|
+
load_js_modules
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.load_js_modules
|
31
|
+
RailsMail::Engine.root.join("app", "frontend", "rails_mail", "modules").children.select(&:file?).each_with_object({}) do |file, modules|
|
32
|
+
key = File.basename(file.basename.to_s, ".js").to_sym
|
33
|
+
modules[key] = file
|
34
|
+
end.merge(MODULE_OVERRIDES)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Necessarly to serve Javascript to the browser
|
38
|
+
skip_after_action :verify_same_origin_request, raise: false
|
39
|
+
before_action { expires_in 1.year, public: true }
|
40
|
+
|
41
|
+
def static
|
42
|
+
render file: STATIC_ASSETS.dig(params[:format].to_sym, params[:name].to_sym) || raise(ActionController::RoutingError, "Not Found")
|
43
|
+
end
|
44
|
+
|
45
|
+
def module
|
46
|
+
raise(ActionController::RoutingError, "Not Found") if params[:format] != "js"
|
47
|
+
|
48
|
+
render file: self.class.js_modules[params[:name].to_sym] || raise(ActionController::RoutingError, "Not Found")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { Application } from "stimulus";
|
2
|
+
import "turbo";
|
3
|
+
import EmailHighlightController from "email_highlight_controller";
|
4
|
+
import AutoSubmit from 'auto_submit'
|
5
|
+
import Timeago from 'timeago'
|
6
|
+
|
7
|
+
const application = Application.start();
|
8
|
+
|
9
|
+
application.register("email-highlight", EmailHighlightController);
|
10
|
+
application.register('auto-submit', AutoSubmit)
|
11
|
+
application.register('timeago', Timeago)
|
12
|
+
|
13
|
+
window.Stimulus = Application.start();
|
14
|
+
|
15
|
+
import "emails_channel";
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { Controller } from "stimulus";
|
2
|
+
function debounce(callback, delay) {
|
3
|
+
let timeout;
|
4
|
+
return (...args) => {
|
5
|
+
clearTimeout(timeout), timeout = setTimeout(() => {
|
6
|
+
callback.apply(this, args);
|
7
|
+
}, delay);
|
8
|
+
};
|
9
|
+
}
|
10
|
+
const _AutoSubmit = class _AutoSubmit extends Controller {
|
11
|
+
initialize() {
|
12
|
+
this.submit = this.submit.bind(this);
|
13
|
+
}
|
14
|
+
connect() {
|
15
|
+
this.delayValue > 0 && (this.submit = debounce(this.submit, this.delayValue));
|
16
|
+
}
|
17
|
+
submit() {
|
18
|
+
this.element.requestSubmit();
|
19
|
+
}
|
20
|
+
};
|
21
|
+
_AutoSubmit.values = {
|
22
|
+
delay: {
|
23
|
+
type: Number,
|
24
|
+
default: 150
|
25
|
+
}
|
26
|
+
};
|
27
|
+
let AutoSubmit = _AutoSubmit;
|
28
|
+
export {
|
29
|
+
AutoSubmit as default
|
30
|
+
};
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import { Controller } from "stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["link"]
|
5
|
+
|
6
|
+
// Define the Tailwind class as a static property
|
7
|
+
static activeClass = "bg-gray-100"
|
8
|
+
|
9
|
+
connect() {
|
10
|
+
console.log("EmailHighlightController connected")
|
11
|
+
// Listen for Turbo frame load events
|
12
|
+
document.addEventListener("turbo:frame-load", this.highlightCurrentEmail.bind(this))
|
13
|
+
}
|
14
|
+
|
15
|
+
disconnect() {
|
16
|
+
// Clean up event listener when the controller is disconnected
|
17
|
+
document.removeEventListener("turbo:frame-load", this.highlightCurrentEmail.bind(this))
|
18
|
+
}
|
19
|
+
|
20
|
+
// Called when a link is clicked
|
21
|
+
highlight(event) {
|
22
|
+
// Remove active class from all links
|
23
|
+
this.linkTargets.forEach(link => {
|
24
|
+
link.classList.remove(this.constructor.activeClass)
|
25
|
+
})
|
26
|
+
|
27
|
+
// Add active class to clicked link
|
28
|
+
const clickedLink = event.currentTarget
|
29
|
+
clickedLink.classList.add(this.constructor.activeClass)
|
30
|
+
}
|
31
|
+
|
32
|
+
highlightCurrentEmail() {
|
33
|
+
// Get the current email ID from the content frame
|
34
|
+
const emailContent = document.querySelector("#email_content [data-email-id]")
|
35
|
+
if (emailContent) {
|
36
|
+
const currentEmailId = emailContent.dataset.emailId
|
37
|
+
|
38
|
+
// Highlight the link with the matching email ID
|
39
|
+
this.linkTargets.forEach(link => {
|
40
|
+
if (link.dataset.emailId === currentEmailId) {
|
41
|
+
link.classList.add(this.constructor.activeClass)
|
42
|
+
} else {
|
43
|
+
link.classList.remove(this.constructor.activeClass)
|
44
|
+
}
|
45
|
+
})
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import consumer from "consumer" // Your Action Cable consumer setup
|
2
|
+
import { renderStreamMessage } from "turbo" // If you've got turbo.js available
|
3
|
+
|
4
|
+
consumer.subscriptions.create({
|
5
|
+
channel: "RailsMail::EmailsChannel"
|
6
|
+
}, {
|
7
|
+
received(data) {
|
8
|
+
console.log("received data on Emails Channel", data)
|
9
|
+
// data should be a string containing <turbo-stream> tags
|
10
|
+
renderStreamMessage(data)
|
11
|
+
}
|
12
|
+
})
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import { Controller } from "stimulus";
|
2
|
+
import { formatDistanceToNow } from "date-fns";
|
3
|
+
const _Timeago = class _Timeago extends Controller {
|
4
|
+
initialize() {
|
5
|
+
this.isValid = !0;
|
6
|
+
}
|
7
|
+
connect() {
|
8
|
+
this.load(), this.hasRefreshIntervalValue && this.isValid && this.startRefreshing();
|
9
|
+
}
|
10
|
+
disconnect() {
|
11
|
+
this.stopRefreshing();
|
12
|
+
}
|
13
|
+
load() {
|
14
|
+
const datetime = this.datetimeValue, date = Date.parse(datetime), options = {
|
15
|
+
includeSeconds: this.includeSecondsValue,
|
16
|
+
addSuffix: this.addSuffixValue,
|
17
|
+
locale: this.locale
|
18
|
+
};
|
19
|
+
Number.isNaN(date) && (this.isValid = !1, console.error(
|
20
|
+
`[@stimulus-components/timeago] Value given in 'data-timeago-datetime' is not a valid date (${datetime}). Please provide a ISO 8601 compatible datetime string. Displaying given value instead.`
|
21
|
+
)), this.element.dateTime = datetime, this.element.innerHTML = this.isValid ? formatDistanceToNow(date, options) : datetime;
|
22
|
+
}
|
23
|
+
startRefreshing() {
|
24
|
+
this.refreshTimer = setInterval(() => {
|
25
|
+
this.load();
|
26
|
+
}, this.refreshIntervalValue);
|
27
|
+
}
|
28
|
+
stopRefreshing() {
|
29
|
+
this.refreshTimer && clearInterval(this.refreshTimer);
|
30
|
+
}
|
31
|
+
};
|
32
|
+
_Timeago.values = {
|
33
|
+
datetime: String,
|
34
|
+
refreshInterval: Number,
|
35
|
+
includeSeconds: Boolean,
|
36
|
+
addSuffix: Boolean
|
37
|
+
};
|
38
|
+
let Timeago = _Timeago;
|
39
|
+
export {
|
40
|
+
Timeago as default
|
41
|
+
};
|
File without changes
|