-
{renderText(title || fileName)}
- {showSeekline && renderSeekline(playProgress, bufferedProgress, seekHandlers)}
- {!showSeekline && (
-
-
{renderText(performer || 'Unknown')}
+
{renderText(title || fileName)}
+ {showSeekline && (
+
+
+ {formatMediaDuration(duration * playProgress, duration)}
+
+ {renderSeekline(playProgress, bufferedProgress, seekerRef)}
+
+ )}
+ {!showSeekline && showProgress && (
+
+ {progress ? `${getFileSizeString(audio!.size * progress)} / ` : undefined}{getFileSizeString(audio!.size)}
+
+ )}
+ {!showSeekline && !showProgress && (
+
+ {formatMediaDuration(duration)}
+ •
+ {renderText(performer || 'Unknown')}
{date && (
<>
- {' '}
- •
- {' '}
+ •
{formatMediaDateTime(lang, date * 1000)}
>
)}
)}
-
- {playProgress > 0 ? `${formatMediaDuration(duration * playProgress)} / ` : undefined}
- {formatMediaDuration(duration)}
-
);
}
-function renderVoice(voice: ApiVoice, renderedWaveform: any, isMediaUnread?: boolean) {
+function renderVoice(voice: ApiVoice, renderedWaveform: any, playProgress: number, isMediaUnread?: boolean) {
return (
{renderedWaveform}
- {formatMediaDuration(voice.duration)}
- {isMediaUnread && •}
+ {playProgress === 0 ? formatMediaDuration(voice.duration) : formatMediaDuration(voice.duration * playProgress)}
+ {isMediaUnread && }
);
@@ -379,8 +427,8 @@ function renderWaveform(
voice: ApiVoice,
playProgress = 0,
isOwn = false,
- { handleStartSeek, handleSeek, handleStopSeek }: ISeekMethods,
theme: ISettings['theme'],
+ seekerRef: React.Ref
,
) {
const { waveform, duration } = voice;
@@ -411,9 +459,7 @@ function renderWaveform(
height={height}
className="waveform"
draggable={false}
- onMouseDown={handleStartSeek}
- onMouseMove={handleSeek}
- onMouseUp={handleStopSeek}
+ ref={seekerRef as React.Ref}
/>
);
}
@@ -421,14 +467,12 @@ function renderWaveform(
function renderSeekline(
playProgress: number,
bufferedProgress: number,
- { handleStartSeek, handleSeek, handleStopSeek }: ISeekMethods,
+ seekerRef: React.Ref,
) {
return (
}
>
= ({
}, [focusMessage]);
const handlePlayAudio = useCallback((messageId: number, chatId: number) => {
- openAudioPlayer({ chatId, messageId });
+ openAudioPlayer({ chatId, messageId, origin: AudioOrigin.Search });
}, [openAudioPlayer]);
function renderList() {
@@ -97,7 +97,7 @@ const AudioResults: FC = ({
key={message.id}
theme={theme}
message={message}
- target="searchResult"
+ origin={AudioOrigin.Search}
senderTitle={getSenderName(lang, message, chatsById, usersById)}
date={message.date}
lastSyncTime={lastSyncTime}
diff --git a/src/components/left/search/LeftSearch.scss b/src/components/left/search/LeftSearch.scss
index b10b4e594..6b89e1cf4 100644
--- a/src/components/left/search/LeftSearch.scss
+++ b/src/components/left/search/LeftSearch.scss
@@ -96,10 +96,6 @@
}
.Audio {
- .duration span {
- padding: 0 .25rem;
- }
-
.ProgressSpinner {
margin: -.1875rem 0 0 -.1875rem;
}
diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx
index 86f10a5a7..04e9cd6c0 100644
--- a/src/components/main/Main.tsx
+++ b/src/components/main/Main.tsx
@@ -3,6 +3,7 @@ import React, {
} from '../../lib/teact/teact';
import { getGlobal, withGlobal } from '../../lib/teact/teactn';
+import { AudioOrigin } from '../../types';
import { GlobalActions } from '../../global/types';
import { ApiMessage } from '../../api/types';
import { LangCode } from '../../types';
@@ -55,6 +56,7 @@ type StateProps = {
hasNotifications: boolean;
hasDialogs: boolean;
audioMessage?: ApiMessage;
+ audioOrigin?: AudioOrigin;
safeLinkModalUrl?: string;
isHistoryCalendarOpen: boolean;
shouldSkipHistoryAnimations?: boolean;
@@ -84,6 +86,7 @@ const Main: FC = ({
hasNotifications,
hasDialogs,
audioMessage,
+ audioOrigin,
safeLinkModalUrl,
isHistoryCalendarOpen,
shouldSkipHistoryAnimations,
@@ -242,7 +245,7 @@ const Main: FC = ({
- {audioMessage && }
+ {audioMessage && }
{
- const { chatId: audioChatId, messageId: audioMessageId } = global.audioPlayer;
+ const { chatId: audioChatId, messageId: audioMessageId, origin } = global.audioPlayer;
const audioMessage = audioChatId && audioMessageId
? selectChatMessage(global, audioChatId, audioMessageId)
: undefined;
@@ -292,6 +295,7 @@ export default memo(withGlobal(
hasNotifications: Boolean(global.notifications.length),
hasDialogs: Boolean(global.dialogs.length),
audioMessage,
+ audioOrigin: origin,
safeLinkModalUrl: global.safeLinkModalUrl,
isHistoryCalendarOpen: Boolean(global.historyCalendarSelectedAt),
shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations,
diff --git a/src/components/mediaViewer/VideoPlayer.tsx b/src/components/mediaViewer/VideoPlayer.tsx
index 9ead5304f..215f88364 100644
--- a/src/components/mediaViewer/VideoPlayer.tsx
+++ b/src/components/mediaViewer/VideoPlayer.tsx
@@ -118,10 +118,8 @@ const VideoPlayer: FC = ({
}
}, [exitFullscreen, isFullscreen, setFullscreen]);
- const handleSeek = useCallback((e: React.ChangeEvent) => {
- e.stopPropagation();
-
- videoRef.current!.currentTime = (Number(e.target.value) * videoRef.current!.duration) / 100;
+ const handleSeek = useCallback((position: number) => {
+ videoRef.current!.currentTime = position;
}, []);
const toggleControls = useCallback((e: React.MouseEvent) => {
diff --git a/src/components/mediaViewer/VideoPlayerControls.scss b/src/components/mediaViewer/VideoPlayerControls.scss
index 54e6e1dab..437934c08 100644
--- a/src/components/mediaViewer/VideoPlayerControls.scss
+++ b/src/components/mediaViewer/VideoPlayerControls.scss
@@ -101,7 +101,8 @@
right: 1rem;
top: 0;
height: 1rem;
-
+ touch-action: none;
+ cursor: pointer;
&-track {
position: absolute;
@@ -143,18 +144,5 @@
transform: translate(.325rem, -50%);
}
}
-
- &-input {
- width: 100%;
- opacity: 0;
- margin: 0;
- padding: 0;
- cursor: pointer;
- overflow: hidden;
-
- &::-webkit-slider-thumb {
- margin-top: -2rem;
- }
- }
}
}
diff --git a/src/components/mediaViewer/VideoPlayerControls.tsx b/src/components/mediaViewer/VideoPlayerControls.tsx
index 6322844c2..440fd598a 100644
--- a/src/components/mediaViewer/VideoPlayerControls.tsx
+++ b/src/components/mediaViewer/VideoPlayerControls.tsx
@@ -1,9 +1,12 @@
-import React, { FC, useState, useEffect } from '../../lib/teact/teact';
+import React, {
+ FC, useState, useEffect, useRef, useCallback,
+} from '../../lib/teact/teact';
import { 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';
@@ -21,11 +24,9 @@ type IProps = {
isFullscreen: boolean;
onChangeFullscreen: (e: React.MouseEvent) => void;
onPlayPause: (e: React.MouseEvent) => void;
- onSeek: OnChangeHandler;
+ onSeek: (position: number) => void;
};
-type OnChangeHandler = (e: React.ChangeEvent) => void;
-
const stopEvent = (e: React.MouseEvent) => {
e.stopPropagation();
};
@@ -47,6 +48,9 @@ const VideoPlayerControls: FC = ({
onSeek,
}) => {
const [isVisible, setVisibility] = useState(true);
+ // eslint-disable-next-line no-null/no-null
+ const seekerRef = useRef(null);
+ const isSeeking = useRef(false);
useEffect(() => {
if (isForceVisible) {
@@ -86,13 +90,40 @@ const VideoPlayerControls: FC = ({
const lang = useLang();
+ const handleSeek = useCallback((e: MouseEvent | TouchEvent) => {
+ if (isSeeking.current && seekerRef.current) {
+ const { width, left } = seekerRef.current.getBoundingClientRect();
+ const clientX = e instanceof MouseEvent ? e.clientX : e.targetTouches[0].clientX;
+ onSeek(Math.max(Math.min(duration * ((clientX - left) / width), duration), 0));
+ }
+ }, [duration, onSeek]);
+
+ const handleStartSeek = useCallback((e: MouseEvent | TouchEvent) => {
+ isSeeking.current = true;
+ handleSeek(e);
+ }, [handleSeek]);
+
+ const handleStopSeek = useCallback(() => {
+ isSeeking.current = false;
+ }, []);
+
+ useEffect(() => {
+ if (!seekerRef.current || !isVisible) return undefined;
+ return captureEvents(seekerRef.current, {
+ onCapture: handleStartSeek,
+ onRelease: handleStopSeek,
+ onClick: handleStopSeek,
+ onDrag: handleSeek,
+ });
+ }, [isVisible, handleStartSeek, handleSeek, handleStopSeek]);
+
if (!isVisible && !isForceVisible) {
return undefined;
}
return (
- {renderSeekLine(currentTime, duration, bufferedProgress, onSeek)}
+ {renderSeekLine(currentTime, duration, bufferedProgress, seekerRef)}