Skip to content

Commit

Permalink
Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
miharekar committed Dec 28, 2024
1 parent 2258f48 commit ea5d7a0
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 93 deletions.
6 changes: 3 additions & 3 deletions app/controllers/push_subscriptions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ class PushSubscriptionsController < ApplicationController
def create
subscription = Current.user.push_subscriptions.find_by(push_subscription_params)
if subscription
subscription.touch # rubocop:disable Rails/SkipsModelValidations
subscription.update!(user_agent: request.user_agent)
else
Current.user.push_subscriptions.create!(push_subscription_params)
Current.user.push_subscriptions.create!(push_subscription_params.merge(user_agent: request.user_agent))
end

head :ok
Expand All @@ -13,6 +13,6 @@ def create
private

def push_subscription_params
params.require(:push_subscription).permit(:endpoint, :p256dh_key, :auth_key).merge(user_agent: request.user_agent)
params.require(:push_subscription).permit(:endpoint, :p256dh_key, :auth_key)
end
end
106 changes: 30 additions & 76 deletions app/javascript/controllers/push_controller.js
Original file line number Diff line number Diff line change
@@ -1,80 +1,41 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
connect() {
// Check if the browser supports notifications
if ("Notification" in window) {
// Request permission from the user to send notifications
Notification.requestPermission().then((permission) => {
if (permission === "granted") {
// If permission is granted, register the service worker
this.registerServiceWorker()
console.log("Service worker registered")
} else if (permission === "denied") {
console.warn("User rejected to allow notifications.")
} else {
console.warn("User still didn't give an answer about notifications.")
}
})
} else {
console.warn("Push notifications not supported.")
}
console.log("Push controller connected")
}
static values = { vapidKey: String }

registerServiceWorker() {
// Check if the browser supports service workers
if ("serviceWorker" in navigator) {
// Register the service worker script (service_worker.js)
navigator.serviceWorker
.register('service-worker.js')
.then((serviceWorkerRegistration) => {
// Check if a subscription to push notifications already exists
serviceWorkerRegistration.pushManager
.getSubscription()
.then((existingSubscription) => {
if (!existingSubscription) {
// Convert the base64 VAPID key to Uint8Array
const vapidPublicKey = document.querySelector('meta[name="vapid-public-key"]').content
const convertedVapidKey = this.urlBase64ToUint8Array(vapidPublicKey)
connect() {
if (!("Notification" in window)) return

// If no subscription exists, subscribe to push notifications
serviceWorkerRegistration.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey: convertedVapidKey // Use the converted key
})
.then((subscription) => {
// Save the subscription on the server
this.saveSubscription(subscription)
})
}
})
})
.catch((error) => {
console.error("Error during registration Service Worker:", error)
})
}
Notification.requestPermission().then((permission) => {
if (permission !== "granted") return
this.registerServiceWorker()
})
}

// Add this helper method to convert the VAPID key
urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4)
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/')

const rawData = window.atob(base64)
const outputArray = new Uint8Array(rawData.length)

for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
registerServiceWorker() {
if (!("serviceWorker" in navigator)) return

navigator.serviceWorker
.register('service-worker.js')
.then((serviceWorkerRegistration) => {
serviceWorkerRegistration.pushManager
.getSubscription()
.then((existingSubscription) => {
if (existingSubscription) return

serviceWorkerRegistration.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey: Uint8Array.from(atob(this.vapidKeyValue), c => c.codePointAt(0))
})
.then((subscription) => {
this.saveSubscription(subscription)
})
})
})
}

saveSubscription(subscription) {
// Extract necessary subscription data
const endpoint = subscription.endpoint
const p256dh = btoa(
String.fromCharCode.apply(
Expand All @@ -89,20 +50,13 @@ export default class extends Controller {
)
)

// Send the subscription data to the server
fetch("/push_subscriptions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({
push_subscription: {
endpoint,
p256dh_key: p256dh,
auth_key: auth
}
})
}).catch(error => console.error("Failed to save subscription:", error))
body: JSON.stringify({ push_subscription: { endpoint, p256dh_key: p256dh, auth_key: auth } })
})
}
}
11 changes: 4 additions & 7 deletions app/jobs/web_push_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@ class WebPushJob < ApplicationJob

queue_as :default

