feat: parse xml

This commit is contained in:
Zamitto 2024-10-30 15:25:11 -03:00
parent 119af47d77
commit fd80b85786
4 changed files with 117 additions and 56 deletions

View file

@ -4,13 +4,13 @@ import { parseICO } from "icojs";
import trayIcon from "@resources/tray-icon.png?asset";
import { Game } from "@main/entity";
import { gameRepository, userPreferencesRepository } from "@main/repository";
import { toXmlString } from "powertoast";
import fs from "node:fs";
import axios from "axios";
import path from "node:path";
import sound from "sound-play";
import { achievementSoundPath } from "@main/constants";
import icon from "@resources/icon.png?asset";
import { NotificationOptions, toXmlString } from "./xml";
const getGameIconNativeImage = async (gameId: number) => {
try {
@ -101,7 +101,7 @@ export const publishCombinedNewAchievementNotification = async (
? await downloadImage(achievementIcon)
: icon;
new Notification({
const options: NotificationOptions = {
title: "New achievement unlocked",
body: t("new_achievements_unlocked", {
ns: "achievement",
@ -110,16 +110,11 @@ export const publishCombinedNewAchievementNotification = async (
}),
icon: iconPath,
silent: true,
toastXml: toXmlString({
title: "New achievement unlocked",
message: t("new_achievements_unlocked", {
ns: "achievement",
gameCount,
achievementCount,
}),
icon: iconPath,
silent: true,
}),
};
new Notification({
...options,
toastXml: toXmlString(options),
}).show();
sound.play(achievementSoundPath);
@ -133,24 +128,22 @@ export const publishNewAchievementNotification = async (achievement: {
}) => {
const iconPath = await downloadImage(achievement.achievementIcon);
new Notification({
const options: NotificationOptions = {
title: "New achievement unlocked",
body: achievement.displayName,
icon: iconPath,
silent: true,
toastXml: toXmlString({
title: "New achievement unlocked",
message: achievement.displayName,
icon: iconPath,
silent: true,
progress: {
value: Math.round(
(achievement.unlockedAchievementCount * 100) /
achievement.totalAchievementCount
),
valueOverride: `${achievement.unlockedAchievementCount}/${achievement.totalAchievementCount} achievements`,
},
}),
progress: {
value:
achievement.unlockedAchievementCount /
achievement.totalAchievementCount,
valueOverride: `${achievement.unlockedAchievementCount}/${achievement.totalAchievementCount} achievements`,
},
};
new Notification({
...options,
toastXml: toXmlString(options),
}).show();
sound.play(achievementSoundPath);

View file

@ -0,0 +1,98 @@
export interface NotificationOptions {
title: string;
body?: string;
icon: string;
duration?: "short" | "long";
silent?: boolean;
progress?: {
title?: string;
status?: string;
value: number;
valueOverride: string;
};
}
function escape(string: string) {
return string.replace(/[<>&'"]/g, (match) => {
switch (match) {
case "<":
return "&lt;";
case ">":
return "&gt;";
case "&":
return "&amp;";
case "'":
return "&apos;";
case '"':
return "&quot;";
default:
return "";
}
});
}
function addAttributeOrTrim(name, value) {
return value ? `${name}="${value}" ` : "";
}
const Activation = {
types: [
"protocol",
"background",
"foreground",
"system", //system call such as alarm (snooze/dismiss), also used by Notification Visualizer
],
behavior: ["default", "pendingUpdate"],
};
const Scenarios = [
"default",
"alarm",
"reminder",
"incomingCall",
"urgent", //win10/11 22h2
];
export function toXmlString(options: NotificationOptions) {
let template =
"<toast " +
`displayTimestamp="${new Date().toISOString()}" ` +
`scenario="${Scenarios[0]}" ` +
`duration="${options.duration ?? "short"}" ` +
`activationType="${Activation.types[0]}" ` +
">";
//Visual
template += `<visual><binding template="ToastGeneric">`;
if (options.icon)
template += `<image placement="appLogoOverride" src="${options.icon}" hint-crop="none"/>`;
template +=
`<text><![CDATA[${options.title}]]></text>` +
`<text><![CDATA[${options.body}]]></text>`;
//Progress bar
if (options.progress) {
template +=
"<progress " +
`value="${options.progress.value}" ` +
`status="" ` +
addAttributeOrTrim("title", escape(options.progress.title || "")) +
addAttributeOrTrim(
"valueStringOverride",
escape(options.progress.valueOverride)
) +
"/>";
}
template += "</binding></visual>";
//Actions
template += "<actions>";
template += "</actions>";
//Audio
template += "<audio " + `silent="true" ` + `loop="false" ` + "/>";
//EOF
template += "</toast>";
return template;
}