Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ConcordProvider } from './context/ConcordContext'
import { Menu } from './components/Menu/Menu'
import { Explorer, Notifications, Settings, TimelinePage, EntityPage, MessagePage, ListPage, Devtool } from './pages'

import useSound from 'use-sound'
import { useGuardedSound } from './hooks/useGuardedSound'
import { MobileMenu } from './components/Menu/MobileMenu'
import { useClient } from './context/ClientContext'
import { GlobalActionsProvider } from './context/GlobalActions'
Expand Down Expand Up @@ -322,7 +322,10 @@ function App(): JSX.Element {
})
}, [client])

const [playNotification] = useSound(sound.notification, { volume: sound.volume / 100, format: UseSoundFormats })
const [playNotification] = useGuardedSound(sound.notification, {
volume: sound.volume / 100,
format: UseSoundFormats
})
const playNotificationRef = useRef(playNotification)
useEffect(() => {
playNotificationRef.current = playNotification
Expand Down
4 changes: 2 additions & 2 deletions app/src/components/RealtimeTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { ErrorBoundary, type FallbackProps } from 'react-error-boundary'
import HeartBrokenIcon from '@mui/icons-material/HeartBroken'
import type { TimelineReader } from '@concrnt/client'
import { useRefWithForceUpdate } from '../hooks/useRefWithForceUpdate'
import useSound from 'use-sound'
import { useGuardedSound } from '../hooks/useGuardedSound'
import { usePreference } from '../context/PreferenceContext'
import { VList, type VListHandle } from 'virtua'
import { useClient } from '../context/ClientContext'
Expand Down Expand Up @@ -87,7 +87,7 @@ const timeline = forwardRef((props: RealtimeTimelineProps, ref: ForwardedRef<VLi

const [newArrivals, setNewArrivals] = useState<NewArrivalIcons[]>([])

const [playBubble] = useSound(sound.post, {
const [playBubble] = useGuardedSound(sound.post, {
volume: sound.volume / 100,
interrupt: false,
format: UseSoundFormats
Expand Down
17 changes: 16 additions & 1 deletion app/src/components/Settings/Theme.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Box,
Button,
Divider,
IconButton,
ListItemIcon,
Expand All @@ -10,6 +11,7 @@ import {
Typography,
useTheme
} from '@mui/material'
import { useSnackbar } from 'notistack'
import { ThemeCreator } from '../ThemeCreator'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
Expand All @@ -33,6 +35,7 @@ export const ThemeSettings = (): JSX.Element => {
const theme = useTheme<ConcurrentTheme>()
const { t } = useTranslation('', { keyPrefix: 'ui' })
const editorModal = useEditorModal()
const { enqueueSnackbar } = useSnackbar()

const previewTheme: Record<string, ConcurrentTheme> = useMemo(
() => Object.fromEntries(Object.keys(Themes).map((e) => [e, loadConcurrentTheme(e)])),
Expand Down Expand Up @@ -95,7 +98,19 @@ export const ThemeSettings = (): JSX.Element => {
</Paper>
</Box>
<Box display="flex" flexDirection="column" gap={1}>
<Typography variant="h3">Select Theme:</Typography>
<Box display="flex" justifyContent="space-between" alignItems="center" flexWrap="wrap" gap={1}>
<Typography variant="h3">Select Theme:</Typography>
<Button
variant="outlined"
startIcon={<DataObjectIcon />}
onClick={() => {
window.navigator.clipboard.writeText(JSON.stringify(customThemes))
enqueueSnackbar('全カスタムテーマをコピーしました')
}}
>
全部コピー
</Button>
</Box>
<Box
sx={{
display: { xs: 'flex', md: 'grid' },
Expand Down
19 changes: 19 additions & 0 deletions app/src/hooks/useGuardedSound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import useSound from 'use-sound'
import { useCallback } from 'react'
import { type HookOptions, type PlayOptions, type ReturnedValue } from 'use-sound/dist/types'

// バックグラウンドタブではAudioContextがsuspendされ、howlerがplay要求を
// 内部キューに溜めて復帰時に一斉再生してしまうため、hidden中は再生要求自体を捨てる
export function useGuardedSound(src: string | string[], options?: HookOptions): ReturnedValue {
const [play, exposed] = useSound(src, options)

const guardedPlay = useCallback(
(opts?: PlayOptions): void => {
if (document.hidden) return
play(opts)
},
[play]
)

return [guardedPlay, exposed]
}
Loading