def perform(push_subscription, title:, body:, url: "/")
def perform(push_subscription, title:, body:, path: "/")
WebPush.payload_send(
message: {
title:,
body:,
icon: ICON_PATH,
data: {url:}
}.to_json,
message: {title:, body:, icon: ICON_PATH, data: {path:}}.to_json,
endpoint: push_subscription.endpoint,
p256dh: push_subscription.p256dh_key,
auth: push_subscription.auth_key,
Expand All @@ -21,5 +16,7 @@ def perform(push_subscription, title:, body:, url: "/")
private_key: Rails.application.credentials.webpush.private_key
}
)
rescue WebPush::ExpiredSubscription
push_subscription.destroy
end
end
7 changes: 7 additions & 0 deletions app/models/shot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class Shot < ApplicationRecord
validate :daily_limit, on: :create

broadcasts_to ->(shot) { [shot.user, :shots] }, inserts_by: :prepend
after_create_commit :send_web_push_notification

def self.from_file(user, file_content)
return Shot.new(user:) if file_content.blank?
Expand Down Expand Up @@ -82,6 +83,12 @@ def daily_limit

errors.add(:base, :over_daily_limit, message: "You've reached your daily limit of #{DAILY_LIMIT} shots. Please consider upgrading to a premium account.")
end

def send_web_push_notification
user&.push_subscriptions&.each do |push_subscription|
WebPushJob.perform_later(push_subscription, title: "New Shot", body: "See your new #{profile_title} shot 👀", path: "/shots/#{id}")
end
end
end

