@ -13,59 +13,14 @@
#
require 'webpush'
require_relative '../../models/setting'
class Web :: PushSubscription < ApplicationRecord
include RoutingHelper
include StreamEntriesHelper
include ActionView :: Helpers :: TranslationHelper
include ActionView :: Helpers :: SanitizeHelper
has_one :session_activation
before_create :send_welcome_notification
def push ( notification )
name = display_name notification . from_account
title = title_str ( name , notification )
body = body_str notification
dir = dir_str body
url = url_str notification
image = image_str notification
actions = actions_arr notification
access_token = actions . empty? ? nil : find_or_create_access_token ( notification ) . token
nsfw = notification . target_status . nil? || notification . target_status . spoiler_text . empty? ? nil : notification . target_status . spoiler_text
# TODO: Make sure that the payload does not exceed 4KB - Webpush::PayloadTooLarge
Webpush . payload_send (
message : JSON . generate (
title : title ,
dir : dir ,
image : image ,
badge : full_asset_url ( 'badge.png' , skip_pipeline : true ) ,
tag : notification . id ,
timestamp : notification . created_at ,
icon : notification . from_account . avatar_static_url ,
data : {
content : decoder . decode ( strip_tags ( body ) ) ,
nsfw : nsfw . nil? ? nil : decoder . decode ( strip_tags ( nsfw ) ) ,
url : url ,
actions : actions ,
access_token : access_token ,
message : translate ( 'push_notifications.group.title' ) , # Do not pass count, will be formatted in the ServiceWorker
}
) ,
endpoint : endpoint ,
p256dh : key_p256dh ,
auth : key_auth ,
vapid : {
subject : " mailto: #{ Setting . site_contact_email } " ,
private_key : Rails . configuration . x . vapid_private_key ,
public_key : Rails . configuration . x . vapid_public_key ,
} ,
ttl : 40 * 60 * 60 # 48 hours
)
I18n . with_locale ( session_activation . user . locale || I18n . default_locale ) do
push_payload ( message_from ( notification ) , 48 . hours . seconds )
end
end
def pushable? ( notification )
@ -73,120 +28,47 @@ class Web::PushSubscription < ApplicationRecord
end
def as_payload
payload = {
id : id ,
endpoint : endpoint ,
}
payload = { id : id , endpoint : endpoint }
payload [ :alerts ] = data [ 'alerts' ] if data && data . key? ( 'alerts' )
payload
end
private
def title_str ( name , notification )
case notification . type
when :mention then translate ( 'push_notifications.mention.title' , name : name )
when :follow then translate ( 'push_notifications.follow.title' , name : name )
when :favourite then translate ( 'push_notifications.favourite.title' , name : name )
when :reblog then translate ( 'push_notifications.reblog.title' , name : name )
end
end
def body_str ( notification )
case notification . type
when :mention then notification . target_status . text
when :follow then notification . from_account . note
when :favourite then notification . target_status . text
when :reblog then notification . target_status . text
end
end
def url_str ( notification )
case notification . type
when :mention then web_url ( " statuses/ #{ notification . target_status . id } " )
when :follow then web_url ( " accounts/ #{ notification . from_account . id } " )
when :favourite then web_url ( " statuses/ #{ notification . target_status . id } " )
when :reblog then web_url ( " statuses/ #{ notification . target_status . id } " )
end
def access_token
find_or_create_access_token . token
end
def actions_arr ( notification )
actions =
case notification . type
when :mention then [
{
title : translate ( 'push_notifications.mention.action_favourite' ) ,
icon : full_asset_url ( 'web-push-icon_favourite.png' , skip_pipeline : true ) ,
todo : 'request' ,
method : 'POST' ,
action : " /api/v1/statuses/ #{ notification . target_status . id } /favourite " ,
} ,
]
else [ ]
end
should_hide = notification . type . equal? ( :mention ) && ! notification . target_status . nil? && ( notification . target_status . sensitive || ! notification . target_status . spoiler_text . empty? )
can_boost = notification . type . equal? ( :mention ) && ! notification . target_status . nil? && ! notification . target_status . hidden?
if should_hide
actions . insert ( 0 , title : translate ( 'push_notifications.mention.action_expand' ) , icon : full_asset_url ( 'web-push-icon_expand.png' , skip_pipeline : true ) , todo : 'expand' , action : 'expand' )
end
if can_boost
actions << { title : translate ( 'push_notifications.mention.action_boost' ) , icon : full_asset_url ( 'web-push-icon_reblog.png' , skip_pipeline : true ) , todo : 'request' , method : 'POST' , action : " /api/v1/statuses/ #{ notification . target_status . id } /reblog " }
end
actions
end
def image_str ( notification )
return nil if notification . target_status . nil? || notification . target_status . media_attachments . empty?
full_asset_url ( notification . target_status . media_attachments . first . file . url ( :small ) )
end
private
def dir_str ( body )
rtl? ( body ) ? 'rtl' : 'ltr'
end
def push_payload ( message , ttl = 5 . minutes . seconds )
# TODO: Make sure that the payload does not
# exceed 4KB - Webpush::PayloadTooLarge
def send_welcome_notification
Webpush . payload_send (
message : JSON . generate (
title : translate ( 'push_notifications.subscribed.title' ) ,
icon : full_asset_url ( 'android-chrome-192x192.png' , skip_pipeline : true ) ,
badge : full_asset_url ( 'badge.png' , skip_pipeline : true ) ,
data : {
content : translate ( 'push_notifications.subscribed.body' ) ,
actions : [ ] ,
url : web_url ( 'notifications' ) ,
message : translate ( 'push_notifications.group.title' ) , # Do not pass count, will be formatted in the ServiceWorker
}
) ,
message : Oj . dump ( message ) ,
endpoint : endpoint ,
p256dh : key_p256dh ,
auth : key_auth ,
ttl : ttl ,
vapid : {
subject : " mailto: #{ Setting . site_contact_email } " ,
private_key : Rails . configuration . x . vapid_private_key ,
public_key : Rails . configuration . x . vapid_public_key ,
} ,
ttl : 5 * 60 # 5 minutes
}
)
end
def find_or_create_access_token ( notification )
def message_from ( notification )
serializable_resource = ActiveModelSerializers :: SerializableResource . new ( notification , serializer : Web :: NotificationSerializer , scope : self , scope_name : :current_push_subscription )
serializable_resource . as_json
end
def find_or_create_access_token
Doorkeeper :: AccessToken . find_or_create_for (
Doorkeeper :: Application . find_by ( superapp : true ) ,
notification . account . user . id ,
session_activation . user_id ,
Doorkeeper :: OAuth :: Scopes . from_string ( 'read write follow' ) ,
Doorkeeper . configuration . access_token_expires_in ,
Doorkeeper . configuration . refresh_token_enabled?
)
end
def decoder
@decoder || = HTMLEntities . new
end
end