diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index dcb190f25..9c9f9ad39 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -1,5 +1,7 @@ import { getSettings } from 'soapbox/actions/settings'; import messages from 'soapbox/locales/messages'; +import { queryClient } from 'soapbox/queries/client'; +import { play, soundCache } from 'soapbox/utils/sounds'; import { connectStream } from '../stream'; @@ -89,6 +91,10 @@ const connectTimelineStream = ( case 'filters_changed': dispatch(fetchFilters()); break; + case 'chat_message': + queryClient.invalidateQueries(['chats', 'messages', JSON.parse(data.payload).chat_id]); + play(soundCache.chat); + break; case 'pleroma:chat_update': dispatch((dispatch: AppDispatch, getState: () => RootState) => { const chat = JSON.parse(data.payload); diff --git a/app/soapbox/features/chats/components/chat-widget.tsx b/app/soapbox/features/chats/components/chat-widget.tsx index a956eeb20..9f776bc61 100644 --- a/app/soapbox/features/chats/components/chat-widget.tsx +++ b/app/soapbox/features/chats/components/chat-widget.tsx @@ -1,10 +1,22 @@ -import React from 'react'; +import React, { useEffect } from 'react'; +import { connectDirectStream } from 'soapbox/actions/streaming'; import { ChatProvider } from 'soapbox/contexts/chat-context'; +import { useAppDispatch } from 'soapbox/hooks'; import ChatPane from './chat-pane/chat-pane'; const ChatWidget = () => { + const dispatch = useAppDispatch(); + + useEffect(() => { + const disconnect = dispatch(connectDirectStream()); + + return (() => { + disconnect(); + }); + }, []); + return ( diff --git a/app/soapbox/middleware/sounds.ts b/app/soapbox/middleware/sounds.ts index 94ba15313..61b9c2058 100644 --- a/app/soapbox/middleware/sounds.ts +++ b/app/soapbox/middleware/sounds.ts @@ -1,65 +1,22 @@ 'use strict'; +import { AnyAction } from 'redux'; + +import { play, soundCache } from 'soapbox/utils/sounds'; + import type { ThunkMiddleware } from 'redux-thunk'; +import type { Sounds } from 'soapbox/utils/sounds'; -/** Soapbox audio clip. */ -type Sound = { - src: string, - type: string, -} -/** Produce HTML5 audio from sound data. */ -const createAudio = (sources: Sound[]): HTMLAudioElement => { - const audio = new Audio(); - sources.forEach(({ type, src }) => { - const source = document.createElement('source'); - source.type = type; - source.src = src; - audio.appendChild(source); - }); - return audio; -}; - -/** Play HTML5 sound. */ -const play = (audio: HTMLAudioElement): void => { - if (!audio.paused) { - audio.pause(); - if (typeof audio.fastSeek === 'function') { - audio.fastSeek(0); - } else { - audio.currentTime = 0; - } +interface Action extends AnyAction { + meta: { + sound: Sounds } - - audio.play(); -}; +} /** Middleware to play sounds in response to certain Redux actions. */ export default function soundsMiddleware(): ThunkMiddleware { - const soundCache: Record = { - boop: createAudio([ - { - src: require('../../sounds/boop.ogg'), - type: 'audio/ogg', - }, - { - src: require('../../sounds/boop.mp3'), - type: 'audio/mpeg', - }, - ]), - chat: createAudio([ - { - src: require('../../sounds/chat.oga'), - type: 'audio/ogg', - }, - { - src: require('../../sounds/chat.mp3'), - type: 'audio/mpeg', - }, - ]), - }; - - return () => next => action => { + return () => next => (action: Action) => { if (action.meta?.sound && soundCache[action.meta.sound]) { play(soundCache[action.meta.sound]); } diff --git a/app/soapbox/utils/sounds.ts b/app/soapbox/utils/sounds.ts new file mode 100644 index 000000000..40e396080 --- /dev/null +++ b/app/soapbox/utils/sounds.ts @@ -0,0 +1,60 @@ +/** Soapbox audio clip. */ +type Sound = { + src: string, + type: string, +} + +export type Sounds = 'boop' | 'chat' + +/** Produce HTML5 audio from sound data. */ +const createAudio = (sources: Sound[]): HTMLAudioElement => { + const audio = new Audio(); + sources.forEach(({ type, src }) => { + const source = document.createElement('source'); + source.type = type; + source.src = src; + audio.appendChild(source); + }); + return audio; +}; + +/** Play HTML5 sound. */ +const play = (audio: HTMLAudioElement): void => { + if (!audio.paused) { + audio.pause(); + if (typeof audio.fastSeek === 'function') { + audio.fastSeek(0); + } else { + audio.currentTime = 0; + } + } + + console.log('playing'); + + audio.play(); +}; + +const soundCache: Record = { + boop: createAudio([ + { + src: require('../../sounds/boop.ogg'), + type: 'audio/ogg', + }, + { + src: require('../../sounds/boop.mp3'), + type: 'audio/mpeg', + }, + ]), + chat: createAudio([ + { + src: require('../../sounds/chat.oga'), + type: 'audio/ogg', + }, + { + src: require('../../sounds/chat.mp3'), + type: 'audio/mpeg', + }, + ]), +}; + +export { soundCache, play };