diff --git a/src/components/common/UiLoader.scss b/src/components/common/UiLoader.scss index 2a743164b..cd4b89c9f 100644 --- a/src/components/common/UiLoader.scss +++ b/src/components/common/UiLoader.scss @@ -75,19 +75,16 @@ .theme-dark body.initial & { background-color: #0f0f0f; + background-image: url('../../assets/chat-bg-dark.png'); + background-position: top left; + background-size: 650px; + background-repeat: repeat; } .theme-light body.initial &, body:not(.initial) & { background-image: url('../../assets/chat-bg.jpg'); } - - .theme-dark body.initial & { - background-image: url('../../assets/chat-bg-dark.png'); - background-position: top left; - background-size: 650px; - background-repeat: repeat; - } } html.theme-light body.animation-level-2 &.with-right-column::before { diff --git a/src/components/mediaViewer/MediaViewerContent.tsx b/src/components/mediaViewer/MediaViewerContent.tsx index 04726d3f2..f5cc71a85 100644 --- a/src/components/mediaViewer/MediaViewerContent.tsx +++ b/src/components/mediaViewer/MediaViewerContent.tsx @@ -64,6 +64,9 @@ type StateProps = { message?: ApiMessage; origin?: MediaViewerOrigin; isProtected?: boolean; + volume: number; + isMuted: boolean; + playbackRate: number; }; const ANIMATION_DURATION = 350; @@ -78,10 +81,13 @@ const MediaViewerContent: FC = (props) => { profilePhotoIndex, origin, animationLevel, - onClose, - onFooterClick, isFooterHidden, isProtected, + volume, + playbackRate, + isMuted, + onClose, + onFooterClick, setIsFooterHidden, } = props; /* Content */ @@ -211,6 +217,9 @@ const MediaViewerContent: FC = (props) => { toggleControls={toggleControls} noPlay={!isActive} onClose={onClose} + isMuted={isMuted} + volume={volume} + playbackRate={playbackRate} /> ))} {textParts && ( @@ -236,14 +245,20 @@ export default memo(withGlobal( origin, } = ownProps; + const { + volume, + isMuted, + playbackRate, + } = global.mediaViewer; + if (origin === MediaViewerOrigin.SearchResult) { if (!(chatId && messageId)) { - return {}; + return { volume, isMuted, playbackRate }; } const message = selectChatMessage(global, chatId, messageId); if (!message) { - return {}; + return { volume, isMuted, playbackRate }; } return { @@ -253,6 +268,9 @@ export default memo(withGlobal( origin, message, isProtected: selectIsMessageProtected(global, message), + volume, + isMuted, + playbackRate, }; } @@ -265,11 +283,14 @@ export default memo(withGlobal( avatarOwner: sender, profilePhotoIndex: profilePhotoIndex || 0, origin, + volume, + isMuted, + playbackRate, }; } if (!(chatId && threadId && messageId)) { - return {}; + return { volume, isMuted, playbackRate }; } let message: ApiMessage | undefined; @@ -280,7 +301,7 @@ export default memo(withGlobal( } if (!message) { - return {}; + return { volume, isMuted, playbackRate }; } return { @@ -291,6 +312,9 @@ export default memo(withGlobal( origin, message, isProtected: selectIsMessageProtected(global, message), + volume, + isMuted, + playbackRate, }; }, )(MediaViewerContent)); diff --git a/src/components/mediaViewer/MediaViewerSlides.tsx b/src/components/mediaViewer/MediaViewerSlides.tsx index eeeca038a..3a754bf55 100644 --- a/src/components/mediaViewer/MediaViewerSlides.tsx +++ b/src/components/mediaViewer/MediaViewerSlides.tsx @@ -567,7 +567,10 @@ function checkIfInsideSelector(element: HTMLElement, selector: string) { function checkIfControlTarget(e: TouchEvent | MouseEvent) { const target = e.target as HTMLElement; if (checkIfInsideSelector(target, '.VideoPlayerControls')) { - if (checkIfInsideSelector(target, '.play, .fullscreen')) { + if (checkIfInsideSelector( + target, + '.play, .fullscreen, .volume, .volume-slider, .playback-rate, .playback-rate-menu', + )) { return true; } e.preventDefault(); diff --git a/src/components/mediaViewer/VideoPlayer.tsx b/src/components/mediaViewer/VideoPlayer.tsx index 714132ad5..1cf2681b6 100644 --- a/src/components/mediaViewer/VideoPlayer.tsx +++ b/src/components/mediaViewer/VideoPlayer.tsx @@ -1,6 +1,7 @@ import React, { FC, memo, useCallback, useEffect, useRef, useState, } from '../../lib/teact/teact'; +import { getActions } from '../../global'; import { ApiDimensions } from '../../api/types'; @@ -28,6 +29,9 @@ type OwnProps = { isMediaViewerOpen?: boolean; noPlay?: boolean; areControlsVisible: boolean; + volume: number; + isMuted: boolean; + playbackRate: number; toggleControls: (isVisible: boolean) => void; onClose: (e: React.MouseEvent) => void; }; @@ -43,10 +47,18 @@ const VideoPlayer: FC = ({ fileSize, isMediaViewerOpen, noPlay, + volume, + isMuted, + playbackRate, onClose, toggleControls, areControlsVisible, }) => { + const { + setMediaViewerVolume, + setMediaViewerMuted, + setMediaViewerPlaybackRate, + } = getActions(); // eslint-disable-next-line no-null/no-null const videoRef = useRef(null); const [isPlayed, setIsPlayed] = useState(!IS_TOUCH_ENV || !IS_IOS); @@ -84,6 +96,14 @@ const VideoPlayer: FC = ({ } }, [currentTime]); + useEffect(() => { + videoRef.current!.volume = volume; + }, [volume]); + + useEffect(() => { + videoRef.current!.playbackRate = playbackRate; + }, [playbackRate]); + const togglePlayState = useCallback((e: React.MouseEvent | KeyboardEvent) => { e.stopPropagation(); if (isPlayed) { @@ -129,6 +149,18 @@ const VideoPlayer: FC = ({ videoRef.current!.currentTime = position; }, []); + const handleVolumeChange = useCallback((newVolume: number) => { + setMediaViewerVolume({ volume: newVolume / 100 }); + }, [setMediaViewerVolume]); + + const handleVolumeMuted = useCallback(() => { + setMediaViewerMuted({ isMuted: !isMuted }); + }, [isMuted, setMediaViewerMuted]); + + const handlePlaybackRateChange = useCallback((newPlaybackRate: number) => { + setMediaViewerPlaybackRate({ playbackRate: newPlaybackRate }); + }, [setMediaViewerPlaybackRate]); + useEffect(() => { if (!isMediaViewerOpen) return undefined; const togglePayingStateBySpace = (e: KeyboardEvent) => { @@ -164,7 +196,7 @@ const VideoPlayer: FC = ({ playsInline loop={isGif} // This is to force auto playing on mobiles - muted={isGif} + muted={isGif || isMuted} id="media-viewer-video" style={videoStyle} onPlay={IS_IOS ? () => setIsPlayed(true) : undefined} @@ -198,6 +230,7 @@ const VideoPlayer: FC = ({ = ({ onSeek={handleSeek} onChangeFullscreen={handleFullscreenChange} onPlayPause={togglePlayState} + volume={volume} + playbackRate={playbackRate} + isMuted={isMuted} + onVolumeClick={handleVolumeMuted} + onVolumeChange={handleVolumeChange} + onPlaybackRateChange={handlePlaybackRateChange} /> )} diff --git a/src/components/mediaViewer/VideoPlayerControls.scss b/src/components/mediaViewer/VideoPlayerControls.scss index ae1f44438..de0d2e1d6 100644 --- a/src/components/mediaViewer/VideoPlayerControls.scss +++ b/src/components/mediaViewer/VideoPlayerControls.scss @@ -28,77 +28,67 @@ pointer-events: all; } - &.mobile { - .player-file-size { - position: static; - transform: none; - margin-left: auto; - } - - .fullscreen { - margin-left: 1rem; - } - - .player-time + .fullscreen { - margin-left: auto; - } + .buttons { + display: flex; + align-items: center; + width: 100%; } - .Button { - width: 2.25rem; + .spacer { + flex-grow: 1; + } + + .Button.round { + width: 2rem; padding: 0; margin: 0.25rem; - height: 1.75rem; - @media (max-width: 600px) { - height: 2.25rem; + height: 2rem; + } + + .volume-slider { + margin-bottom: 0; + margin-left: -0.75rem; + padding: 0.5rem 0.5rem 0.5rem 0.5rem; + width: 0; + + --volume-slider-width: 4rem; + --slider-color: #fff; + --color-borders: rgba(255, 255, 255, 0.5); + /* stylelint-disable-next-line plugin/no-low-performance-animation-properties */ + transition: width 0.2s ease-in-out; + + &:hover{ + overflow: hidden; + width: var(--volume-slider-width); + .RangeSlider__input { + opacity: 1; + } + } + + .RangeSlider__input { + margin-bottom: 0; + opacity: 0; + transition: opacity 0.15s ease-in-out; } } - .play i { - line-height: 1.5rem; + .volume:hover + .volume-slider { + overflow: hidden; + width: var(--volume-slider-width); + .RangeSlider__input { + opacity: 1; + } } .player-time { - margin: 0 1rem; white-space: nowrap; } - .fullscreen { - margin-left: auto; - @media (max-width: 600px) { - margin-left: 1rem; - } - i { - line-height: 1.75rem; - @media (max-width: 600px) { - font-size: 1.5rem; - } - } - } - - @media (max-width: 600px) { - .player-time + .fullscreen { - margin-left: auto; - } - } - .player-file-size { - position: absolute; - left: 50%; - transform: translateX(-50%); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - - @media (max-width: 600px) { - position: static; - transform: none; - margin-left: auto; - margin-right: 1rem; - & + .fullscreen { - margin-left: 0; - } - } + margin-left: 0.5rem; } .player-seekline { @@ -155,4 +145,9 @@ } } } + + .playback-rate-menu .bubble { + min-width: 4rem; + margin-right: 4rem; + } } diff --git a/src/components/mediaViewer/VideoPlayerControls.tsx b/src/components/mediaViewer/VideoPlayerControls.tsx index a49f457b3..74ddba171 100644 --- a/src/components/mediaViewer/VideoPlayerControls.tsx +++ b/src/components/mediaViewer/VideoPlayerControls.tsx @@ -1,19 +1,23 @@ import React, { - FC, useEffect, useRef, useCallback, + FC, useEffect, useRef, useCallback, useMemo, } from '../../lib/teact/teact'; import buildClassName from '../../util/buildClassName'; -import { IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment'; +import useFlag from '../../hooks/useFlag'; +import { IS_IOS, IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment'; import { formatMediaDuration } from '../../util/dateFormat'; import formatFileSize from './helpers/formatFileSize'; import useLang from '../../hooks/useLang'; import { captureEvents } from '../../util/captureEvents'; import Button from '../ui/Button'; +import RangeSlider from '../ui/RangeSlider'; +import Menu from '../ui/Menu'; +import MenuItem from '../ui/MenuItem'; import './VideoPlayerControls.scss'; -type IProps = { +type OwnProps = { bufferedProgress: number; currentTime: number; duration: number; @@ -22,9 +26,16 @@ type IProps = { isPlayed: boolean; isFullscreenSupported: boolean; isFullscreen: boolean; - onChangeFullscreen: (e: React.MouseEvent) => void; - onPlayPause: (e: React.MouseEvent) => void; isVisible: boolean; + isBuffered: boolean; + volume: number; + isMuted: boolean; + playbackRate: number; + onChangeFullscreen: (e: React.MouseEvent) => void; + onVolumeClick: () => void; + onVolumeChange: (volume: number) => void; + onPlaybackRateChange: (playbackRate: number) => void; + onPlayPause: (e: React.MouseEvent) => void; setVisibility: (isVisible: boolean) => void; onSeek: (position: number) => void; }; @@ -33,9 +44,16 @@ const stopEvent = (e: React.MouseEvent) => { e.stopPropagation(); }; +const PLAYBACK_RATES = [ + 0.5, + 1, + 1.5, + 2, +]; + const HIDE_CONTROLS_TIMEOUT_MS = 1500; -const VideoPlayerControls: FC = ({ +const VideoPlayerControls: FC = ({ bufferedProgress, currentTime, duration, @@ -44,12 +62,20 @@ const VideoPlayerControls: FC = ({ isPlayed, isFullscreenSupported, isFullscreen, - onChangeFullscreen, - onPlayPause, isVisible, + isBuffered, + volume, + isMuted, + playbackRate, + onChangeFullscreen, + onVolumeClick, + onVolumeChange, + onPlaybackRateChange, + onPlayPause, setVisibility, onSeek, }) => { + const [isPlaybackMenuOpen, openPlaybackMenu, closePlaybackMenu] = useFlag(); // eslint-disable-next-line no-null/no-null const seekerRef = useRef(null); const isSeekingRef = useRef(false); @@ -57,7 +83,7 @@ const VideoPlayerControls: FC = ({ useEffect(() => { let timeout: number | undefined; - if (!isVisible || !isPlayed || isSeeking) { + if (!isVisible || !isPlayed || isSeeking || isPlaybackMenuOpen) { if (timeout) window.clearTimeout(timeout); return undefined; } @@ -67,7 +93,7 @@ const VideoPlayerControls: FC = ({ return () => { if (timeout) window.clearTimeout(timeout); }; - }, [isPlayed, isVisible, isSeeking, setVisibility]); + }, [isPlayed, isVisible, isSeeking, setVisibility, isPlaybackMenuOpen]); useEffect(() => { if (isVisible) { @@ -80,6 +106,12 @@ const VideoPlayerControls: FC = ({ }; }, [isVisible]); + useEffect(() => { + if (!isVisible) { + closePlaybackMenu(); + } + }, [closePlaybackMenu, isVisible]); + const lang = useLang(); const handleSeek = useCallback((e: MouseEvent | TouchEvent) => { @@ -112,35 +144,84 @@ const VideoPlayerControls: FC = ({ }); }, [isVisible, handleStartSeek, handleSeek, handleStopSeek]); + const volumeIcon = useMemo(() => { + if (volume === 0 || isMuted) return 'icon-muted'; + if (volume < 0.3) return 'icon-volume-1'; + if (volume < 0.6) return 'icon-volume-2'; + return 'icon-volume-3'; + }, [volume, isMuted]); + return (
{renderSeekLine(currentTime, duration, bufferedProgress, seekerRef)} - - {renderTime(currentTime, duration)} - {bufferedProgress < 1 && renderFileSize(bufferedProgress, fileSize)} - {isFullscreenSupported && ( +
+ - )} + {!IS_IOS && ( + + )} + {renderTime(currentTime, duration)} + {!isBuffered && renderFileSize(bufferedProgress, fileSize)} +
+ + {isFullscreenSupported && ( + + )} +
+ + {PLAYBACK_RATES.map((rate) => ( + onPlaybackRateChange(rate)}> + {`${rate}x`} + + ))} +
); }; diff --git a/src/components/middle/AudioPlayer.tsx b/src/components/middle/AudioPlayer.tsx index 3631b3e41..296f51bf9 100644 --- a/src/components/middle/AudioPlayer.tsx +++ b/src/components/middle/AudioPlayer.tsx @@ -216,7 +216,7 @@ const AudioPlayer: FC = ({ <>
- +
)} diff --git a/src/components/ui/RangeSlider.scss b/src/components/ui/RangeSlider.scss index 88cebf9f9..72a5dc0ad 100644 --- a/src/components/ui/RangeSlider.scss +++ b/src/components/ui/RangeSlider.scss @@ -73,6 +73,18 @@ pointer-events: none; } + &.bold { + .slider-main::before { + top: 0.25rem; + height: 0.25rem; + } + + .slider-fill-track { + top: 0.25rem; + height: 0.25rem; + } + } + // Reset range input browser styles @include reset-range(); diff --git a/src/components/ui/RangeSlider.tsx b/src/components/ui/RangeSlider.tsx index 4729371e4..e3e9c1655 100644 --- a/src/components/ui/RangeSlider.tsx +++ b/src/components/ui/RangeSlider.tsx @@ -15,8 +15,10 @@ type OwnProps = { step?: number; label?: string; value: number; - renderValue?: (value: number) => string; disabled?: boolean; + bold?: boolean; + className?: string; + renderValue?: (value: number) => string; onChange: (value: number) => void; }; @@ -27,8 +29,10 @@ const RangeSlider: FC = ({ step = 1, label, value, - renderValue, disabled, + bold, + className, + renderValue, onChange, }) => { const lang = useLang(); @@ -36,9 +40,11 @@ const RangeSlider: FC = ({ onChange(Number(event.currentTarget.value)); }, [onChange]); - const className = buildClassName( + const mainClassName = buildClassName( + className, 'RangeSlider', disabled && 'disabled', + bold && 'bold', ); const trackWidth = useMemo(() => { @@ -51,7 +57,7 @@ const RangeSlider: FC = ({ }, [options, value, max, min, step]); return ( -
+
{label && (
{label} @@ -71,6 +77,7 @@ const RangeSlider: FC = ({ value={value} step={step} type="range" + className="RangeSlider__input" onChange={handleChange} /> {options && ( diff --git a/src/global/actions/all.ts b/src/global/actions/all.ts index 380749b86..b4560d3c7 100644 --- a/src/global/actions/all.ts +++ b/src/global/actions/all.ts @@ -9,6 +9,7 @@ import './ui/settings'; import './ui/misc'; import './ui/payments'; import './ui/calls'; +import './ui/mediaViewer'; import './api/initial'; import './api/chats'; diff --git a/src/global/actions/ui/mediaViewer.ts b/src/global/actions/ui/mediaViewer.ts new file mode 100644 index 000000000..c4a594903 --- /dev/null +++ b/src/global/actions/ui/mediaViewer.ts @@ -0,0 +1,78 @@ +import { addActionHandler } from '../../index'; + +addActionHandler('openMediaViewer', (global, actions, payload) => { + const { + chatId, threadId, messageId, avatarOwnerId, profilePhotoIndex, origin, volume, playbackRate, isMuted, + } = payload; + + return { + ...global, + mediaViewer: { + ...global.mediaViewer, + chatId, + threadId, + messageId, + avatarOwnerId, + profilePhotoIndex, + origin, + volume: volume ?? global.mediaViewer.volume, + playbackRate: playbackRate || global.mediaViewer.playbackRate, + isMuted: isMuted || global.mediaViewer.isMuted, + }, + forwardMessages: {}, + }; +}); + +addActionHandler('closeMediaViewer', (global) => { + const { volume, isMuted, playbackRate } = global.mediaViewer; + return { + ...global, + mediaViewer: { + volume, + isMuted, + playbackRate, + }, + }; +}); + +addActionHandler('setMediaViewerVolume', (global, actions, payload) => { + const { + volume, + } = payload; + + return { + ...global, + mediaViewer: { + ...global.mediaViewer, + volume, + }, + }; +}); + +addActionHandler('setMediaViewerPlaybackRate', (global, actions, payload) => { + const { + playbackRate, + } = payload; + + return { + ...global, + mediaViewer: { + ...global.mediaViewer, + playbackRate, + }, + }; +}); + +addActionHandler('setMediaViewerMuted', (global, actions, payload) => { + const { + isMuted, + } = payload; + + return { + ...global, + mediaViewer: { + ...global.mediaViewer, + isMuted, + }, + }; +}); diff --git a/src/global/actions/ui/messages.ts b/src/global/actions/ui/messages.ts index 2e2224628..868e97b55 100644 --- a/src/global/actions/ui/messages.ts +++ b/src/global/actions/ui/messages.ts @@ -156,36 +156,10 @@ addActionHandler('replyToNextMessage', (global, actions, payload) => { }); }); -addActionHandler('openMediaViewer', (global, actions, payload) => { - const { - chatId, threadId, messageId, avatarOwnerId, profilePhotoIndex, origin, - } = payload!; - - return { - ...global, - mediaViewer: { - chatId, - threadId, - messageId, - avatarOwnerId, - profilePhotoIndex, - origin, - }, - forwardMessages: {}, - }; -}); - -addActionHandler('closeMediaViewer', (global) => { - return { - ...global, - mediaViewer: {}, - }; -}); - addActionHandler('openAudioPlayer', (global, actions, payload) => { const { chatId, threadId, messageId, origin, volume, playbackRate, isMuted, - } = payload!; + } = payload; return { ...global, @@ -204,7 +178,7 @@ addActionHandler('openAudioPlayer', (global, actions, payload) => { addActionHandler('setAudioPlayerVolume', (global, actions, payload) => { const { volume, - } = payload!; + } = payload; return { ...global, @@ -218,7 +192,7 @@ addActionHandler('setAudioPlayerVolume', (global, actions, payload) => { addActionHandler('setAudioPlayerPlaybackRate', (global, actions, payload) => { const { playbackRate, - } = payload!; + } = payload; return { ...global, @@ -232,7 +206,7 @@ addActionHandler('setAudioPlayerPlaybackRate', (global, actions, payload) => { addActionHandler('setAudioPlayerMuted', (global, actions, payload) => { const { isMuted, - } = payload!; + } = payload; return { ...global, @@ -246,7 +220,7 @@ addActionHandler('setAudioPlayerMuted', (global, actions, payload) => { addActionHandler('setAudioPlayerOrigin', (global, actions, payload) => { const { origin, - } = payload!; + } = payload; return { ...global, diff --git a/src/global/cache.ts b/src/global/cache.ts index af09c1346..2c8b0ff99 100644 --- a/src/global/cache.ts +++ b/src/global/cache.ts @@ -196,6 +196,14 @@ function migrateCache(cached: GlobalState, initialState: GlobalState) { cached.audioPlayer.playbackRate = DEFAULT_PLAYBACK_RATE; } + if (cached.mediaViewer.volume === undefined) { + cached.mediaViewer.volume = DEFAULT_VOLUME; + } + + if (cached.mediaViewer.playbackRate === undefined) { + cached.mediaViewer.playbackRate = DEFAULT_PLAYBACK_RATE; + } + if (!cached.groupCalls) { cached.groupCalls = initialState.groupCalls; } @@ -246,6 +254,11 @@ function updateCache() { playbackRate: global.audioPlayer.playbackRate, isMuted: global.audioPlayer.isMuted, }, + mediaViewer: { + volume: global.mediaViewer.volume, + playbackRate: global.mediaViewer.playbackRate, + isMuted: global.mediaViewer.isMuted, + }, isChatInfoShown: reduceShowChatInfo(global), users: reduceUsers(global), chats: reduceChats(global), diff --git a/src/global/initialState.ts b/src/global/initialState.ts index 8d0522c58..50b095bfd 100644 --- a/src/global/initialState.ts +++ b/src/global/initialState.ts @@ -116,7 +116,11 @@ export const INITIAL_STATE: GlobalState = { topInlineBots: {}, - mediaViewer: {}, + mediaViewer: { + volume: DEFAULT_VOLUME, + playbackRate: DEFAULT_PLAYBACK_RATE, + isMuted: false, + }, audioPlayer: { volume: DEFAULT_VOLUME, diff --git a/src/global/types.ts b/src/global/types.ts index f0facf4cf..c1fc9541b 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -374,6 +374,9 @@ export type GlobalState = { avatarOwnerId?: string; profilePhotoIndex?: number; origin?: MediaViewerOrigin; + volume: number; + playbackRate: number; + isMuted: boolean; }; audioPlayer: { @@ -521,12 +524,60 @@ export interface ActionPayloads { type?: MessageListType; shouldReplaceHistory?: boolean; }; + + // Messages setEditingDraft: { text?: ApiFormattedText; chatId: string; threadId: number; type: MessageListType; }; + + // Media Viewer & Audio Player + openMediaViewer: { + chatId?: string; + threadId?: number; + messageId?: number; + avatarOwnerId?: string; + profilePhotoIndex?: number; + origin?: MediaViewerOrigin; + volume?: number; + playbackRate?: number; + isMuted?: boolean; + }; + closeMediaViewer: {}; + setMediaViewerVolume: { + volume: number; + }; + setMediaViewerPlaybackRate: { + playbackRate: number; + }; + setMediaViewerMuted: { + isMuted: boolean; + }; + + openAudioPlayer: { + chatId: string; + threadId?: number; + messageId: number; + origin?: AudioOrigin; + volume?: number; + playbackRate?: number; + isMuted?: boolean; + }; + closeAudioPlayer: {}; + setAudioPlayerVolume: { + volume: number; + }; + setAudioPlayerPlaybackRate: { + playbackRate: number; + }; + setAudioPlayerMuted: { + isMuted: boolean; + }; + setAudioPlayerOrigin: { + origin: AudioOrigin; + }; } export type NonTypedActionNames = ( @@ -613,10 +664,6 @@ export type NonTypedActionNames = ( // bots 'clickInlineButton' | 'sendBotCommand' | 'loadTopInlineBots' | 'queryInlineBot' | 'sendInlineBotResult' | 'resetInlineBot' | 'restartBot' | 'startBot' | - // media viewer & audio player - 'openMediaViewer' | 'closeMediaViewer' | - 'openAudioPlayer' | 'setAudioPlayerVolume' | 'setAudioPlayerPlaybackRate' | - 'setAudioPlayerMuted' | 'setAudioPlayerOrigin' | 'closeAudioPlayer' | // misc 'openPollModal' | 'closePollModal' | 'loadWebPagePreview' | 'clearWebPagePreview' | 'loadWallpapers' | 'uploadWallpaper' |