Support sounds
parent
09b128f27a
commit
dc7ca6e405
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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;
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue