Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ __These keys must be safely stored and should not change.__

## Usage

Now you can use the channel in your `via()` method inside the notification and send a web push notification:
Now you can use the channel in your `via()` method inside the notification and send a generic web push notification:

```php
use Illuminate\Notifications\Notification;
Expand Down Expand Up @@ -107,6 +107,45 @@ class AccountApproved extends Notification
}
```

This package also supports [Declarative Web Push messages](https://www.w3.org/TR/push-api/#declarative-push-message), which aim to reduce the complexity of using push on the web in general and address some challenges of generic web push notifications like privacy concerns & battery life on mobile by making a client-side service worker optional while remaining fully backwards compatible:

```php
use Illuminate\Notifications\Notification;
use NotificationChannels\WebPush\DeclarativeWebPushMessage;
use NotificationChannels\WebPush\WebPushChannel;

class AccountApproved extends Notification
{
public function via($notifiable)
{
return [WebPushChannel::class];
}

public function toWebPush($notifiable, $notification)
{
return (new DeclarativeWebPushMessage)
->title('Approved!')
->icon('/approved-icon.png')
->body('Your account was approved!')
->action('View account', 'view_account', 'https://myapp.com/accounts')
->navigate('https://myapp.com');
// ->data(['id' => $notification->id])
// ->badge()
// ->dir()
// ->image()
// ->lang()
// ->mutable()
// ->renotify()
// ->requireInteraction()
// ->silent()
// ->tag()
// ->timestamp()
// ->vibrate()
// ->options(['TTL' => 1000, 'contentType' => 'application/json'])
}
}
```

You can find the available options [here](https://github.com/web-push-libs/web-push-php#notifications-and-default-options).

### Save/Update Subscriptions
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"php": "^8.2",
"illuminate/notifications": "^11.0|^12.0",
"illuminate/support": "^11.0|^12.0",
"minishlink/web-push": "^9.0"
"minishlink/web-push": "^9.0.3"
},
"require-dev": {
"larastan/larastan": "^3.1",
Expand Down
305 changes: 305 additions & 0 deletions src/DeclarativeWebPushMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
<?php

namespace NotificationChannels\WebPush;

use Illuminate\Support\Arr;
use NotificationChannels\WebPush\Exceptions\MessageValidationFailed;

/**
* @implements \Illuminate\Contracts\Support\Arrayable<string, mixed>
*
* @link https://www.w3.org/TR/push-api/#members
*/
class DeclarativeWebPushMessage implements WebPushMessageInterface
{
protected string $title;

/**
* @var array<array-key, array{'title': string, 'action': string, 'navigate': string, 'icon'?: string}>
*/
protected array $actions = [];

protected string $badge;

protected string $body;

protected string $dir;

protected string $icon;

protected string $image;

protected string $lang;

protected bool $mutable;

protected string $navigate;

protected bool $renotify;

protected bool $requireInteraction;

protected bool $silent;

protected string $tag;

protected int $timestamp;

/**
* @var array<int>
*/
protected array $vibrate;

protected mixed $data;

/**
* @var array<string, mixed>
*/
protected array $options = [
'contentType' => 'application/json',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the documentation I saw application/notification+json, but not sure if this is now a requirement?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this comment, the absence of the content type header in the parser spec draft and that there's been no activity on the RFC 8030 side of things for years I assume the specific header will no longer be taken into account. This can obviously change in the future, but for now application/json is a safe bet that can easily be changed if needed imho. I also tested this with Safari, and it works flawlessly with the regular JSON content type.

];

/**
* Set the notification title.
*
* @return $this
*/
public function title(string $value): static
{
$this->title = $value;

return $this;
}

/**
* Add a notification action.
*
* @return $this
*/
public function action(string $title, string $action, string $navigate, ?string $icon = null): static
{
$this->actions[] = array_filter(['title' => $title, 'action' => $action, 'navigate' => $navigate, 'icon' => $icon]);

return $this;
}

/**
* Set the notification badge.
*
* @return $this
*/
public function badge(string $value): static
{
$this->badge = $value;

return $this;
}

/**
* Set the notification body.
*
* @return $this
*/
public function body(string $value): static
{
$this->body = $value;

return $this;
}

/**
* Set the notification direction.
*
* @return $this
*/
public function dir(string $value): static
{
$this->dir = $value;

return $this;
}

/**
* Set the notification icon url.
*
* @return $this
*/
public function icon(string $value): static
{
$this->icon = $value;

return $this;
}

/**
* Set the notification image url.
*
* @return $this
*/
public function image(string $value): static
{
$this->image = $value;

return $this;
}

/**
* Set the notification language.
*
* @return $this
*/
public function lang(string $value): static
{
$this->lang = $value;

return $this;
}

/**
* @return $this
*/
public function mutable(bool $value = true): static
{
$this->mutable = $value;

return $this;
}

/**
* Set the navigation target upon activation.
*
* @return $this
*/
public function navigate(string $value): static
{
$this->navigate = $value;

return $this;
}

/**
* @return $this
*/
public function renotify(bool $value = true): static
{
$this->renotify = $value;

return $this;
}

/**
* @return $this
*/
public function requireInteraction(bool $value = true): static
{
$this->requireInteraction = $value;

return $this;
}

/**
* @return $this
*/
public function silent(bool $value = true): static
{
$this->silent = $value;

return $this;
}

/**
* Set the notification tag.
*
* @return $this
*/
public function tag(string $value): static
{
$this->tag = $value;

return $this;
}

/**
* Set the timestamp associated with the notification.
*
* @return $this
*/
public function timestamp(int $value): static
{
$this->timestamp = $value;

return $this;
}

/**
* Set the notification vibration pattern.
*
* @param array<int> $value
* @return $this
*/
public function vibrate(array $value): static
{
$this->vibrate = $value;

return $this;
}

/**
* Set the notification arbitrary data.
*
* @return $this
*/
public function data(mixed $value): static
{
$this->data = $value;

return $this;
}

/**
* Set the notification options.
*
* @link https://github.com/web-push-libs/web-push-php#notifications-and-default-options
*
* @param array<string, mixed> $value
* @return $this
*/
public function options(array $value): static
{
$this->options = $value;

return $this;
}

/**
* Get the notification options.
*
* @return array<string, mixed>
*/
public function getOptions(): array
{
return $this->options;
}

/**
* Get an array representation of the message.
*
* @return array<string, mixed>
*/
public function toArray(): array
{
if (empty($this->title)) {
throw MessageValidationFailed::titleRequired();
}

if (empty($this->navigate)) {
throw MessageValidationFailed::navigateRequired();
}

return Arr::whereNotNull([
'web_push' => 8030,
'notification' => Arr::except(array_filter(get_object_vars($this)), ['mutable', 'options']),
'mutable' => $this->mutable ?? null,
]);
}
}
4 changes: 2 additions & 2 deletions src/Events/NotificationFailed.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Illuminate\Queue\SerializesModels;
use Minishlink\WebPush\MessageSentReport;
use NotificationChannels\WebPush\PushSubscription;
use NotificationChannels\WebPush\WebPushMessage;
use NotificationChannels\WebPush\WebPushMessageInterface;

class NotificationFailed
{
Expand All @@ -16,7 +16,7 @@ class NotificationFailed
*
* @return void
*/
public function __construct(public MessageSentReport $report, public PushSubscription $subscription, public WebPushMessage $message)
public function __construct(public MessageSentReport $report, public PushSubscription $subscription, public WebPushMessageInterface $message)
{
//
}
Expand Down
Loading