diff --git a/src/components/audio.ts b/src/components/audio.ts
index 1a523144..a94ddebb 100644
--- a/src/components/audio.ts
+++ b/src/components/audio.ts
@@ -73,39 +73,21 @@ export function decodeWaveform(waveform: Uint8Array | number[]) {
result = new Uint8Array([]);
}
- /* var byteIndex = (valueCount - 1) / 8 | 0;
- var bitShift = (valueCount - 1) % 8;
- if(byteIndex === waveform.length - 1) {
- var value = waveform[byteIndex];
- } else {
- var value = dataView.getUint16(byteIndex, true);
- }
- console.log('decoded waveform, setting last value:', value, byteIndex, bitShift);
- result[valueCount - 1] = (value >> bitShift) & 0b00011111; */
return result;
}
function createWaveformBars(waveform: Uint8Array, duration: number) {
const barWidth = 2;
- const barMargin = 2; // mediaSizes.isMobile ? 2 : 1;
- const barHeightMin = 4; // mediaSizes.isMobile ? 3 : 2;
+ const barMargin = 2;
+ const barHeightMin = 4;
const barHeightMax = mediaSizes.isMobile ? 16 : 23;
- // const availW = 150; //mediaSizes.isMobile ? 152 : 190;
const minW = mediaSizes.isMobile ? 152 : 190;
const maxW = mediaSizes.isMobile ? 190 : 256;
- const availW = clamp(duration / 60 * maxW, minW, maxW); // mediaSizes.isMobile ? 152 : 224;
-
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svg.classList.add('audio-waveform-bars');
- svg.setAttributeNS(null, 'width', '' + availW);
- svg.setAttributeNS(null, 'height', '' + barHeightMax);
- svg.setAttributeNS(null, 'viewBox', `0 0 ${availW} ${barHeightMax}`);
-
- // console.log('decoded waveform:', waveform);
+ const availW = clamp(duration / 60 * maxW, minW, maxW);
const normValue = Math.max(...waveform);
- const wfSize = waveform.length ? waveform.length : 100;
+ const wfSize = waveform.length;
const barCount = Math.min((availW / (barWidth + barMargin)) | 0, wfSize);
let maxValue = 0;
@@ -122,9 +104,7 @@ function createWaveformBars(waveform: Uint8Array, duration: number) {
const bar_value = Math.max(((maxValue * maxDelta) + ((normValue + 1) / 2)) / (normValue + 1), barHeightMin);
- const h = `
-
- `;
+ const h = ``;
html += h;
barX += barWidth + barMargin;
@@ -141,11 +121,24 @@ function createWaveformBars(waveform: Uint8Array, duration: number) {
}
}
- const container = document.createElement('div');
- container.classList.add('audio-waveform');
- container.append(svg);
+ let container: HTMLElement, svg: SVGSVGElement;
+
+ if(!html) {
+
+ } else {
+ container = document.createElement('div');
+ container.classList.add('audio-waveform');
+
+ svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ svg.classList.add('audio-waveform-bars');
+ svg.setAttributeNS(null, 'width', '' + availW);
+ svg.setAttributeNS(null, 'height', '' + barHeightMax);
+ svg.setAttributeNS(null, 'viewBox', `0 0 ${availW} ${barHeightMax}`);
+ svg.insertAdjacentHTML('beforeend', html);
+
+ container.append(svg);
+ }
- svg.insertAdjacentHTML('beforeend', html);
return {svg, container, availW};
}
@@ -164,13 +157,19 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
const {svg, container: svgContainer, availW} = createWaveformBars(waveform, doc.duration);
- const fakeSvgContainer = svgContainer.cloneNode(true) as HTMLElement;
- fakeSvgContainer.classList.add('audio-waveform-fake');
- svgContainer.classList.add('audio-waveform-background');
+ let fakeSvgContainer: HTMLElement;
+ if(svgContainer) {
+ fakeSvgContainer = svgContainer.cloneNode(true) as HTMLElement;
+ fakeSvgContainer.classList.add('audio-waveform-fake');
+ svgContainer.classList.add('audio-waveform-background');
+ }
const waveformContainer = document.createElement('div');
waveformContainer.classList.add('audio-waveform-container');
- waveformContainer.append(svgContainer, fakeSvgContainer);
+
+ if(svgContainer) {
+ waveformContainer.append(svgContainer, fakeSvgContainer);
+ }
const timeDiv = document.createElement('div');
timeDiv.classList.add('audio-time');
@@ -224,7 +223,14 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
audioEl.append(speechRecognitionDiv);
}
- let progress = svg as any as HTMLElement;
+ let progress = svg as any as HTMLElement, progressLine: MediaProgressLine;
+ if(!progress) {
+ progressLine = new MediaProgressLine({
+ streamable: doc.supportsStreaming
+ });
+
+ waveformContainer.append(progressLine.container);
+ }
const onLoad = () => {
let audio = audioEl.audio;
@@ -238,7 +244,9 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
};
const onTimeUpdate = () => {
- fakeSvgContainer.style.width = (audio.currentTime / audio.duration * 100) + '%';
+ if(fakeSvgContainer) {
+ fakeSvgContainer.style.width = (audio.currentTime / audio.duration * 100) + '%';
+ }
};
if(!audio.paused || (audio.currentTime > 0 && audio.currentTime !== audio.duration)) {
@@ -250,7 +258,7 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
audioEl.addAudioListener('ended', throttledTimeUpdate);
audioEl.addAudioListener('play', setAnimation);
- audioEl.readyPromise.then(() => {
+ progress && audioEl.readyPromise.then(() => {
let mousedown = false, mousemove = false;
progress.addEventListener('mouseleave', (e) => {
if(mousedown) {
@@ -298,8 +306,10 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
}
}, noop);
+ !progress && progressLine.setMedia(audio);
+
return () => {
- progress.remove();
+ progress?.remove();
progress = null;
audio = null;
};
@@ -495,7 +505,10 @@ export default class AudioElement extends HTMLElement {
const isOutgoing = this.message.pFlags.is_outgoing;
const uploadingFileName = this.message?.uploadingFileName;
- const durationStr = toHHMMSS(doc.duration | 0);
+ const getDurationStr = () => {
+ const duration = this.audio && this.audio.readyState >= this.audio.HAVE_CURRENT_DATA ? this.audio.duration : doc.duration;
+ return toHHMMSS(duration | 0);
+ };
this.innerHTML = `
@@ -523,7 +536,7 @@ export default class AudioElement extends HTMLElement {
const onTypeLoad = await (isVoice ? wrapVoiceMessage(this) : wrapAudio(this));
const audioTimeDiv = this.querySelector('.audio-time') as HTMLDivElement;
- audioTimeDiv.innerHTML = durationStr;
+ audioTimeDiv.textContent = getDurationStr();
const onLoad = this.onLoad = (autoload: boolean) => {
this.onLoad = undefined;
@@ -538,7 +551,7 @@ export default class AudioElement extends HTMLElement {
this.onTypeDisconnect = onTypeLoad();
- const getTimeStr = () => toHHMMSS(audio.currentTime | 0) + (isVoice ? (' / ' + durationStr) : '');
+ const getTimeStr = () => toHHMMSS(audio.currentTime | 0) + (isVoice ? (' / ' + getDurationStr()) : '');
const onPlay = () => {
audioTimeDiv.innerText = getTimeStr();
@@ -573,7 +586,7 @@ export default class AudioElement extends HTMLElement {
this.addAudioListener('ended', () => {
toggle.classList.remove('playing');
- audioTimeDiv.innerText = durationStr;
+ audioTimeDiv.innerText = getDurationStr();
});
this.addAudioListener('timeupdate', () => {
diff --git a/src/global.d.ts b/src/global.d.ts
index 342aa822..2b8d6064 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -65,7 +65,7 @@ declare global {
'USER_ALREADY_PARTICIPANT' | 'USERNAME_INVALID' | 'USERNAME_PURCHASE_AVAILABLE' | 'USERNAMES_ACTIVE_TOO_MUCH' |
'BOT_INVALID' | 'USERNAME_NOT_OCCUPIED' | 'PINNED_TOO_MUCH' | 'LOCATION_INVALID' |
'FILE_ID_INVALID' | 'CHANNEL_FORUM_MISSING' | 'TRANSCRIPTION_FAILED' | 'USER_NOT_PARTICIPANT' |
- 'PEER_ID_INVALID';
+ 'PEER_ID_INVALID' | 'MSG_VOICE_MISSING';
type ErrorType = LocalErrorType | ServerErrorType;
diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts
index 49b45ce9..f243ddd2 100644
--- a/src/lib/appManagers/appMessagesManager.ts
+++ b/src/lib/appManagers/appMessagesManager.ts
@@ -549,7 +549,7 @@ export class AppMessagesManager extends AppManager {
return result;
},
processError: (error) => {
- if(error.type === 'TRANSCRIPTION_FAILED') {
+ if(error.type === 'TRANSCRIPTION_FAILED' || error.type === 'MSG_VOICE_MISSING') {
process({
_: 'messages.transcribedAudio',
transcription_id: 0,
diff --git a/src/scss/partials/_audio.scss b/src/scss/partials/_audio.scss
index f4342833..afad156b 100644
--- a/src/scss/partials/_audio.scss
+++ b/src/scss/partials/_audio.scss
@@ -406,6 +406,8 @@
position: relative;
height: 23px;
margin-top: 1px;
+ display: flex;
+ align-items: center;
}
&-bar {
@@ -576,6 +578,21 @@
align-items: center;
}
+ .progress-line {
+ margin-top: 4px;
+ margin-inline-start: 0;
+
+ &__filled:after {
+ opacity: 0;
+ }
+ }
+
+ // .audio-toggle:not(.playing) + .audio-waveform-container {
+ // .progress-line__filled:after {
+ // opacity: 0;
+ // }
+ // }
+
@include respond-to(handhelds) {
.audio-time {
margin-top: .3125rem;