Support sounds

This commit is contained in:
Philipp Heckel 2022-03-06 00:02:27 -05:00
parent 09b128f27a
commit dc7ca6e405
13 changed files with 73 additions and 16 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,8 +1,8 @@
import {formatMessage, formatTitleWithDefault, openUrl, topicShortUrl} from "./utils";
import {formatMessage, formatTitleWithDefault, openUrl, playSound, topicShortUrl} from "./utils";
import prefs from "./Prefs";
import subscriptionManager from "./SubscriptionManager";
class NotificationManager {
class Notifier {
async notify(subscriptionId, notification, onClickFallback) {
const subscription = await subscriptionManager.get(subscriptionId);
const shouldNotify = await this.shouldNotify(subscription, notification);
@ -13,7 +13,8 @@ class NotificationManager {
const message = formatMessage(notification);
const title = formatTitleWithDefault(notification, shortUrl);
console.log(`[NotificationManager, ${shortUrl}] Displaying notification ${notification.id}: ${message}`);
// Show notification
console.log(`[Notifier, ${shortUrl}] Displaying notification ${notification.id}: ${message}`);
const n = new Notification(title, {
body: message,
icon: '/static/img/favicon.png'
@ -23,6 +24,17 @@ class NotificationManager {
} else {
n.onclick = onClickFallback;
}
// Play sound
const sound = await prefs.sound();
if (sound && sound !== "none") {
try {
await playSound(sound);
} catch (e) {
console.log(`[Notifier, ${shortUrl}] Error playing audio`, e);
// FIXME show no sound allowed popup
}
}
}
granted() {
@ -48,5 +60,5 @@ class NotificationManager {
}
}
const notificationManager = new NotificationManager();
export default notificationManager;
const notifier = new Notifier();
export default notifier;

View File

@ -1,13 +1,13 @@
import db from "./db";
class Prefs {
async setSelectedSubscriptionId(selectedSubscriptionId) {
db.prefs.put({key: 'selectedSubscriptionId', value: selectedSubscriptionId});
async setSound(sound) {
db.prefs.put({key: 'sound', value: sound.toString()});
}
async selectedSubscriptionId() {
const selectedSubscriptionId = await db.prefs.get('selectedSubscriptionId');
return (selectedSubscriptionId) ? selectedSubscriptionId.value : "";
async sound() {
const sound = await db.prefs.get('sound');
return (sound) ? sound.value : "mixkit-correct-answer-tone";
}
async setMinPriority(minPriority) {

View File

@ -41,7 +41,7 @@ class SubscriptionManager {
if (exists) {
return false;
}
await db.notifications.add({ ...notification, subscriptionId });
await db.notifications.add({ ...notification, subscriptionId }); // FIXME consider put() for double tab
await db.subscriptions.update(subscriptionId, {
last: notification.id
});

View File

@ -121,6 +121,11 @@ export const subscriptionRoute = (subscription) => {
return `/${subscription.topic}`;
}
export const playSound = async (sound) => {
const audio = new Audio(`/static/sounds/${sound}.mp3`);
return audio.play();
};
// From: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
export async function* fetchLinesIterator(fileURL, headers) {
const utf8Decoder = new TextDecoder('utf-8');

View File

@ -9,7 +9,7 @@ import theme from "./theme";
import connectionManager from "../app/ConnectionManager";
import Navigation from "./Navigation";
import ActionBar from "./ActionBar";
import notificationManager from "../app/NotificationManager";
import notifier from "../app/Notifier";
import NoTopics from "./NoTopics";
import Preferences from "./Preferences";
import {useLiveQuery} from "dexie-react-hooks";
@ -26,6 +26,11 @@ import {subscriptionRoute} from "../app/utils";
// TODO sound
// TODO "copy url" toast
// TODO "copy link url" button
// TODO races when two tabs are open
// TODO sound mentions
// https://notificationsounds.com/message-tones/pristine-609
// https://notificationsounds.com/message-tones/juntos-607
// https://notificationsounds.com/notification-sounds/beep-472
const App = () => {
return (
@ -40,7 +45,7 @@ const App = () => {
const Root = () => {
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
const [notificationsGranted, setNotificationsGranted] = useState(notificationManager.granted());
const [notificationsGranted, setNotificationsGranted] = useState(notifier.granted());
const navigate = useNavigate();
const location = useLocation();
const users = useLiveQuery(() => userManager.all());
@ -54,7 +59,7 @@ const Root = () => {
};
const handleRequestPermission = () => {
notificationManager.maybeRequestPermission(granted => setNotificationsGranted(granted));
notifier.maybeRequestPermission(granted => setNotificationsGranted(granted));
};
useEffect(() => {
@ -68,7 +73,7 @@ const Root = () => {
const added = await subscriptionManager.addNotification(subscriptionId, notification);
if (added) {
const defaultClickAction = (subscription) => navigate(subscriptionRoute(subscription)); // FIXME
await notificationManager.notify(subscriptionId, notification, defaultClickAction)
await notifier.notify(subscriptionId, notification, defaultClickAction)
}
} catch (e) {
console.error(`[App] Error handling notification`, e);

View File

@ -19,6 +19,7 @@ import {Paragraph} from "./styles";
import EditIcon from '@mui/icons-material/Edit';
import CloseIcon from "@mui/icons-material/Close";
import IconButton from "@mui/material/IconButton";
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import Container from "@mui/material/Container";
import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem";
@ -31,6 +32,7 @@ import DialogTitle from "@mui/material/DialogTitle";
import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions";
import userManager from "../app/UserManager";
import {playSound} from "../app/utils";
const Preferences = () => {
return (
@ -50,6 +52,7 @@ const Notifications = () => {
Notifications
</Typography>
<PrefGroup>
<Sound/>
<MinPriority/>
<DeleteAfter/>
</PrefGroup>
@ -57,8 +60,40 @@ const Notifications = () => {
);
};
const Sound = () => {
const sound = useLiveQuery(async () => prefs.sound());
const handleChange = async (ev) => {
await prefs.setSound(ev.target.value);
}
if (!sound) {
return null; // While loading
}
return (
<Pref title="Notification sound">
<div style={{ display: 'flex', width: '100%' }}>
<FormControl fullWidth variant="standard" sx={{ margin: 1 }}>
<Select value={sound} onChange={handleChange}>
<MenuItem value={"none"}>No sound</MenuItem>
<MenuItem value={"mixkit-correct-answer-tone"}>Ding</MenuItem>
<MenuItem value={"juntos"}>Juntos</MenuItem>
<MenuItem value={"pristine"}>Pristine</MenuItem>
<MenuItem value={"mixkit-software-interface-start"}>Dadum</MenuItem>
<MenuItem value={"mixkit-message-pop-alert"}>Pop</MenuItem>
<MenuItem value={"mixkit-long-pop"}>Pop swoosh</MenuItem>
<MenuItem value={"beep"}>Beep</MenuItem>
</Select>
</FormControl>
<IconButton onClick={() => playSound(sound)} disabled={sound === "none"}>
<PlayArrowIcon />
</IconButton>
</div>
</Pref>
)
};
const MinPriority = () => {
const minPriority = useLiveQuery(() => prefs.minPriority());
const minPriority = useLiveQuery(async () => prefs.minPriority());
const handleChange = async (ev) => {
await prefs.setMinPriority(ev.target.value);
}