mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 17:21:38 -05:00
Add raw implementation of notifications interactive channel
This commit is contained in:
parent
34c12a9536
commit
14b7397840
12 changed files with 125 additions and 1 deletions
|
|
@ -1,4 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module ApplicationCable
|
module ApplicationCable
|
||||||
class Connection < ActionCable::Connection::Base
|
class Connection < ActionCable::Connection::Base
|
||||||
|
identified_by :current_user
|
||||||
|
|
||||||
|
def connect
|
||||||
|
self.current_user = find_verified_user
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_verified_user
|
||||||
|
if (verified_user = env['warden'].user)
|
||||||
|
verified_user
|
||||||
|
else
|
||||||
|
reject_unauthorized_connection
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
7
app/channels/notifications_channel.rb
Normal file
7
app/channels/notifications_channel.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class NotificationsChannel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
stream_for current_user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -8,3 +8,4 @@ import "leaflet"
|
||||||
import "leaflet-providers"
|
import "leaflet-providers"
|
||||||
import "chartkick"
|
import "chartkick"
|
||||||
import "Chart.bundle"
|
import "Chart.bundle"
|
||||||
|
import "./channels"
|
||||||
|
|
|
||||||
6
app/javascript/channels/consumer.js
Normal file
6
app/javascript/channels/consumer.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Action Cable provides the framework to deal with WebSockets in Rails.
|
||||||
|
// You can generate new channels where WebSocket features live using the `bin/rails generate channel` command.
|
||||||
|
|
||||||
|
import { createConsumer } from "@rails/actioncable"
|
||||||
|
|
||||||
|
export default createConsumer()
|
||||||
2
app/javascript/channels/index.js
Normal file
2
app/javascript/channels/index.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Import all the channels to be used by Action Cable
|
||||||
|
import "notifications_channel"
|
||||||
15
app/javascript/channels/notifications_channel.js
Normal file
15
app/javascript/channels/notifications_channel.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import consumer from "./consumer"
|
||||||
|
|
||||||
|
consumer.subscriptions.create("NotificationsChannel", {
|
||||||
|
connected() {
|
||||||
|
console.log("Connected to the notifications channel!");
|
||||||
|
},
|
||||||
|
|
||||||
|
disconnected() {
|
||||||
|
// Called when the subscription has been terminated by the server
|
||||||
|
},
|
||||||
|
|
||||||
|
received(data) {
|
||||||
|
// Called when there's incoming data on the websocket for this channel
|
||||||
|
}
|
||||||
|
});
|
||||||
47
app/javascript/controllers/notifications_controller.js
Normal file
47
app/javascript/controllers/notifications_controller.js
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
import consumer from "../channels/consumer"
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ["container"]
|
||||||
|
static values = { userId: Number }
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
console.log("Controller connecting...")
|
||||||
|
// Ensure we clean up any existing subscription
|
||||||
|
if (this.subscription) {
|
||||||
|
console.log("Cleaning up existing subscription")
|
||||||
|
this.subscription.unsubscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.subscription = consumer.subscriptions.create("NotificationsChannel", {
|
||||||
|
connected: () => {
|
||||||
|
console.log("Connected to NotificationsChannel", this.subscription)
|
||||||
|
},
|
||||||
|
disconnected: () => {
|
||||||
|
console.log("Disconnected from NotificationsChannel")
|
||||||
|
},
|
||||||
|
received: (data) => {
|
||||||
|
console.log("Received notification:", data, "Subscription:", this.subscription)
|
||||||
|
this.displayNotification(data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
console.log("Controller disconnecting...")
|
||||||
|
if (this.subscription) {
|
||||||
|
this.subscription.unsubscribe()
|
||||||
|
this.subscription = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayNotification(data) {
|
||||||
|
console.log("Notification received:", data) // For debugging
|
||||||
|
const notification = document.createElement("div")
|
||||||
|
notification.classList.add("notification", `notification-${data.kind}`)
|
||||||
|
notification.innerHTML = `<strong>${data.title}</strong>: ${data.content}`
|
||||||
|
|
||||||
|
this.containerTarget.appendChild(notification)
|
||||||
|
setTimeout(() => notification.remove(), 5000) // Auto-hide after 5 seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Notification < ApplicationRecord
|
class Notification < ApplicationRecord
|
||||||
|
after_create_commit :broadcast_notification
|
||||||
|
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
validates :title, :content, :kind, presence: true
|
validates :title, :content, :kind, presence: true
|
||||||
|
|
@ -12,4 +14,18 @@ class Notification < ApplicationRecord
|
||||||
def read?
|
def read?
|
||||||
read_at.present?
|
read_at.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def broadcast_notification
|
||||||
|
Rails.logger.debug "Broadcasting notification to #{user.id}"
|
||||||
|
NotificationsChannel.broadcast_to(
|
||||||
|
user,
|
||||||
|
{
|
||||||
|
title: title,
|
||||||
|
content: content,
|
||||||
|
kind: kind
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<title><%= full_title(yield(:title)) %></title>
|
<title><%= full_title(yield(:title)) %></title>
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<%= action_cable_meta_tag %>
|
||||||
<%= csrf_meta_tags %>
|
<%= csrf_meta_tags %>
|
||||||
<%= csp_meta_tag %>
|
<%= csp_meta_tag %>
|
||||||
|
|
||||||
|
|
@ -20,6 +21,9 @@
|
||||||
<div class='container mx-auto'>
|
<div class='container mx-auto'>
|
||||||
<%= render 'shared/navbar' %>
|
<%= render 'shared/navbar' %>
|
||||||
<%= render 'shared/flash' %>
|
<%= render 'shared/flash' %>
|
||||||
|
<div data-controller="notifications" data-notifications-user-id-value="<%= current_user.id %>">
|
||||||
|
<div data-notifications-target="container" class="notifications-container"></div>
|
||||||
|
</div>
|
||||||
<div class="flex flex-row gap-5 w-full px-5">
|
<div class="flex flex-row gap-5 w-full px-5">
|
||||||
<%= yield %>
|
<%= yield %>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,7 @@ pin 'leaflet-providers' # @2.0.0
|
||||||
pin 'chartkick', to: 'chartkick.js'
|
pin 'chartkick', to: 'chartkick.js'
|
||||||
pin 'Chart.bundle', to: 'Chart.bundle.js'
|
pin 'Chart.bundle', to: 'Chart.bundle.js'
|
||||||
pin 'leaflet.heat' # @0.2.0
|
pin 'leaflet.heat' # @0.2.0
|
||||||
pin "leaflet-draw" # @1.0.4
|
pin 'leaflet-draw' # @1.0.4
|
||||||
|
pin '@rails/actioncable', to: 'actioncable.esm.js'
|
||||||
|
pin_all_from 'app/javascript/channels', under: 'channels'
|
||||||
|
pin 'notifications_channel', to: 'channels/notifications_channel.js'
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
require 'sidekiq/web'
|
require 'sidekiq/web'
|
||||||
|
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
|
mount ActionCable.server => '/cable'
|
||||||
mount Rswag::Api::Engine => '/api-docs'
|
mount Rswag::Api::Engine => '/api-docs'
|
||||||
mount Rswag::Ui::Engine => '/api-docs'
|
mount Rswag::Ui::Engine => '/api-docs'
|
||||||
authenticate :user, ->(u) { u.admin? } do
|
authenticate :user, ->(u) { u.admin? } do
|
||||||
|
|
|
||||||
5
spec/channels/notifications_channel_spec.rb
Normal file
5
spec/channels/notifications_channel_spec.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe NotificationsChannel, type: :channel do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
Loading…
Reference in a new issue