import { LitElement, html, css, svg } from "lit";
import { customElement, property } from "lit/decorators.js";
@customElement("ritzy-player")
export class HelloWorld extends LitElement {
@property({ type: String }) clientName = "Ritzy";
@property({ type: String }) appId = "";
@property({ type: String }) selector = "";
// @property({ type: String }) lang = "en-US";
// TODO: add meta tag prop. here for extracting the language from
// eg.
@property({ type: String }) lang = navigator.language ?? "en-US";
@property({ type: String }) text = "";
@property({ type: Boolean }) initialPlay = true;
@property({ type: Boolean }) playing = false;
@property({ type: Number }) startTimeInMiliseconds = 0;
@property({ type: Number }) endTimeInMiliseconds = 0;
@property({ type: Object }) analyticsData = {};
static styles = css`
svg {
width: 25px;
height: 25px;
cursor: pointer;
}
`;
private _isSendBeaconSupported() {
return "sendBeacon" in navigator;
}
private _isAppIdProvided() {
if (!this.appId || !Boolean(this.appId) || this.appId === "") {
console.log("Please define your app id.");
return false;
}
return true;
}
private _isSelectorDefined() {
if (this.selector === "") {
console.log("Please define your HTML wrapper selector, eg. '#ritzy'");
return false;
}
return true;
}
private svgPlayButton = svg`
`;
private svgPauseButton = svg`
`;
connectedCallback() {
super.connectedCallback();
console.log(`appId: ${this.appId}`)
document?.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden" && this._isAppIdProvided()) {
try {
this._isSendBeaconSupported() && this._sendAnalytics();
} catch (err) {
throw new Error("An error occured while sending analytics data.");
}
}
});
}
private _play() {
if (!this._isSelectorDefined()) {
return;
}
const allElements = document.querySelectorAll(this.selector);
this.text += Array.from(allElements).reduce(
(prev, current) => (prev !== "" ? ", " : "") + current.textContent + ".",
""
);
if ("speechSynthesis" in window) {
const utterance = new SpeechSynthesisUtterance(this.text);
utterance.lang = this.lang;
utterance.rate = 1;
utterance.onstart = () => {
this.startTimeInMiliseconds = Date.now();
};
utterance.onend = () => {
window.speechSynthesis.cancel();
this.playing = false;
this.initialPlay = true;
this.endTimeInMiliseconds = Math.floor(
(Date.now() - this.startTimeInMiliseconds) / 1000
);
};
if (this.initialPlay) {
window.speechSynthesis.cancel();
window.speechSynthesis.speak(utterance);
this.playing = true;
} else {
window.speechSynthesis.resume();
this.playing = true;
}
this.initialPlay = false;
this.text = "";
}
}
private _pause() {
window.speechSynthesis.pause();
this.playing = false;
}
// TODO: Check if possible to move the logic into an inline web / service worker
private _sendAnalytics() {
const analyticsData = JSON.stringify({
date: new Date(),
url: window.location.href,
listeningTimeInMiliseconds: this.endTimeInMiliseconds,
});
if (this.endTimeInMiliseconds > 0) {
console.log(`send analytics (${this.clientName}): ${analyticsData}`);
if (navigator.sendBeacon) {
navigator.sendBeacon(
`http://localhost:3001/apps/${this.appId}/analytics`,
analyticsData
);
} else {
console.log(
"No Beacon support detected in your browser. Please enable it, or use another browser."
);
}
}
}
render() {
if (
typeof this.appId === "undefined" ||
this.appId === "" ||
!this._isAppIdProvided() ||
!this._isSelectorDefined()
) {
return;
}
return this.playing
? html`${this.svgPauseButton}`
: html`${this.svgPlayButton}`;
}
}