Er du lei av å bruke enda et verktøy for å gå inn og sende ut pushvarsler til brukerne dine? La oss ta en titt på hvordan du kan bruke Sanity til å administrere og sende ut pushvarsler!
Pushvarsler kan være så mangt. De kan være informative, irriterende, få deg til å smile eller slå av mobilen din. De kan være utrolig effektive til å trigge folk til å bruke produktet ditt, og de kan være like effektive til å få folk til å slette appen din fra mobilen. Trå varsomt, med andre ord.
Men skal du først sende ut en, så er det ikke alltid like lett. Det finnes massevis av systemer som lar deg sende dem ut, men mange av dem er klumsete å bruke, og vet ikke om resten av innholdet ditt.
I denne artikkelen skal vi se på hvordan du kan bruke CMSet Sanity og et enkelt API-endepunkt til å sende ut pushvarsler som er tett integrert med innholdet du har lyst til å prioritere.
Lag et pushvarsel-schema
Om du vil sende ut pushvarsler via Sanity, så er det første du må gjøre å definere et schema for pushvarsler. Det kan f.eks. se slik ut:
import { FaBell } from "react-icons/fa";
import { defineField, defineType } from "sanity";
export const pushvarsel = defineType({
title: "Pushvarsel",
description: "Et pushvarsel som sendes til brukere",
icon: FaBell,
name: "pushvarsel",
type: "document",
fields: [
defineField({
title: "Tittel",
description: "Overskriften på pushvarselet",
name: "tittel",
type: "string",
validation: (Rule) => Rule.required().max(30),
}),
defineField({
title: "Undertittel",
description: "Teksten som kommer under overskriften. Supplerende informasjon.",
name: "undertittel",
type: "string",
validation: (Rule) => Rule.required().max(30),
}),
defineField({
title: "Innhold",
description: "Velg det innholdet du ønsker at folk kommer til om de navigerer til siden din",
name: "innhold",
type: "reference",
to: [{ type: "side" }, { type: "kampanje" }, { type: "bloggpost" }],
validation: (Rule) => Rule.required(),
}),
],
preview: {
select: {
title: "tittel",
subtitle: "undertittel",
},
prepare(selection) {
const { title, subtitle } = selection;
return {
title: title || "Uten tittel",
subtitle: `${subtitle || "Ingen underoverskrift"}`,
media: FaBell,
};
},
},
});
Et par linjer kode, kanskje, men kort oppsummert, så lager man et dokument som har en overskrift, en undertittel og en lenke til innholdet man ønsker at man sendes til når man trykker på den. Enkelt nok, sant?
Du kan utvide denne typen etterhvert, som for eksempel et bilde, forskjellige språk eller hvilket segment med brukere du ønsker å sende ut til.
Lag en webhook
Så nå kan du skrive pushvarslene dine i Sanity, men ingenting skjer når du publiserer dem. Hvordan får du det til å skje?
Når man gjør noe i Sanity, som f.eks. publiserer noe, så kan man trigge en webhook. Den webhooken kaller et API-endepunkt, som igjen kan gjøre fancy ting som å sende ut et pushvarsel. Og det er det vi skal gjøre nå.
Merk at Sanity nå har lansert Functions – et slags lite API-endepunkt som også kan gjøre mye av det samme. Siden det fortsatt er i beta når dette har blitt skrevet, holder vi oss til webhooks og API-endepunkter.
For å lage en webhook, går du inn på sanity.io/manage, velger API, og oppretter en webhook. Der fyller du inn navn, beskrivelse, URLen til et API-endepunkt du skal lage (f.eks. mittprosjekt.no/api/webhook/push) og datasettet du vil bruke.
Så velger du når webhooken din skal fyre av. I vårt eksempel ønsker jeg at den skal trigge når vi publiserer et nytt dokument (Sanity kaller dette "create").
Det neste du fyller ut er et filter – dvs hvilke dokumenter du vil at Sanity sin webhook skal fyre av på. I vårt tilfelle er dette:
_type == "pushNotification"Det siste du må fylle ut er projeksjonen, altså hvilken data fra de dokumentene du ønsker skal bli sendt med webhooken. Der ønsker vi vi egentlig all data – inkludert litt info om hvilket dokument som lenkes til:
{
_id,
title,
description,
"content": linkTo-> {
_id,
_type
}
}Huk av for "enable webhook", og lag en secret du kan bruke senere. Nå er vi nemlig klare for å implementere koden som skal kjøre av når webhooken din kjører!
Lag API-endepunktet ditt
Neste steg i denne prosessen er å ta imot webhook-kallet ditt. Det gjøres ved å lage et API-endepunkt, som Sanity kaller når du publiserer en ny push-notifikasjon.
Du kan selvfølgelig implementere API-endepunktet ditt i din foretrukne teknologi. Jeg har tilfeldigvis gjort det som en API route i Next.js.
Her er litt boilerplate for å ta imot webhooken, inkludert dekoding med hemmeligheten din:
import { adminFirestore } from "@/utils/firebase/admin";
import { toLanguage } from "@/utils/i18n";
import { SIGNATURE_HEADER_NAME, isValidSignature } from "@sanity/webhook";
import { Expo, ExpoPushMessage } from "expo-server-sdk";
import { NextApiRequest, NextApiResponse } from "next";
const expoClient = new Expo();
type PushNotificationProjection = {
_id: string;
title: { no: string; en: string };
description: { no: string; en: string };
content?: {
_id: string;
_type: string;
};
};
/**
* Webhook handler for sending push notifications when they are published in Sanity.
*/
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Vi tar kun imot POST-kall
if (req.method !== "POST") {
return res.status(405).json({ message: "Method not allowed" });
}
// Sjekk at signaturen er med fra Sanity
const signature = req.headers[SIGNATURE_HEADER_NAME];
if (typeof signature !== "string") {
return res.status(400).json({ message: "Missing or malformed signature" });
}
// Sjekk at body-en din er signert med hemmeligheten du oppga i forrige steg
const stringBody = await readBody(req);
if (
!isValidSignature(
stringBody,
signature,
process.env.SANITY_WEBHOOK_SECRET as string
)
) {
res.status(401).json({ message: "Invalid signature" });
return;
}
console.info(
"Valid webhook received from Sanity, processing push notification…"
);
try {
// Her henter vi ut og validerer informasjonen vi får fra webhooken
const pushNotification: PushNotificationProjection = JSON.parse(stringBody);
if (!pushNotification.title || !pushNotification.description) {
console.error(
"Push notification missing required fields:",
pushNotification
);
return res.status(400).json({ message: "Missing required fields" });
}
// Her skal vi sende push-varslinger!
return res.status(200).json({
message: "Push notifications sent",
});
} catch (error) {
console.error("Error processing push notification webhook:", error);
return res.status(500).json({ message: "Internal server error" });
}
}
Det er noen linjer med kode – men det vi gjør (ganske grundig) er å ta imot responsen og validere den. Det eneste som gjenstår er å sende push-varsler.
Sende pushvarsler
Det finnes haugevis av måter å sende pushvarsler på, og like mange tjenester som lover å gjøre det enklere for deg.
Måten jeg skal vise her er ved hjelp av Expo sin notification-tjeneste. Den tar inn et token per bruker, og abstraherer vekk om du skal sende via Apple sin tjeneste eller Google (Firebase) sin.
Tokenet får man ved å hente inn godkjenning fra brukeren, som er utenfor scope av denne artikkelen. Men du kan lese hvordan man gjør det her. Vi antar at bruker-tabellen din har et felt som heter pushToken, og implementerer det slik:
// hent ut alle push tokens fra bruker-tabellen din
// loop gjennom dem og map hver token om til et objekt som inneholder all informasjon om et pushvarsel
// del opp pushvarslene i chunks
// send hver chunk
// verifiser at pushvarslene ble sendt (og retry de som feilet)Om man implementerer dette, får man noe som ser ut som dette:
const users = await getUsersWithPushTokens();
const pushNotifications = users.map(user => ({
to: token,
sound: "default",
title: pushNotification.title[user.preferredLanguage],
body: pushNotification.description[user.preferredLanguage],
data: {
notificationId: pushNotification._id,
path, // hvor jeg vil at brukeren skal deep linkes i appen
},
}));
const chunks = expoClient.chunkPushNotifications(messages);
const tickets = [];
for (const chunk of chunks) {
try {
const ticketChunk = await expoClient.sendPushNotificationsAsync(chunk);
tickets.push(...ticketChunk);
} catch (error) {
console.error("Error sending push notification chunk:", error);
}
}
const receiptIds = tickets
.filter((ticket) => ticket.status === "ok")
.map((ticket) => ticket.id);
if (receiptIds.length > 0) {
console.info(`${receiptIds.length} notifications sent successfully`);
}
const errorTickets = tickets.filter((ticket) => ticket.status === "error");
if (errorTickets.length > 0) {
console.error(
`${errorTickets.length} notifications failed:`,
errorTickets
);
}Og det er det – nå har du plutselig muligheten til å sende pushvarsler ved å publisere et dokument i Sanity 🎉
Next steps
Dette er en fungerende, men forenklet versjon av hvordan vi har implementert dette i vår kode. Men det er masse mer du kan legge til for å forbedre opplevelsen:
- Legge til segmentering i Sanity-modellen, slik at man kan filtrere hva slags brukere man ønsker å sende ut til
- Legge til A/B-testing, for å se hvilken push som konverterer best
- Vise åpningsstatistikk direkte i Sanity
- Videre tilpasse med bilder, lyder osv basert på bruksscenario
- Publisere pushvarsel som en del av en release i Sanity, slik at innhold og promotering av det lanseres samtidig
Mulighetene er egentlig bare begrenset av hva du kan komme på selv – hverken APIet eller Sanity legger noen demper på kreativiteten din.