fixup! Add webpush front-end support

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-05-07 09:06:45 +02:00
parent 8e647724ae
commit bdae03fc52
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
6 changed files with 56 additions and 9 deletions

View File

@ -75,3 +75,34 @@ registerRoute(
], ],
}) })
); );
self.addEventListener("push", async (event: any) => {
console.log("received push", event);
const options = {
body: "Ceci est une notification envoyée par Mobilizon !",
icon: "images/notification-flat.png",
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: 1,
},
actions: [
{
action: "explore",
title: "Go to the site",
icon: "images/checkmark.png",
},
{
action: "close",
title: "Close the notification",
icon: "images/xmark.png",
},
],
};
event.waitUntil(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
self.registration.showNotification("Push Notification", options)
);
});

View File

@ -18,11 +18,9 @@ function urlBase64ToUint8Array(base64String: string): Uint8Array {
} }
export async function subscribeUserToPush(): Promise<PushSubscription | null> { export async function subscribeUserToPush(): Promise<PushSubscription | null> {
const registration = await navigator.serviceWorker.register(
"/service-worker.js"
);
const client = apolloProvider.defaultClient as ApolloClient<NormalizedCacheObject>; const client = apolloProvider.defaultClient as ApolloClient<NormalizedCacheObject>;
const registration = await navigator.serviceWorker.ready;
const { data } = await client.mutate<{ config: IConfig }>({ const { data } = await client.mutate<{ config: IConfig }>({
mutation: WEB_PUSH, mutation: WEB_PUSH,
}); });
@ -45,3 +43,8 @@ export async function subscribeUserToPush(): Promise<PushSubscription | null> {
} }
return null; return null;
} }
export async function isSubscribed(): Promise<boolean> {
const registration = await navigator.serviceWorker.ready;
return (await registration.pushManager.getSubscription()) !== null;
}

View File

@ -18,6 +18,7 @@
<div class="setting-title"> <div class="setting-title">
<h2>{{ $t("Participation notifications") }}</h2> <h2>{{ $t("Participation notifications") }}</h2>
</div> </div>
<!-- <span v-if="isSubscribed()">{{ $t("You are already subscribed") }}</span> -->
<b-button <b-button
icon-left="rss" icon-left="rss"
@click="subscribeToWebPush" @click="subscribeToWebPush"
@ -212,7 +213,10 @@ import { IUser } from "../../types/current-user.model";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import { IFeedToken } from "@/types/feedtoken.model"; import { IFeedToken } from "@/types/feedtoken.model";
import { CREATE_FEED_TOKEN, DELETE_FEED_TOKEN } from "@/graphql/feed_tokens"; import { CREATE_FEED_TOKEN, DELETE_FEED_TOKEN } from "@/graphql/feed_tokens";
import { subscribeUserToPush } from "../../services/push-subscription"; import {
isSubscribed,
subscribeUserToPush,
} from "../../services/push-subscription";
import { REGISTER_PUSH_MUTATION } from "@/graphql/webPush"; import { REGISTER_PUSH_MUTATION } from "@/graphql/webPush";
@Component({ @Component({
@ -320,10 +324,11 @@ export default class Notifications extends Vue {
async subscribeToWebPush(): Promise<void> { async subscribeToWebPush(): Promise<void> {
if (this.canShowWebPush()) { if (this.canShowWebPush()) {
const subscription = await subscribeUserToPush(); const subscription = await subscribeUserToPush();
console.log("subscription", subscription?.toJSON());
const { data } = await this.$apollo.mutate({ const { data } = await this.$apollo.mutate({
mutation: REGISTER_PUSH_MUTATION, mutation: REGISTER_PUSH_MUTATION,
variables: { variables: {
...subscription, ...subscription?.toJSON(),
}, },
}); });
console.log(data); console.log(data);
@ -336,6 +341,10 @@ export default class Notifications extends Vue {
return window.isSecureContext && !!navigator.serviceWorker; return window.isSecureContext && !!navigator.serviceWorker;
} }
async isSubscribed(): Promise<boolean> {
return await isSubscribed();
}
private async deleteFeedToken(token: string): Promise<void> { private async deleteFeedToken(token: string): Promise<void> {
await this.$apollo.mutate({ await this.$apollo.mutate({
mutation: DELETE_FEED_TOKEN, mutation: DELETE_FEED_TOKEN,

View File

@ -25,7 +25,7 @@ defmodule Mobilizon.GraphQL.Resolvers.PushSubscription do
def register_push_subscription(_parent, args, %{ def register_push_subscription(_parent, args, %{
context: %{current_user: %User{id: user_id}} context: %{current_user: %User{id: user_id}}
}) do }) do
case Users.create_push_subscription(Map.put(args, :user_id, user_id)) do case Users.create_push_subscription(%{data: args, user_id: user_id}) do
{:ok, %PushSubscription{}} -> {:ok, %PushSubscription{}} ->
{:ok, "OK"} {:ok, "OK"}

View File

@ -3,6 +3,7 @@ defmodule Mobilizon.Users.PushSubscription do
alias Mobilizon.Users.User alias Mobilizon.Users.User
import Ecto.Changeset import Ecto.Changeset
@primary_key {:id, :binary_id, autogenerate: true}
schema "user_push_subscriptions" do schema "user_push_subscriptions" do
field(:digest, :string) field(:digest, :string)
belongs_to(:user, User) belongs_to(:user, User)
@ -26,6 +27,7 @@ defmodule Mobilizon.Users.PushSubscription do
|> cast_embed(:data, with: &cast_data/2) |> cast_embed(:data, with: &cast_data/2)
|> put_change(:digest, compute_digest(attrs.data)) |> put_change(:digest, compute_digest(attrs.data))
|> validate_required([:digest, :user_id, :data]) |> validate_required([:digest, :user_id, :data])
|> unique_constraint([:digest, :user_id], name: :user_push_subscriptions_user_id_digest_index)
end end
defp cast_data(schema, attrs) do defp cast_data(schema, attrs) do
@ -42,6 +44,8 @@ defmodule Mobilizon.Users.PushSubscription do
end end
defp compute_digest(data) do defp compute_digest(data) do
data = Jason.encode!(data)
:sha256 :sha256
|> :crypto.hash(data) |> :crypto.hash(data)
|> Base.encode16() |> Base.encode16()

View File

@ -19,12 +19,12 @@ defmodule Mobilizon.Service.Notifier.Push do
@impl Notifier @impl Notifier
def send(%User{id: user_id} = _user, %Activity{} = activity, _opts) do def send(%User{id: user_id} = _user, %Activity{} = activity, _opts) do
%Page{elements: subscriptions} = Users.list_user_push_subscriptions(user_id, 1, 100) %Page{elements: subscriptions} = Users.list_user_push_subscriptions(user_id, 1, 100)
Enum.each(subscriptions, &send_subscription(activity, &1)) Enum.map(subscriptions, &send_subscription(activity, &1.data))
end end
@impl Notifier @impl Notifier
def send(%User{} = user, activities, opts) when is_list(activities) do def send(%User{} = user, activities, opts) when is_list(activities) do
Enum.each(activities, &Push.send(user, &1, opts)) Enum.map(activities, &Push.send(user, &1, opts))
end end
defp payload(%Activity{subject: subject}) do defp payload(%Activity{subject: subject}) do