Skip to content

Commit

Permalink
Support for confirm links.
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianStehle committed Sep 6, 2023
1 parent 7378bc3 commit 0c53d38
Show file tree
Hide file tree
Showing 23 changed files with 141 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public sealed class MobilePushMessage : BaseMessage

public string? Body { get; init; }

public string? ConfirmLink { get; init; }

public string? ConfirmText { get; init; }

public string? ConfirmUrl { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public static Message ToFirebaseMessage(this MobilePushMessage source, DateTimeO
message.Data =
new Dictionary<string, string>()
.WithNonEmpty("id", source.NotificationId.ToString())
.WithNonEmpty("confirmLink", source.ConfirmLink)
.WithNonEmpty("confirmText", source.ConfirmText)
.WithNonEmpty("confirmUrl", source.ConfirmUrl)
.WithNonEmpty("isConfirmed", source.IsConfirmed.ToString())
Expand Down
10 changes: 0 additions & 10 deletions backend/src/Notifo.Domain/Channels/ChannelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,6 @@ public static string HtmlTrackingLink(this BaseUserNotification notification, Gu
return trackingLink;
}

public static string? ConfirmText(this BaseUserNotification notification)
{
return notification.Formatting.ConfirmText;
}

public static string? ConfirmUrl(this BaseUserNotification notification)
{
return notification.ConfirmUrl;
}

public static string? ImageSmall(this BaseUserNotification notification, IImageFormatter imageFormatter, string preset)
{
return imageFormatter.AddPreset(notification.Formatting.ImageSmall, preset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ private MobilePushMessage BuildMessage(MobilePushJob job)
{
Subject = notification.Formatting?.Subject,
Body = notification.Formatting?.Body,
ConfirmLink = notification.Formatting?.ConfirmLink,
ConfirmText = notification.Formatting?.ConfirmText,
ConfirmUrl = notification.ComputeConfirmUrl(Providers.MobilePush, job.ConfigurationId),
Data = notification.Data,
Expand Down
5 changes: 5 additions & 0 deletions backend/src/Notifo.Domain/Channels/WebPush/WebPushChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ public override IEnumerable<SendConfiguration> GetConfigurations(UserNotificatio
public override async Task SendAsync(UserNotification notification, ChannelContext context,
CancellationToken ct)
{
if (context.IsUpdate)
{
return;
}

if (!context.Configuration.TryGetValue(Endpoint, out var endpoint))
{
// Old configuration without a mobile push token.
Expand Down
4 changes: 3 additions & 1 deletion backend/src/Notifo.Domain/Channels/WebPush/WebPushPayload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public sealed class WebPushPayload
[JsonPropertyName("cu")]
public string? ConfirmUrl { get; set; }

[JsonPropertyName("cl")]
public string? ConfirmLink { get; set; }

[JsonPropertyName("ct")]
public string? ConfirmText { get; set; }

Expand Down Expand Up @@ -61,7 +64,6 @@ public static WebPushPayload Create(UserNotification notification, Guid configur
SimpleMapper.Map(notification.Formatting, result);

// Compute the tracking links afterwards because the mapping would override it.
result.ConfirmText = notification.Formatting.ConfirmText;
result.ConfirmUrl = notification.ComputeConfirmUrl(Providers.WebPush, configurationId);
result.TrackSeenUrl = notification.ComputeTrackSeenUrl(Providers.WebPush, configurationId);
result.TrackDeliveredUrl = notification.ComputeTrackDeliveredUrl(Providers.WebPush, configurationId);
Expand Down
4 changes: 4 additions & 0 deletions backend/src/Notifo.Domain/NotificationFormatting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public static NotificationFormatting<string> Clone(this NotificationFormatting<s
return new NotificationFormatting<string>
{
Body = source.Body,
ConfirmLink = source.ConfirmLink,
ConfirmMode = source.ConfirmMode,
ConfirmText = source.ConfirmText,
ImageLarge = source.ImageLarge,
Expand All @@ -76,6 +77,7 @@ public static NotificationFormatting<LocalizedText> MergedWith(this Notification
return new NotificationFormatting<LocalizedText>
{
Body = Merged(source.Body, other?.Body),
ConfirmLink = Merged(source.ConfirmLink, other?.ConfirmLink),
ConfirmMode = source.ConfirmMode ?? other?.ConfirmMode,
ConfirmText = Merged(source.ConfirmText, other?.ConfirmText),
ImageLarge = Merged(source.ImageLarge, other?.ImageLarge),
Expand All @@ -91,6 +93,7 @@ public static NotificationFormatting<LocalizedText> Clone(this NotificationForma
return new NotificationFormatting<LocalizedText>
{
Body = source.Body?.Clone(),
ConfirmLink = source.ConfirmLink?.Clone(),
ConfirmMode = source.ConfirmMode,
ConfirmText = source.ConfirmText?.Clone(),
ImageLarge = source.ImageLarge?.Clone(),
Expand All @@ -108,6 +111,7 @@ public static NotificationFormatting<TOut> Transform<TIn, TOut>(this Notificatio
var result = new NotificationFormatting<TOut>
{
Body = transform(formatting.Body),
ConfirmLink = transform(formatting.ConfirmLink),
ConfirmMode = formatting.ConfirmMode,
ConfirmText = transform(formatting.ConfirmText),
ImageLarge = transform(formatting.ImageLarge),
Expand Down
2 changes: 2 additions & 0 deletions backend/src/Notifo.Domain/NotificationFormatting{TText}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public sealed class NotificationFormatting<TText> where TText : class

public TText? Body { get; set; }

public TText? ConfirmLink { get; set; }

public TText? ConfirmText { get; set; }

public TText? ImageSmall { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public sealed class NotificationFormattingDto
/// </summary>
public LocalizedText? Body { get; set; }

/// <summary>
/// The optional confirm link with one entry per language.
/// </summary>
public LocalizedText? ConfirmLink { get; set; }

/// <summary>
/// The optional confirm text with one entry per language.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ public abstract class UserNotificationBaseDto
/// </summary>
public string? LinkText { get; set; }

/// <summary>
/// The link after the confirm button.
/// </summary>
public string? ConfirmLink { get; set; }

/// <summary>
/// The text for the confirm button.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Notifo.Domain;
using Notifo.Domain.Apps;
using Notifo.Domain.UserNotifications;
using Notifo.Infrastructure;

namespace Notifo.Areas.Api.Controllers.Tracking;

Expand Down Expand Up @@ -76,22 +77,29 @@ public async Task<IActionResult> Confirm(string id, [FromQuery] TrackingQueryDto
return View();
}

var app = await appStore.GetCachedAsync(notification.AppId, HttpContext.RequestAborted);

if (app?.ConfirmUrl != null && Uri.IsWellFormedUriString(app.ConfirmUrl, UriKind.Absolute))
static bool TryGetLink(string? url, string id, out IActionResult result)
{
var url = app.ConfirmUrl!;
result = null!;

if (url.Contains('?', StringComparison.OrdinalIgnoreCase))
{
url += $"&id={id}";
}
else
if (url == null || !Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
url += $"?id={id}";
return false;
}

return Redirect(url);
result = new RedirectResult(url.AppendQueries("id", id));
return true;
}

if (TryGetLink(notification.Formatting.ConfirmLink, id, out var redirect))
{
return redirect;
}

var app = await appStore.GetCachedAsync(notification.AppId, HttpContext.RequestAborted);

if (TryGetLink(app?.ConfirmUrl, id, out redirect))
{
return redirect;
}

return View();
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/app/service/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6283,6 +6283,8 @@ export interface NotificationFormattingDto {
subject: LocalizedText;
/** The optional body with one entry per language. */
body?: LocalizedText | undefined;
/** The optional confirm link with one entry per language. */
confirmlink?: LocalizedText | undefined;
/** The optional confirm text with one entry per language. */
confirmText?: LocalizedText | undefined;
/** The optional small image with one entry per language. */
Expand Down Expand Up @@ -6390,6 +6392,8 @@ export interface UserNotificationBaseDto {
linkUrl?: string | undefined;
/** The link text. */
linkText?: string | undefined;
/** The link after the confirm button. */
confirmLink?: string | undefined;
/** The text for the confirm button. */
confirmText?: string | undefined;
/** The tracking url that needs to be invoked to mark the notification as confirmed. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ export module NotificationsForm {
<Forms.LocalizedText name={`${field}.linkText`} {...props} picker={PICK_TEXT}
label={texts.common.linkText} hints={texts.common.linkTextHints} />

<Forms.LocalizedText name={`${field}.confirmLink`} {...props} picker={PICK_TEXT}
label={texts.common.confirmLink} hints={texts.common.confirmLinkHints} />

<Forms.LocalizedText name={`${field}.confirmText`} {...props} picker={PICK_TEXT}
label={texts.common.confirmText} hints={texts.common.confirmTextHints} />

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/app/texts/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export const EN = {
Inherit: '-',
},
confirm: 'Confirm',
confirmLink: 'Confirm Link',
confirmLinkHints: 'The URL that will be opened after the button has been pressed.',
confirmMode: 'Confirm Mode',
confirmModeHints: 'In Explicit mode a button will be shown to confirm the notification.',
confirmModes: {
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/sdk/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,11 @@ <h3>notifo Demo</h3>
apiUrl: '/',

onNotification: (notification) => {
console.log(JSON.stringify(notification));
console.log(`Received: ${JSON.stringify(notification, 0, 2)}`);
},

onConfirm: (notification) => {
console.log(`Confirmed: ${JSON.stringify(notification, 0, 2)}`);
},

linkTarget: '_blank',
Expand Down
11 changes: 4 additions & 7 deletions frontend/src/sdk/push/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ export module PUSH {
}

const serviceWorker = await registerServiceWorker(config, options);

const simpleConfig = buildConfig(config);
const serviceConfig = buildConfig(config);

if (serviceWorker.active) {
serviceWorker.active.postMessage({ type: 'subscribe', config: simpleConfig });
serviceWorker.active.postMessage({ type: 'subscribe', config: serviceConfig });
}
}

Expand All @@ -34,11 +33,10 @@ export module PUSH {
}

const serviceWorker = await navigator.serviceWorker.ready;

const simpleConfig = buildConfig(config);
const serviceConfig = buildConfig(config);

if (serviceWorker.active) {
serviceWorker.active.postMessage({ type: 'unsubscribe', config: simpleConfig });
serviceWorker.active.postMessage({ type: 'unsubscribe', config: serviceConfig });
}
}

Expand Down Expand Up @@ -108,7 +106,6 @@ async function registerServiceWorker(config: SDKConfig, options?: SubscribeOptio
return await navigator.serviceWorker.ready;
} else {
const serviceWorker = await navigator.serviceWorker.register(config.serviceWorkerUrl);

await serviceWorker.update();

return serviceWorker;
Expand Down
21 changes: 15 additions & 6 deletions frontend/src/sdk/sdk-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,24 @@ import { apiDeleteWebPush, apiPostWebPush, logWarn, NotifoNotificationDto, parse
}

if (event.action === 'confirm') {
if (notification.confirmLink && self.clients.openWindow) {
const promise = self.clients.openWindow(notification.confirmLink);

event.waitUntil(promise);
}

if (notification.confirmUrl) {
const promise = fetch(notification.confirmUrl);

event.waitUntil(promise);
}
} else if (notification.linkUrl && self.clients.openWindow) {
const promise = self.clients.openWindow(notification.linkUrl);
} else {
if (notification.linkUrl && self.clients.openWindow) {
const promise = self.clients.openWindow(notification.linkUrl);

event.notification.close();
event.waitUntil(promise);
event.notification.close();
event.waitUntil(promise);
}
}
});

Expand Down Expand Up @@ -68,7 +76,7 @@ import { apiDeleteWebPush, apiPostWebPush, logWarn, NotifoNotificationDto, parse

self.addEventListener('push', event => {
if (event.data) {
const notification: NotifoNotificationDto = parseShortNotification(event.data.json());
const notification = parseShortNotification(event.data.json());

const promise = showNotification(self, notification);

Expand Down Expand Up @@ -127,8 +135,9 @@ async function showNotification(self: ServiceWorkerGlobalScope, notification: No
options.tag = notification.id;

if (notification.confirmUrl && notification.confirmText && !notification.isConfirmed) {
options.actions = [{ action: 'confirm', title: notification.confirmText }];
options.requireInteraction = true;
options.actions ||= [];
options.actions.push({ action: 'confirm', title: notification.confirmText });
}

if (notification.body) {
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/sdk/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ const instance = {
break;
}
case 'hide-notifications': {
queueJobs.enqueue((() => UI.destroy(args[1])));
queueJobs.enqueue((() => UI.release(args[1])));
break;
}
case 'hide-topic': {
queueJobs.enqueue((() => UI.destroy(args[1])));
queueJobs.enqueue((() => UI.release(args[1])));
break;
}
case 'show-notifications': {
Expand Down Expand Up @@ -77,7 +77,7 @@ const instance = {
return Promise.resolve(false);
}

if (await PUSH.isPending() && !await UI.askForWebPush(queueInit.config)) {
if (await PUSH.isPending() && !await UI.askForWebPush(queueInit.config, args[1])) {
return false;
}

Expand Down
4 changes: 4 additions & 0 deletions frontend/src/sdk/shared/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ export interface NotifoNotificationDto {
// The confirm url.
confirmUrl?: string;

// The confirm link.
confirmLink?: string;

// The confirm text.
confirmText?: string;

Expand Down Expand Up @@ -129,6 +132,7 @@ export function parseShortNotification(value: any): NotifoNotificationDto {
return {
id: value.id,
body: value.nb,
confirmLink: value.cl,
confirmText: value.ct,
confirmUrl: value.cu,
imageLarge: value.il,
Expand Down
Loading

0 comments on commit 0c53d38

Please sign in to comment.