From 8f626255065e3f650e3ba8a9fdf0cee8727a1e52 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 24 Jun 2021 14:26:18 +0100 Subject: [PATCH] Settings: Link notification sounds to playSound. Fix: Restore hooks.ts patch, additionally use numbers. --- external/lang | 2 +- src/assets/sounds/Audio.ts | 5 +- .../common/messaging/MessageBox.tsx | 4 ++ src/context/Settings.tsx | 29 ++++++++-- src/context/Voice.tsx | 14 ++++- src/context/index.tsx | 23 ++++---- src/context/revoltjs/Notifications.tsx | 6 +- src/context/revoltjs/hooks.ts | 10 ++-- src/pages/settings/panes/Notifications.tsx | 58 +++++++++---------- src/redux/reducers/settings.ts | 15 ++++- 10 files changed, 104 insertions(+), 62 deletions(-) diff --git a/external/lang b/external/lang index be021b37..5af0f9c8 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit be021b37763b2b0f8f0367b49f9912add845aa21 +Subproject commit 5af0f9c8092382aa9608ec39bf5149194da9161c diff --git a/src/assets/sounds/Audio.ts b/src/assets/sounds/Audio.ts index 37da121e..1168c6c6 100644 --- a/src/assets/sounds/Audio.ts +++ b/src/assets/sounds/Audio.ts @@ -1,14 +1,17 @@ import message from './message.mp3'; +import outbound from './outbound.mp3'; import call_join from './call_join.mp3'; import call_leave from './call_leave.mp3'; const SoundMap: { [key in Sounds]: string } = { message, + outbound, call_join, call_leave } -export type Sounds = 'message' | 'call_join' | 'call_leave'; +export type Sounds = 'message' | 'outbound' | 'call_join' | 'call_leave'; +export const SOUNDS_ARRAY: Sounds[] = [ 'message', 'outbound', 'call_join', 'call_leave' ]; export function playSound(sound: Sounds) { let file = SoundMap[sound]; diff --git a/src/components/common/messaging/MessageBox.tsx b/src/components/common/messaging/MessageBox.tsx index a3c98c1e..86463d3a 100644 --- a/src/components/common/messaging/MessageBox.tsx +++ b/src/components/common/messaging/MessageBox.tsx @@ -23,6 +23,7 @@ import { SingletonMessageRenderer, SMOOTH_SCROLL_ON_RECEIVE } from "../../../lib import ReplyBar from "./bars/ReplyBar"; import FilePreview from './bars/FilePreview'; import AutoComplete, { useAutoComplete } from "../AutoComplete"; +import { SoundContext } from "../../../context/Settings"; type Props = WithDispatcher & { channel: Channel; @@ -59,6 +60,7 @@ function MessageBox({ channel, draft, dispatcher }: Props) { const [ uploadState, setUploadState ] = useState({ type: 'none' }); const [ typing, setTyping ] = useState(false); const [ replies, setReplies ] = useState([]); + const playSound = useContext(SoundContext); const { openScreen } = useIntermediate(); const client = useContext(AppContext); const translate = useTranslation(); @@ -108,6 +110,7 @@ function MessageBox({ channel, draft, dispatcher }: Props) { stopTyping(); setMessage(); setReplies([]); + playSound('outbound'); const nonce = ulid(); dispatcher({ @@ -208,6 +211,7 @@ function MessageBox({ channel, draft, dispatcher }: Props) { setMessage(); setReplies([]); + playSound('outbound'); if (files.length > CAN_UPLOAD_AT_ONCE) { setUploadState({ diff --git a/src/context/Settings.tsx b/src/context/Settings.tsx index f45005ec..92737e41 100644 --- a/src/context/Settings.tsx +++ b/src/context/Settings.tsx @@ -4,28 +4,47 @@ // // Replace references to SettingsContext with connectState in the future // if it does cause problems though. +// +// This now also supports Audio stuff. -import { Settings } from "../redux/reducers/settings"; +import { DEFAULT_SOUNDS, Settings, SoundOptions } from "../redux/reducers/settings"; +import { playSound, Sounds } from "../assets/sounds/Audio"; import { connectState } from "../redux/connector"; +import defaultsDeep from "lodash.defaultsdeep"; import { Children } from "../types/Preact"; import { createContext } from "preact"; +import { useMemo } from "preact/hooks"; export const SettingsContext = createContext({} as any); +export const SoundContext = createContext<(sound: Sounds) => void>({} as any); interface Props { children?: Children, settings: Settings } -function Settings(props: Props) { +function Settings({ settings, children }: Props) { + console.info(settings.notification); + const play = useMemo(() => { + const enabled: SoundOptions = defaultsDeep(settings.notification ?? {}, DEFAULT_SOUNDS); + return (sound: Sounds) => { + console.info('check if we can play sound', enabled[sound]); + if (enabled[sound]) { + playSound(sound); + } + }; + }, [ settings.notification ]); + return ( - - { props.children } + + + { children } + ) } -export default connectState(Settings, state => { +export default connectState>(Settings, state => { return { settings: state.settings } diff --git a/src/context/Voice.tsx b/src/context/Voice.tsx index 30d6bbbe..ac5c4077 100644 --- a/src/context/Voice.tsx +++ b/src/context/Voice.tsx @@ -5,6 +5,7 @@ import { AppContext } from "./revoltjs/RevoltClient"; import type VoiceClient from "../lib/vortex/VoiceClient"; import type { ProduceType, VoiceUser } from "../lib/vortex/Types"; import { useContext, useEffect, useMemo, useRef, useState } from "preact/hooks"; +import { SoundContext } from "./Settings"; export enum VoiceStatus { LOADING = 0, @@ -106,6 +107,7 @@ export default function Voice({ children }: Props) { } catch (error) { console.error(error); setStatus(VoiceStatus.READY); + return; } setStatus(VoiceStatus.CONNECTED); @@ -154,6 +156,8 @@ export default function Voice({ children }: Props) { }, [ client ]); const { forceUpdate } = useForceUpdate(); + const playSound = useContext(SoundContext); + useEffect(() => { if (!client?.supported()) return; @@ -164,8 +168,14 @@ export default function Voice({ children }: Props) { client.on("startProduce", forceUpdate); client.on("stopProduce", forceUpdate); - client.on("userJoined", forceUpdate); - client.on("userLeft", forceUpdate); + client.on("userJoined", () => { + playSound('call_join'); + forceUpdate(); + }); + client.on("userLeft", () => { + playSound('call_leave'); + forceUpdate(); + }); client.on("userStartProduce", forceUpdate); client.on("userStopProduce", forceUpdate); client.on("close", forceUpdate); diff --git a/src/context/index.tsx b/src/context/index.tsx index f600e5b9..6bb0de8f 100644 --- a/src/context/index.tsx +++ b/src/context/index.tsx @@ -4,23 +4,26 @@ import { BrowserRouter as Router } from "react-router-dom"; import Intermediate from './intermediate/Intermediate'; import Client from './revoltjs/RevoltClient'; -import Voice from "./Voice"; +import Settings from "./Settings"; import Locale from "./Locale"; +import Voice from "./Voice"; import Theme from "./Theme"; export default function Context({ children }: { children: Children }) { return ( - - - - - {children} - - - - + + + + + + {children} + + + + + ); diff --git a/src/context/revoltjs/Notifications.tsx b/src/context/revoltjs/Notifications.tsx index 009ba059..7f7fe11f 100644 --- a/src/context/revoltjs/Notifications.tsx +++ b/src/context/revoltjs/Notifications.tsx @@ -1,10 +1,10 @@ import { decodeTime } from "ulid"; +import { SoundContext } from "../Settings"; import { AppContext } from "./RevoltClient"; import { useTranslation } from "../../lib/i18n"; import { Users } from "revolt.js/dist/api/objects"; import { useContext, useEffect } from "preact/hooks"; import { connectState } from "../../redux/connector"; -import { playSound } from "../../assets/sounds/Audio"; import { Message, SYSTEM_USER_ID, User } from "revolt.js"; import { NotificationOptions } from "../../redux/reducers/settings"; import { Route, Switch, useHistory, useParams } from "react-router-dom"; @@ -27,8 +27,6 @@ async function createNotification(title: string, options: globalThis.Notificatio function Notifier(props: Props) { const translate = useTranslation(); const showNotification = props.options?.desktopEnabled ?? false; - // const playIncoming = props.options?.soundEnabled ?? true; - // const playOutgoing = props.options?.outgoingSoundEnabled ?? true; const client = useContext(AppContext); const { guild: guild_id, channel: channel_id } = useParams<{ @@ -36,13 +34,13 @@ function Notifier(props: Props) { channel: string; }>(); const history = useHistory(); + const playSound = useContext(SoundContext); async function message(msg: Message) { if (msg.author === client.user!._id) return; if (msg.channel === channel_id && document.hasFocus()) return; if (client.user?.status?.presence === Users.Presence.Busy) return; - // Sounds.playInbound(); playSound('message'); if (!showNotification) return; diff --git a/src/context/revoltjs/hooks.ts b/src/context/revoltjs/hooks.ts index a6ca7925..bb112951 100644 --- a/src/context/revoltjs/hooks.ts +++ b/src/context/revoltjs/hooks.ts @@ -11,8 +11,9 @@ export interface HookContext { export function useForceUpdate(context?: HookContext): HookContext { const client = useContext(AppContext); if (context) return context; - /*const H = useState(undefined); - var updateState: (_: undefined) => void; + + const H = useState(0); + var updateState: (_: number) => void; if (Array.isArray(H)) { let [, u] = H; updateState = u; @@ -20,9 +21,8 @@ export function useForceUpdate(context?: HookContext): HookContext { console.warn('Failed to construct using useState.'); console.warn(H); updateState = ()=>{}; - }*/ + } - const [, updateState] = useState(0); return { client, forceUpdate: () => updateState(Math.random()) }; } @@ -99,7 +99,7 @@ export function useDMs(context?: HookContext) { return map .toArray() - .filter(x => x.channel_type === 'DirectMessage' || x.channel_type === 'Group' || x.channel_type === 'SavedMessages') as (Channels.GroupChannel | Channels.DirectMessageChannel | Channels.SavedMessagesChannel)[]; + .filter(x => (x.channel_type === 'DirectMessage' && x.active) || x.channel_type === 'Group' || x.channel_type === 'SavedMessages') as (Channels.GroupChannel | Channels.DirectMessageChannel | Channels.SavedMessagesChannel)[]; } export function useUserPermission(id: string, context?: HookContext) { diff --git a/src/pages/settings/panes/Notifications.tsx b/src/pages/settings/panes/Notifications.tsx index 2ba745a6..1dca0cd6 100644 --- a/src/pages/settings/panes/Notifications.tsx +++ b/src/pages/settings/panes/Notifications.tsx @@ -1,19 +1,21 @@ import { Text } from "preact-i18n"; import styles from "./Panes.module.scss"; +import defaultsDeep from "lodash.defaultsdeep"; import Checkbox from "../../../components/ui/Checkbox"; import { connectState } from "../../../redux/connector"; import { WithDispatcher } from "../../../redux/reducers"; +import { SOUNDS_ARRAY } from "../../../assets/sounds/Audio"; import { useContext, useEffect, useState } from "preact/hooks"; import { urlBase64ToUint8Array } from "../../../lib/conversion"; import { AppContext } from "../../../context/revoltjs/RevoltClient"; -import { NotificationOptions } from "../../../redux/reducers/settings"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; +import { DEFAULT_SOUNDS, NotificationOptions, SoundOptions } from "../../../redux/reducers/settings"; interface Props { options?: NotificationOptions; } -export function Component(props: Props & WithDispatcher) { +export function Component({ options, dispatcher }: Props & WithDispatcher) { const client = useContext(AppContext); const { openScreen } = useIntermediate(); const [pushEnabled, setPushEnabled] = useState( @@ -28,6 +30,7 @@ export function Component(props: Props & WithDispatcher) { }); }, []); + const enabledSounds: SoundOptions = defaultsDeep(options?.sounds ?? {}, DEFAULT_SOUNDS); return (

@@ -35,7 +38,7 @@ export function Component(props: Props & WithDispatcher) {

{ if (desktopEnabled) { let permission = await Notification.requestPermission(); @@ -47,7 +50,7 @@ export function Component(props: Props & WithDispatcher) { } } - props.dispatcher({ + dispatcher({ type: "SETTINGS_SET_NOTIFICATION_OPTIONS", options: { desktopEnabled } }); @@ -103,34 +106,25 @@ export function Component(props: Props & WithDispatcher) {

- - props.dispatcher({ - type: "SETTINGS_SET_NOTIFICATION_OPTIONS", - options: { soundEnabled } - }) - } - > - -

- -

-
- - props.dispatcher({ - type: "SETTINGS_SET_NOTIFICATION_OPTIONS", - options: { outgoingSoundEnabled } - }) - } - > - -

- -

-
+ { + SOUNDS_ARRAY.map(key => + + dispatcher({ + type: "SETTINGS_SET_NOTIFICATION_OPTIONS", + options: { + sounds: { + ...options?.sounds, + [key]: enabled + } + } + }) + }> + + + ) + }
); } diff --git a/src/redux/reducers/settings.ts b/src/redux/reducers/settings.ts index ac184383..bcab75c2 100644 --- a/src/redux/reducers/settings.ts +++ b/src/redux/reducers/settings.ts @@ -1,11 +1,22 @@ import { filter } from "."; import { SyncUpdateAction } from "./sync"; +import { Sounds } from "../../assets/sounds/Audio"; import { Theme, ThemeOptions } from "../../context/Theme"; +export type SoundOptions = { + [key in Sounds]?: boolean +} + +export const DEFAULT_SOUNDS: SoundOptions = { + message: true, + outbound: false, + call_join: true, + call_leave: true +}; + export interface NotificationOptions { desktopEnabled?: boolean; - soundEnabled?: boolean; - outgoingSoundEnabled?: boolean; + sounds?: SoundOptions } export type EmojiPacks = "mutant" | "twemoji" | "noto" | "openmoji";