# == Schema Information
Expand Down
1 change: 0 additions & 1 deletion app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
<meta name="mobile-web-app-capable" content="yes">
<meta content="#ffffff" media="(prefers-color-scheme: light)" name="theme-color">
<meta content="#171717" media="(prefers-color-scheme: dark)" name="theme-color">
<%= tag.meta name: "vapid-public-key", content: Rails.application.credentials.webpush.public_key %>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= yield :head %>
Expand Down
8 changes: 4 additions & 4 deletions app/views/pwa/service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ self.addEventListener("push", async (event) => {
self.addEventListener("notificationclick", (event) => {
event.notification.close()

const targetUrl = event.notification.data?.url || "/"
const targetPath = event.notification.data?.path || "/"
event.waitUntil(
clients.matchAll({ type: 'window' }).then(windowClients => {
for (let client of windowClients) {
if (client.url === targetUrl && 'focus' in client) {
if (client.path === targetPath && 'focus' in client) {
return client.focus()
}
}
return clients.openWindow(targetUrl)
return clients.openWindow(targetPath)
})
)
})
})
2 changes: 1 addition & 1 deletion app/views/shots/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<% if Current.user && Current.user.shots.where("extract(year from start_time) = ?", 2024).exists? %>
<%= render "yearly_brew_promo" %>
<% end %>
<div class="flex flex-col mx-auto my-6 gap-y-6 max-w-7xl sm:px-6 lg:px-8" data-controller="panel push">
<div class="flex flex-col mx-auto my-6 gap-y-6 max-w-7xl sm:px-6 lg:px-8" data-controller="panel push" data-push-vapid-key-value="<%= Rails.application.credentials.webpush.public_key.tr("_-", "/+") %>">
<div class="flex flex-col gap-2 px-4 sm:flex-row sm:px-0 sm:justify-between">
<div class="flex-shrink min-w-0">
<h1 class="pr-2 font-serif text-4xl font-bold tracking-tight truncate text-neutral-900 dark:text-neutral-100" id="shots-count">
Expand Down
2 changes: 1 addition & 1 deletion config/credentials/production.yml.enc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pPSsYUexBERes34BP3u2pK5pUk2a0lX3r6wlJF+MGbJhsWD5xeR7Mf+tq8Z8ZfiKacABD6ETRTfXINJe8cZ+q3YWNHr/RxSENdxWIdapq0zTtg7ddRzsDVeGLHg8WpxAw6uluZqWeWm9eNhyPxCzuNhQSPpn370+tJxCaW/v+M5JIA8dqKwxy4UE3KHXrdLylVwzU6L1Bd94Mt/i194DZbz3tijuqJFBYdixpwOlI4Yk75xMFrvHZXYWlCA5ZTVx/5kz05UrpElAP/STv+DsvpLs9Ok9JY5RsmeLWOSNqWi+xazFbvhRxL4wkhgAmiuxMVcCar6f6PvjlwfKBza1s+2ySijoeA9JY4DSmkew7s0VCOFMktWlZN7/+IbsPnmgaeRvRPSBdN7+9hdsz2NBbqZ7J4hWGDxln9ZHIQbDuQzEU+VMpp/7C+sT7QkHmmpU31uXlWhHG1JfklqgcwSAkB/UTvN9HPzu75DpcQLTzW3K/Khe+UUULOP2iF+5MMTo280FjwSuXikOvC7AOtZ22V8EdbAMjtLpOkP8vo4s56C0oyOJJraItnA0o8Mc7w2DKwOpAhvmsgkLNrF8fFlncvOdsapUbT77EqBlBbzKeDRv7MqrmAc5BGDg4kajeC+IwZFd7bAZ3PPGmA6lXsTaPSINyc++e6gXnqdEWZ7h/Y5Toa/48n7V5Bp4Bm7DdiUhS/OpaLKA0guQLmCSqpRb850txqLMEI3MhcAu5fs4cV4djNDBa8WUMVEoGKRqGvX8QZIERyzhhc8+AWvhK8yx9R/mfgPaYjpq8uXmF9Ruv0FVq4z7hv7WumgH40UtXUIy+Cm71N4cJRc23bsT1jqupDFd+pwQfu0/ynS2sdCAW7VOiElhH9nUP2knyVO0NIHCWcsDmUZoTuub6aCEBvsHmvSg0We+3vLX2Vw7F+jBg9HEb55cN/v5X2CN/xc1F56S11gYC/epAxshWbtYQIccAyVlYV3yM4qtUVoex//DugaLMU1GcWLkThgKhwqoRAWox0hk6hiOONTjBUwzSwuPwsHdx9XRXCOA4wi6q5r5sySYSft6Wec4Fn7Bfhi/TRJYhk14UlaYsDyiPrIKQXe9U/yIOCu+5lNUFBEyjamWj7zUf0223AgJJw7pYnVqjGSQH1EJb6RWGx7OxLJI6NycERqGltDvbYnppeyMpu31TrVA8dFGFl5nCWVVl0Try9a68Tp7Bdsf1YPoVBApe1ifgcil2EK1B/Ey6oTDBD3MOMLuc2A7ogHAWi08ZuA5e9BHNbwQGOgacWiUKpz8UQQiLz4h0P8o6UtCm/JuwC4RDL+pF8XyOg2SHfW32mV7kRV7ubULdxbZbqCaV02MjcXqkoiN91QPzNJfS8L3RGOwymKGzSocYioiSqB3kRWj7jIVydHexLDUI4HCWNOzv6w62tm6yqvTcyoViT4qIvko7xeKxGd3e/g7JdaCsbqlg7i+GigCTe/OcA3O1RWcfAMgHV7k8Z7upWqNfUmMbysH2IXKOrYz62du/ITJIu6SD9MxVUz7WQJFa3hjZS0Ot93Mi/Z1lD8YwiAlM9Cl/BnCVf+gjEdflYZ6X8UIBpnSFbsQjcmlGfr3Vd2FH+TUSIRp0XCSF5i7/vsgFEIGtg6bR4yqobzfNNxViItQ3y/nmvquefsBxLnKgdOLq5gYrW+6S5uoeu9YTFUNUGpaK5O9yUZEAOnoEoe1rvRt75LJOd0t8li5nMhl6Qrc7y0d9UD5pB8PDWRlSC04GlmJjGOoJRu9r48g1XR/TChj/box1tQxmqmh6VoKOTFp0Tp49odEHvk/oPbHXQZmsm7fLGKV8u5wX1divKrzMNSlG+iSPOcvAoimj6RSDCHSnFZTOvvg4N19yO+u07OvXn96rWQIVVbJQcqkwfOd0G3EqE4Ey1AgHrbkmP9zl2FCkZ0rlJti--KSbLcdyhU8EyDRk7--TcaI0dxQN1kq9Pk9gZUWpA==
P+y4f05WIEQ+3wKjskYrdurYP65GqSvwxF2f4NRABZe28VyM9jxg/KCXWlJTNvCIyMCTE9SvQRfsHq1/iZ3kE4e8uyRVWAAYNlESfMJcnw5Pk9xwMMtOImk9xwe13KLEpglw4mef0zanyyE4bjiDjSYKPvw4iMbgmNiJxYQQ9kqTYXl+uK6MNd70Nlmf2jVhiWTP854MCBU1vLfJytRUfM2RdsCJz+ftMkGhmbHncsUUVDOZTLmEf13LW9mIdNkK+OrIK7TTI9iR1drTB4Ue5WW3iAGAKU1X3Z1WWiYNKQVgnZ4QL744MnJYh0ENrvFy+cHuf8bTrvL25xvDkbEL6V/3/vdOZY/q1CgQv+Sqr+/xpvfi0ai6xiZnHodTmplESNLkAePgBB3EH7cSTB+d9qwvJH7BNVjbh/jeL8cbeb/QbbsxKCpa9FnAv8IOSlRJS/jCJdg83S1c+q5vsYo9ahQllHBHS0Dbv+ZTobSRGZOAMD7wjEdKkc8ODgddGO0TsqoM6Kk8pWrM5R6sKcr2qfgR18+5Fjsk0AMFWdtlM4MRZToIlunAhFIhyqEwbHCk5jE1ZoHUmFTYSFSdPvpuGob0CbI/idLZVqzV1iBk3Uw3oL0GzkURmMX1Ao+KQdKET1xggC3GNQDZ8A/jCJpFn7Nnlv35kO0Fu3xqEjyHjjd3KbOFWaqfooP25JU0i4R2CTNTzTySqxw0CYiL1LTq3Oyxp0k2Vo7BNty5LY/wUQXZg6KIdv23TPv7A5OK6UlgW4Fn/JOKQ6jcNvYA54iF2VUb5fttnOUas6uE/LWkwd2GKfUhUAHcnILjLTN/HFZ08AAJikxbnlinOdcHcgGF/4Sd71QhGJSYx2yxf8ArzfAXjUoKxKaMtPwRulWIvxwihvGZ0UWRa1dsSVw2rYkqEmvspxOldxcUU7Pcf2g0nxPROZo7lH4dvgsyWMpcTXaWcb9Ged5Ex4MA8LSHasJPdFqMRWl0jVusJXXbLOP6QcgXEDaL4XKHxPCg3Es5DRNB40SbBk+QzaLkteq4/BO32WZz2sUDNlMz4+HLZifJoZwh2CG/oZaegBAYjOnkhl3o/kakRBFG8Q4iw1sS34fJ2SNe7W8N7amaIhofto4sCEEb7gPi0x6W7LBgkzJ58Fli3kFKfloGt5VWctAiyw3BgFolC7RL1BuJLg3GTKYuLWIEpqFsR6UqVTi5zbU2HFnG+2bqvFS8sEyVkThvVs/sqzW2ePRPCOCBgJuduDE0wc9kpPmvVn84d+tvctLQuPLkkUWV2KDOZ0mEdaLi/LMC67pFv78vd17UApPWqf3JLm8h4sqp2v73eCHG0e0H+/KQMP8W2CbYr1f1syB9V3H4tHyyznfYXdRXFKwBpRij+wlV4u01HxZvMw4QpGi/b4OW+Pz7zsrmbzqwiS3Qgj9uSB/2bLGPzEIzp6gwBuSHlq2MLjgQkPtVh5IloDomJULQfZZeXANEZ4TlIApm7Icg4k9Ndrb7fhpfwPQmxLsaLoz367Th6RPOdXnSRcdMXjgBeETyDqECmYi5ueJo6uuwkRVmiUWuREQVet4tSSaOTpABUpjxOPQEv4rpjakM0l7QvgKL5MYjurRbEKY13AfZ/wJ4nr7m1BALSM6DmvpA0hWcydbenfRkmFWQYVOxZ1BGpSLON5kH6q6aMl5nKxObBH/sjxZp8JMpZCWgXsF8RS7SXitDibdam7ACFMV5i/VHBfpc+eWXdhoO6bAmmm367n803cPNFpqbBWqpqWohbHdDnZOEdiFdWflD0ufRHLGYXDzbGNyH1b5U0Df/SFUs4nR1xdmkuAbuEhoMwmP+twDFwBrAnN943hUGr3jf/+sxFOu6OBNS7aEE3E/WWuyJGegWgQKHYF35ouRNzs2e5zJWDbx9/B3L6KwuKFZrkaLu5I7VMWAN301JZ06ThBDQ--BnN7pjLShzRjnNiQ--6gozVcY3ohTeXzBNr9O++w==

0 comments on commit ea5d7a0

Please sign in to comment.