Browse Source

Added video messages to voice tab

Some fixes
master
morethanwords 3 years ago
parent
commit
1e4b34aca3
  1. 34
      src/components/appMediaPlaybackController.ts
  2. 43
      src/components/appMediaViewer.ts
  3. 57
      src/components/appSearchSuper..ts
  4. 77
      src/components/audio.ts
  5. 34
      src/components/chat/audio.ts
  6. 11
      src/components/chat/pinnedContainer.ts
  7. 2
      src/components/sidebarLeft/index.ts
  8. 4
      src/components/sidebarLeft/tabs/activeSessions.ts
  9. 2
      src/components/sidebarRight/tabs/sharedMedia.ts
  10. 51
      src/components/wrappers.ts
  11. 60
      src/helpers/date.ts
  12. 2
      src/lang.ts
  13. 33
      src/lib/appManagers/appMessagesManager.ts
  14. 9
      src/lib/langPack.ts
  15. 29
      src/scss/partials/_audio.scss
  16. 4
      src/scss/partials/_chatBubble.scss
  17. 4
      src/scss/partials/_document.scss
  18. 9
      src/scss/partials/_rightSidebar.scss

34
src/components/appMediaPlaybackController.ts

@ -267,22 +267,24 @@ class AppMediaPlaybackController { @@ -267,22 +267,24 @@ class AppMediaPlaybackController {
} else if(isVoice) {
const peerId = message.fromId || message.peerId;
const peerPhoto = appPeersManager.getPeerPhoto(peerId);
const result = appAvatarsManager.loadAvatar(peerId, peerPhoto, 'photo_small');
if(result.cached) {
const url = await result.loadPromise;
artwork.push({
src: url,
sizes: '160x160',
type: 'image/jpeg'
});
} else {
result.loadPromise.then((url) => {
if(this.playingMedia !== playingMedia || !url) {
return;
}
this.setNewMediadata(message);
});
if(peerPhoto) {
const result = appAvatarsManager.loadAvatar(peerId, peerPhoto, 'photo_small');
if(result.cached) {
const url = await result.loadPromise;
artwork.push({
src: url,
sizes: '160x160',
type: 'image/jpeg'
});
} else {
result.loadPromise.then((url) => {
if(this.playingMedia !== playingMedia || !url) {
return;
}
this.setNewMediadata(message);
});
}
}
title = appPeersManager.getPeerTitle(peerId, true, false);

43
src/components/appMediaViewer.ts

@ -28,7 +28,7 @@ import ProgressivePreloader from "./preloader"; @@ -28,7 +28,7 @@ import ProgressivePreloader from "./preloader";
import Scrollable from "./scrollable";
import appSidebarRight from "./sidebarRight";
import SwipeHandler from "./swipeHandler";
import { ONE_DAY } from "../helpers/date";
import { formatFullSentTime, formatTime, ONE_DAY } from "../helpers/date";
import { SearchSuperContext } from "./appSearchSuper.";
import appNavigationController from "./appNavigationController";
import { Message } from "../layer";
@ -1185,46 +1185,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType @@ -1185,46 +1185,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
}
protected setAuthorInfo(fromId: number, timestamp: number) {
const date = new Date();
const time = new Date(timestamp * 1000);
const now = date.getTime() / 1000;
const timeEl = new I18n.IntlDateElement({
date: time,
options: {
hour: '2-digit',
minute: '2-digit'
}
}).element;
let dateEl: Node | string;
if((now - timestamp) < ONE_DAY && date.getDate() === time.getDate()) { // if the same day
dateEl = i18n('Date.Today');
} else if((now - timestamp) < (ONE_DAY * 2) && (date.getDate() - 1) === time.getDate()) { // yesterday
dateEl = capitalizeFirstLetter(I18n.format('Yesterday', true));
} else if(date.getFullYear() !== time.getFullYear()) { // different year
dateEl = new I18n.IntlDateElement({
date: time,
options: {
month: 'short',
day: 'numeric',
year: 'numeric'
}
}).element;
// dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate() + ', ' + time.getFullYear();
} else {
dateEl = new I18n.IntlDateElement({
date: time,
options: {
month: 'short',
day: 'numeric'
}
}).element;
// dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate();
}
this.author.date.innerHTML = '';
this.author.date.append(dateEl, ' ', i18n('ScheduleController.at'), ' ', timeEl);
this.author.date.append(formatFullSentTime(timestamp));
replaceContent(this.author.nameEl, new PeerTitle({
peerId: fromId,

57
src/components/appSearchSuper..ts

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { formatDateAccordingToToday, months } from "../helpers/date";
import { months } from "../helpers/date";
import { copy, getObjectKeysAndSort, safeAssign } from "../helpers/object";
import { escapeRegExp, limitSymbols } from "../helpers/string";
import appChatsManager from "../lib/appManagers/appChatsManager";
@ -26,7 +26,7 @@ import { ripple } from "./ripple"; @@ -26,7 +26,7 @@ import { ripple } from "./ripple";
import Scrollable, { ScrollableX } from "./scrollable";
import { wrapDocument, wrapPhoto, wrapVideo } from "./wrappers";
import useHeavyAnimationCheck, { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
import { LangPackKey, i18n } from "../lib/langPack";
import I18n, { LangPackKey, i18n } from "../lib/langPack";
import findUpClassName from "../helpers/dom/findUpClassName";
import { getMiddleware } from "../helpers/middleware";
import appProfileManager from "../lib/appManagers/appProfileManager";
@ -50,6 +50,7 @@ import htmlToDocumentFragment from "../helpers/dom/htmlToDocumentFragment"; @@ -50,6 +50,7 @@ import htmlToDocumentFragment from "../helpers/dom/htmlToDocumentFragment";
import { SearchSelection } from "./chat/selection";
import { cancelEvent } from "../helpers/dom/cancelEvent";
import { attachClickEvent, simulateClickEvent } from "../helpers/dom/clickEvent";
import { MyDocument } from "../lib/appManagers/appDocsManager";
//const testScroll = false;
@ -599,6 +600,18 @@ export default class AppSearchSuper { @@ -599,6 +600,18 @@ export default class AppSearchSuper {
break;
}
case 'inputMessagesFilterRoundVoice': {
for(let message of messages) {
if(!message.media.document || !(['voice', 'round'] as MyDocument['type'][]).includes(message.media.document.type)) {
continue;
}
filtered.push(message);
}
break;
}
default:
break;
}
@ -695,21 +708,22 @@ export default class AppSearchSuper { @@ -695,21 +708,22 @@ export default class AppSearchSuper {
}
case 'inputMessagesFilterVoice':
case 'inputMessagesFilterRoundVoice':
case 'inputMessagesFilterMusic':
case 'inputMessagesFilterDocument': {
for(const message of messages) {
const showSender = this.showSender || message.media.document.type === 'voice';
const showSender = this.showSender || (['voice', 'round'] as MyDocument['type'][]).includes(message.media.document.type);
const div = wrapDocument({
message,
withTime: !showSender,
fontWeight: 400,
voiceAsMusic: true,
showSender: showSender,
showSender,
searchContext: this.copySearchContext(inputFilter),
lazyLoadQueue: this.lazyLoadQueue
});
if(['audio', 'voice'].includes(message.media.document.type)) {
if((['audio', 'voice', 'round'] as MyDocument['type'][]).includes(message.media.document.type)) {
div.classList.add('audio-48');
}
@ -810,22 +824,19 @@ export default class AppSearchSuper { @@ -810,22 +824,19 @@ export default class AppSearchSuper {
}
subtitleFragment.append(a);
if(this.showSender) {
subtitleFragment.append('\n', appMessagesManager.wrapSenderToPeer(message));
}
if(!title) {
//title = new URL(webpage.url).hostname;
title = RichTextProcessor.wrapPlainText(webpage.display_url.split('/', 1)[0]);
}
let sender = this.showSender ? `<div class="subtitle sender">${appMessagesManager.getSenderToPeerText(message)}</div>` : '';
let titleAdditionHTML = '';
if(this.showSender) {
titleAdditionHTML = `<div class="sent-time">${formatDateAccordingToToday(new Date(message.date * 1000))}</div>`;
}
const row = new Row({
title,
titleRight: titleAdditionHTML,
titleRight: appMessagesManager.wrapSentTime(message),
subtitle: subtitleFragment,
havePadding: true,
clickable: true,
@ -1257,7 +1268,7 @@ export default class AppSearchSuper { @@ -1257,7 +1268,7 @@ export default class AppSearchSuper {
}
// ! Фикс случая, когда не загружаются документы при открытой панели разработчиков (происходит из-за того, что не совпадают критерии отбора документов в getSearch)
if(value.history.length < loadCount) {
if(value.history.length < loadCount || (this.searchContext.folderId !== undefined && !value.next_rate) || value.history.length === value.count) {
//if((value.count || history.length === value.count) && history.length >= value.count) {
//this.log(logStr + 'loaded all media', value, loadCount);
this.loaded[type] = true;
@ -1401,14 +1412,26 @@ export default class AppSearchSuper { @@ -1401,14 +1412,26 @@ export default class AppSearchSuper {
const dateTimestamp = date.getTime();
const containers = this.monthContainers[type] ?? (this.monthContainers[type] = {});
if(!(dateTimestamp in containers)) {
const str = months[date.getMonth()] + ' ' + date.getFullYear();
const container = document.createElement('div');
container.className = 'search-super-month';
const name = document.createElement('div');
name.classList.add('search-super-month-name');
name.innerText = str;
const options: Intl.DateTimeFormatOptions = {
month: 'long'
};
if(date.getFullYear() !== new Date().getFullYear()) {
options.year = 'numeric';
}
const dateElement = new I18n.IntlDateElement({
date,
options
}).element;
name.append(dateElement);
container.append(name);
const items = document.createElement('div');

77
src/components/audio.ts

@ -5,8 +5,7 @@ @@ -5,8 +5,7 @@
*/
import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
import { RichTextProcessor } from "../lib/richtextprocessor";
import { formatDate, wrapPhoto } from "./wrappers";
import { wrapPhoto } from "./wrappers";
import ProgressivePreloader from "./preloader";
import { MediaProgressLine } from "../lib/mediaPlayer";
import appMediaPlaybackController, { MediaItem } from "./appMediaPlaybackController";
@ -17,7 +16,6 @@ import appMessagesManager from "../lib/appManagers/appMessagesManager"; @@ -17,7 +16,6 @@ import appMessagesManager from "../lib/appManagers/appMessagesManager";
import rootScope from "../lib/rootScope";
import './middleEllipsis';
import { SearchSuperContext } from "./appSearchSuper.";
import { formatDateAccordingToToday } from "../helpers/date";
import { cancelEvent } from "../helpers/dom/cancelEvent";
import { attachClickEvent, detachClickEvent } from "../helpers/dom/clickEvent";
import LazyLoadQueue from "./lazyLoadQueue";
@ -25,6 +23,11 @@ import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromi @@ -25,6 +23,11 @@ import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromi
import ListenerSetter, { Listener } from "../helpers/listenerSetter";
import noop from "../helpers/noop";
import findUpClassName from "../helpers/dom/findUpClassName";
import { joinElementsWith } from "../lib/langPack";
import { MiddleEllipsisElement } from "./middleEllipsis";
import htmlToSpan from "../helpers/dom/htmlToSpan";
import { formatFullSentTime } from "../helpers/date";
import { formatBytes } from "../helpers/number";
rootScope.addEventListener('messages_media_read', ({mids, peerId}) => {
mids.forEach(mid => {
@ -273,43 +276,56 @@ function wrapAudio(audioEl: AudioElement) { @@ -273,43 +276,56 @@ function wrapAudio(audioEl: AudioElement) {
const message = audioEl.message;
const doc: MyDocument = message.media.document || message.media.webpage.document;
const senderTitle = audioEl.showSender ? appMessagesManager.getSenderToPeerText(message) : '';
let title = doc.type === 'voice' ? senderTitle : (doc.audioTitle || doc.fileName);
let subtitle: string;
const isVoice = doc.type === 'voice' || doc.type === 'round';
const descriptionEl = document.createElement('div');
descriptionEl.classList.add('audio-description');
if(doc.type === 'voice') {
subtitle = '';
} else {
subtitle = doc.audioPerformer ? RichTextProcessor.wrapPlainText(doc.audioPerformer) : '';
if(!isVoice) {
const parts: (Node | string)[] = [];
if(doc.audioPerformer) {
parts.push(htmlToSpan(doc.audioPerformer));
}
if(withTime) {
subtitle += (subtitle ? ' • ' : '') + formatDate(doc.date);
} else if(!subtitle) {
subtitle = 'Unknown Artist';
parts.push(formatFullSentTime(doc.date));
} else if(!parts.length) {
parts.push(formatBytes(doc.size));
}
if(audioEl.showSender) {
subtitle += ' • ' + senderTitle;
} else {
subtitle = ' • ' + subtitle;
parts.push(appMessagesManager.wrapSenderToPeer(message));
}
}
let titleAdditionHTML = '';
if(audioEl.showSender) {
titleAdditionHTML = `<div class="sent-time">${formatDateAccordingToToday(new Date(message.date * 1000))}</div>`;
descriptionEl.append(...joinElementsWith(parts, ' • '));
}
const html = `
<div class="audio-details">
<div class="audio-title"><middle-ellipsis-element data-font-weight="${audioEl.dataset.fontWeight}">${title}</middle-ellipsis-element>${titleAdditionHTML}</div>
<div class="audio-subtitle"><div class="audio-time"></div>${subtitle || '<div></div>'}</div>
<div class="audio-title"></div>
<div class="audio-subtitle"><div class="audio-time"></div></div>
</div>`;
audioEl.insertAdjacentHTML('beforeend', html);
const titleEl = audioEl.querySelector('.audio-title') as HTMLElement;
const middleEllipsisEl = new MiddleEllipsisElement();
middleEllipsisEl.dataset.fontWeight = audioEl.dataset.fontWeight;
if(isVoice) {
middleEllipsisEl.append(appMessagesManager.wrapSenderToPeer(message));
} else {
middleEllipsisEl.innerHTML = doc.audioTitle || doc.fileName;
}
titleEl.append(middleEllipsisEl);
if(audioEl.showSender) {
titleEl.append(appMessagesManager.wrapSentTime(message));
}
const subtitleDiv = audioEl.querySelector('.audio-subtitle') as HTMLDivElement;
subtitleDiv.append(descriptionEl);
const onLoad = () => {
const subtitleDiv = audioEl.querySelector('.audio-subtitle') as HTMLDivElement;
let launched = false;
let progressLine = new MediaProgressLine(audioEl.audio, doc.supportsStreaming);
@ -317,7 +333,7 @@ function wrapAudio(audioEl: AudioElement) { @@ -317,7 +333,7 @@ function wrapAudio(audioEl: AudioElement) {
audioEl.addAudioListener('ended', () => {
audioEl.classList.remove('audio-show-progress');
// Reset subtitle
subtitleDiv.lastChild.replaceWith(subtitle);
subtitleDiv.lastChild.replaceWith(descriptionEl);
launched = false;
});
@ -519,9 +535,9 @@ export default class AudioElement extends HTMLElement { @@ -519,9 +535,9 @@ export default class AudioElement extends HTMLElement {
onClick();
}
} else {
if(doc.supportsStreaming) {
// if(doc.supportsStreaming) {
onLoad(false);
}
// }
if(doc.thumbs) {
const imgs: HTMLImageElement[] = [];
@ -606,6 +622,8 @@ export default class AudioElement extends HTMLElement { @@ -606,6 +622,8 @@ export default class AudioElement extends HTMLElement {
this.listenerSetter.remove(pauseListener);
});
} else {
preloader = constructDownloadPreloader();
const load = () => {
const download = getDownloadPromise();
preloader.attach(downloadDiv, false, download);
@ -619,7 +637,10 @@ export default class AudioElement extends HTMLElement { @@ -619,7 +637,10 @@ export default class AudioElement extends HTMLElement {
this.append(downloadDiv);
this.classList.add('downloading');
this.readyPromise.then(() => {
this.classList.remove('downloading');
downloadDiv.classList.add('downloaded');
setTimeout(() => {
downloadDiv.remove();

34
src/components/chat/audio.ts

@ -6,30 +6,40 @@ @@ -6,30 +6,40 @@
import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager";
import type ChatTopbar from "./topbar";
import { RichTextProcessor } from "../../lib/richtextprocessor";
import rootScope from "../../lib/rootScope";
import appMediaPlaybackController from "../appMediaPlaybackController";
import DivAndCaption from "../divAndCaption";
import { formatDate } from "../wrappers";
import PinnedContainer from "./pinnedContainer";
import Chat from "./chat";
import { cancelEvent } from "../../helpers/dom/cancelEvent";
import { attachClickEvent } from "../../helpers/dom/clickEvent";
import replaceContent from "../../helpers/dom/replaceContent";
import PeerTitle from "../peerTitle";
import { i18n } from "../../lib/langPack";
import { formatFullSentTime } from "../../helpers/date";
export default class ChatAudio extends PinnedContainer {
private toggleEl: HTMLElement;
constructor(protected topbar: ChatTopbar, protected chat: Chat, protected appMessagesManager: AppMessagesManager) {
super(topbar, chat, topbar.listenerSetter, 'audio', new DivAndCaption('pinned-audio', (title: string | HTMLElement, subtitle: string | HTMLElement) => {
replaceContent(this.divAndCaption.title, title);
replaceContent(this.divAndCaption.subtitle, subtitle);
}), () => {
if(this.toggleEl.classList.contains('flip-icon')) {
appMediaPlaybackController.toggle();
super(
topbar,
chat,
topbar.listenerSetter,
'audio',
new DivAndCaption(
'pinned-audio',
(title: string | HTMLElement | DocumentFragment, subtitle: string | HTMLElement | DocumentFragment) => {
replaceContent(this.divAndCaption.title, title);
replaceContent(this.divAndCaption.subtitle, subtitle);
}
),
() => {
if(this.toggleEl.classList.contains('flip-icon')) {
appMediaPlaybackController.toggle();
}
}
});
);
this.divAndCaption.border.remove();
@ -45,16 +55,16 @@ export default class ChatAudio extends PinnedContainer { @@ -45,16 +55,16 @@ export default class ChatAudio extends PinnedContainer {
this.topbar.listenerSetter.add(rootScope)('audio_play', (e) => {
const {doc, mid, peerId} = e;
let title: string | HTMLElement, subtitle: string;
let title: string | HTMLElement, subtitle: string | HTMLElement | DocumentFragment;
const message = appMessagesManager.getMessageByPeer(peerId, mid);
if(doc.type === 'voice' || doc.type === 'round') {
title = new PeerTitle({peerId: message.fromId}).element;
//subtitle = 'Voice message';
subtitle = formatDate(message.date, false, false);
subtitle = formatFullSentTime(message.date);
} else {
title = doc.audioTitle || doc.fileName;
subtitle = doc.audioPerformer ? RichTextProcessor.wrapPlainText(doc.audioPerformer) : 'Unknown Artist';
subtitle = doc.audioPerformer || i18n('AudioUnknownArtist');
}
this.fill(title, subtitle, message);

11
src/components/chat/pinnedContainer.ts

@ -22,7 +22,14 @@ export default class PinnedContainer { @@ -22,7 +22,14 @@ export default class PinnedContainer {
private close: HTMLElement;
protected wrapper: HTMLElement;
constructor(protected topbar: ChatTopbar, protected chat: Chat, public listenerSetter: ListenerSetter, protected className: string, public divAndCaption: DivAndCaption<(title: string | HTMLElement, subtitle: string | HTMLElement, message?: any) => void>, onClose?: () => void | Promise<boolean>) {
constructor(
protected topbar: ChatTopbar,
protected chat: Chat,
public listenerSetter: ListenerSetter,
protected className: string,
public divAndCaption: DivAndCaption<(title: string | HTMLElement | DocumentFragment, subtitle: string | HTMLElement | DocumentFragment, message?: any) => void>,
onClose?: () => void | Promise<boolean>
) {
/* const prev = this.divAndCaption.fill;
this.divAndCaption.fill = (mid, title, subtitle) => {
this.divAndCaption.container.dataset.mid = '' + mid;
@ -87,7 +94,7 @@ export default class PinnedContainer { @@ -87,7 +94,7 @@ export default class PinnedContainer {
this.topbar.setUtilsWidth();
}
public fill(title: string | HTMLElement, subtitle: string | HTMLElement, message: any) {
public fill(title: string | HTMLElement | DocumentFragment, subtitle: string | HTMLElement | DocumentFragment, message: any) {
this.divAndCaption.container.dataset.peerId = '' + message.peerId;
this.divAndCaption.container.dataset.mid = '' + message.mid;
this.divAndCaption.fill(title, subtitle, message);

2
src/components/sidebarLeft/index.ts

@ -297,7 +297,7 @@ export class AppSidebarLeft extends SidebarSlider { @@ -297,7 +297,7 @@ export class AppSidebarLeft extends SidebarSlider {
name: 'SharedMusicTab2',
type: 'music'
}, {
inputFilter: 'inputMessagesFilterVoice',
inputFilter: 'inputMessagesFilterRoundVoice',
name: 'SharedVoiceTab2',
type: 'voice'
}],

4
src/components/sidebarLeft/tabs/activeSessions.ts

@ -9,7 +9,7 @@ import { SettingSection } from ".."; @@ -9,7 +9,7 @@ import { SettingSection } from "..";
import Button from "../../button";
import Row from "../../row";
import { Authorization } from "../../../layer";
import { formatDateAccordingToToday } from "../../../helpers/date";
import { formatDateAccordingToTodayNew } from "../../../helpers/date";
import { attachContextMenuListener, openBtnMenu, positionMenu } from "../../misc";
import ButtonMenu from "../../buttonMenu";
import apiManager from "../../../lib/mtproto/mtprotoworker";
@ -35,7 +35,7 @@ export default class AppActiveSessionsTab extends SliderSuperTab { @@ -35,7 +35,7 @@ export default class AppActiveSessionsTab extends SliderSuperTab {
title: [auth.app_name, auth.app_version].join(' '),
subtitle: [auth.ip, auth.country].join(' - '),
clickable: true,
titleRight: auth.pFlags.current ? undefined : formatDateAccordingToToday(new Date(Math.max(auth.date_active, auth.date_created) * 1000))
titleRight: auth.pFlags.current ? undefined : formatDateAccordingToTodayNew(new Date(Math.max(auth.date_active, auth.date_created) * 1000))
});
row.container.dataset.hash = auth.hash;

2
src/components/sidebarRight/tabs/sharedMedia.ts

@ -862,7 +862,7 @@ export default class AppSharedMediaTab extends SliderSuperTab { @@ -862,7 +862,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
name: 'SharedMusicTab2',
type: 'music'
}, {
inputFilter: 'inputMessagesFilterVoice',
inputFilter: 'inputMessagesFilterRoundVoice',
name: 'SharedVoiceTab2',
type: 'voice'
}],

51
src/components/wrappers.ts

@ -8,7 +8,7 @@ import type Chat from './chat/chat'; @@ -8,7 +8,7 @@ import type Chat from './chat/chat';
import { getEmojiToneIndex } from '../vendor/emoji';
import { readBlobAsText } from '../helpers/blob';
import { deferredPromise } from '../helpers/cancellablePromise';
import { formatDateAccordingToToday, months } from '../helpers/date';
import { formatFullSentTime } from '../helpers/date';
import mediaSizes, { ScreenSize } from '../helpers/mediaSizes';
import { formatBytes } from '../helpers/number';
import { IS_SAFARI } from '../environment/userAgent';
@ -46,6 +46,8 @@ import { clearBadCharsAndTrim } from '../helpers/cleanSearchText'; @@ -46,6 +46,8 @@ import { clearBadCharsAndTrim } from '../helpers/cleanSearchText';
import blur from '../helpers/blur';
import IS_WEBP_SUPPORTED from '../environment/webpSupport';
import MEDIA_MIME_TYPES_SUPPORTED from '../environment/mediaMimeTypesSupport';
import { MiddleEllipsisElement } from './middleEllipsis';
import { joinElementsWith } from '../lib/langPack';
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB
@ -487,20 +489,6 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -487,20 +489,6 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
return res;
}
export const formatDate = (timestamp: number, monthShort = false, withYear = true) => {
const date = new Date(timestamp * 1000);
let month = months[date.getMonth()];
if(monthShort) month = month.slice(0, 3);
let str = month + ' ' + date.getDate();
if(withYear) {
str += ', ' + date.getFullYear();
}
return str + ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2);
};
rootScope.addEventListener('download_start', (docId) => {
const elements = Array.from(document.querySelectorAll(`.document[data-doc-id="${docId}"]`)) as HTMLElement[];
elements.forEach(element => {
@ -525,7 +513,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS @@ -525,7 +513,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
const doc = (message.media.document || message.media.webpage.document) as MyDocument;
const uploading = message.pFlags.is_outgoing && message.media?.preloader;
if(doc.type === 'audio' || doc.type === 'voice') {
if(doc.type === 'audio' || doc.type === 'voice' || doc.type === 'round') {
const audioElement = new AudioElement();
audioElement.dataset.mid = '' + message.mid;
audioElement.dataset.peerId = '' + message.peerId;
@ -589,27 +577,38 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS @@ -589,27 +577,38 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
//let fileName = stringMiddleOverflow(doc.file_name || 'Unknown.file', 26);
let fileName = doc.fileName || 'Unknown.file';
let size = formatBytes(doc.size);
const descriptionEl = document.createElement('div');
descriptionEl.classList.add('document-description');
const descriptionParts: (HTMLElement | string | DocumentFragment)[] = [formatBytes(doc.size)];
if(withTime) {
size += ' · ' + formatDate(doc.date);
descriptionParts.push(formatFullSentTime(doc.date));
}
if(showSender) {
size += ' · ' + appMessagesManager.getSenderToPeerText(message);
descriptionParts.push(appMessagesManager.wrapSenderToPeer(message));
}
let titleAdditionHTML = '';
if(showSender) {
titleAdditionHTML = `<div class="sent-time">${formatDateAccordingToToday(new Date(message.date * 1000))}</div>`;
}
docDiv.innerHTML = `
${cacheContext.downloaded && !uploading ? '' : `<div class="document-download"></div>`}
<div class="document-name"><middle-ellipsis-element data-font-weight="${fontWeight}">${fileName}</middle-ellipsis-element>${titleAdditionHTML}</div>
<div class="document-size">${size}</div>
<div class="document-name"></div>
<div class="document-size"></div>
`;
const nameDiv = docDiv.querySelector('.document-name') as HTMLElement;
const middleEllipsisEl = new MiddleEllipsisElement();
middleEllipsisEl.dataset.fontWeight = '' + fontWeight;
middleEllipsisEl.innerHTML = fileName;
nameDiv.append(middleEllipsisEl);
if(showSender) {
nameDiv.append(appMessagesManager.wrapSentTime(message));
}
const sizeDiv = docDiv.querySelector('.document-size') as HTMLElement;
sizeDiv.append(...joinElementsWith(descriptionParts, ' · '));
docDiv.prepend(icoDiv);
if(!uploading && message.pFlags.is_outgoing) {

60
src/helpers/date.ts

@ -5,7 +5,8 @@ @@ -5,7 +5,8 @@
*/
import { MOUNT_CLASS_TO } from "../config/debug";
import I18n from "../lib/langPack";
import I18n, { i18n } from "../lib/langPack";
import { capitalizeFirstLetter } from "./string";
export const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
export const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
@ -21,25 +22,6 @@ export const getWeekNumber = (date: Date) => { @@ -21,25 +22,6 @@ export const getWeekNumber = (date: Date) => {
return Math.ceil((((d.getTime() - yearStart.getTime()) / ONE_DAY) + 1) / 7);
};
export const formatDateAccordingToToday = (time: Date) => {
const date = new Date();
const now = date.getTime() / 1000 | 0;
const timestamp = time.getTime() / 1000 | 0;
let timeStr: string;
if((now - timestamp) < ONE_DAY && date.getDate() === time.getDate()) { // if the same day
timeStr = ('0' + time.getHours()).slice(-2) + ':' + ('0' + time.getMinutes()).slice(-2);
} else if(date.getFullYear() !== time.getFullYear()) { // different year
timeStr = time.getDate() + '.' + ('0' + (time.getMonth() + 1)).slice(-2) + '.' + ('' + time.getFullYear()).slice(-2);
} else if((now - timestamp) < (ONE_DAY * 7) && getWeekNumber(date) === getWeekNumber(time)) { // current week
timeStr = days[time.getDay()].slice(0, 3);
} else { // same year
timeStr = months[time.getMonth()].slice(0, 3) + ' ' + ('0' + time.getDate()).slice(-2);
}
return timeStr;
};
export function formatDateAccordingToTodayNew(time: Date) {
const today = new Date();
const now = today.getTime() / 1000 | 0;
@ -64,6 +46,44 @@ export function formatDateAccordingToTodayNew(time: Date) { @@ -64,6 +46,44 @@ export function formatDateAccordingToTodayNew(time: Date) {
}).element;
}
export function formatFullSentTime(timestamp: number) {
const date = new Date();
const time = new Date(timestamp * 1000);
const now = date.getTime() / 1000;
const timeEl = formatTime(time);
let dateEl: Node | string;
if((now - timestamp) < ONE_DAY && date.getDate() === time.getDate()) { // if the same day
dateEl = i18n('Date.Today');
} else if((now - timestamp) < (ONE_DAY * 2) && (date.getDate() - 1) === time.getDate()) { // yesterday
dateEl = capitalizeFirstLetter(I18n.format('Yesterday', true));
} else if(date.getFullYear() !== time.getFullYear()) { // different year
dateEl = new I18n.IntlDateElement({
date: time,
options: {
month: 'short',
day: 'numeric',
year: 'numeric'
}
}).element;
// dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate() + ', ' + time.getFullYear();
} else {
dateEl = new I18n.IntlDateElement({
date: time,
options: {
month: 'short',
day: 'numeric'
}
}).element;
// dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate();
}
const fragment = document.createDocumentFragment();
fragment.append(dateEl, ' ', i18n('ScheduleController.at'), ' ', timeEl);
return fragment;
}
export function formatTime(date: Date) {
return new I18n.IntlDateElement({
date,

2
src/lang.ts

@ -584,6 +584,8 @@ const lang = { @@ -584,6 +584,8 @@ const lang = {
"AreYouSureBlockContact2": "Are you sure you want to block **%1$s**?",
"UserBlocked": "User blocked",
"UserUnblocked": "User unblocked",
"AudioUnknownArtist": "Unknown artist",
"AudioUnknownTitle": "Unknown title",
// * macos
"AccountSettings.Filters": "Chat Folders",

33
src/lib/appManagers/appMessagesManager.ts

@ -12,7 +12,7 @@ @@ -12,7 +12,7 @@
import { LazyLoadQueueBase } from "../../components/lazyLoadQueue";
import ProgressivePreloader from "../../components/preloader";
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
import { formatTime, tsNow } from "../../helpers/date";
import { formatDateAccordingToTodayNew, formatTime, tsNow } from "../../helpers/date";
import { createPosterForVideo } from "../../helpers/files";
import { copy, getObjectKeysAndSort } from "../../helpers/object";
import { randomLong } from "../../helpers/random";
@ -2721,21 +2721,36 @@ export class AppMessagesManager { @@ -2721,21 +2721,36 @@ export class AppMessagesManager {
}
}
public getSenderToPeerText(message: MyMessage) {
let senderTitle = '', peerTitle: string;
public wrapSenderToPeer(message: MyMessage) {
const senderTitle: HTMLElement = document.createElement('span');
senderTitle.classList.add('sender-title');
senderTitle = message.pFlags.out ? 'You' : appPeersManager.getPeerTitle(message.fromId, false, false);
peerTitle = appPeersManager.isAnyGroup(message.peerId) || (message.pFlags.out && message.peerId !== rootScope.myId) ?
appPeersManager.getPeerTitle(message.peerId, false, false) :
'';
const fromMe = message.fromId === rootScope.myId && message.peerId !== rootScope.myId;
senderTitle.append(
fromMe ?
i18n('FromYou') :
new PeerTitle({
peerId: message.fromId,
dialog: message.peerId === rootScope.myId
}).element
);
if(peerTitle) {
senderTitle += ' ➝ ' + peerTitle;
if(appPeersManager.isAnyGroup(message.peerId) || fromMe) {
const peerTitle = new PeerTitle({peerId: message.peerId}).element;
senderTitle.append(' ➝ ', peerTitle);
}
return senderTitle;
}
public wrapSentTime(message: MyMessage) {
const el: HTMLElement = document.createElement('span');
el.classList.add('sent-time');
el.append(formatDateAccordingToTodayNew(new Date(message.date * 1000)));
return el;
}
public wrapMessageActionTextNew(message: any, plain: true): string;
public wrapMessageActionTextNew(message: any, plain?: false): HTMLElement;
public wrapMessageActionTextNew(message: any, plain: boolean): HTMLElement | string;

9
src/lib/langPack.ts

@ -448,16 +448,19 @@ export {i18n_}; @@ -448,16 +448,19 @@ export {i18n_};
const _i18n = I18n._i18n;
export {_i18n};
export function join(elements: (Node | string)[], useLast = true) {
export function joinElementsWith(elements: (Node | string)[], joiner: typeof elements[0] | ((isLast: boolean) => typeof elements[0])) {
const arr = elements.slice(0, 1);
for(let i = 1; i < elements.length; ++i) {
const isLast = (elements.length - 1) === i;
const delimiterKey: LangPackKey = isLast && useLast ? 'WordDelimiterLast' : 'WordDelimiter';
arr.push(i18n(delimiterKey));
arr.push(typeof(joiner) === 'function' ? joiner(isLast) : joiner);
arr.push(elements[i]);
}
return arr;
}
export function join(elements: (Node | string)[], useLast = true) {
return joinElementsWith(elements, (isLast) => i18n(isLast && useLast ? 'WordDelimiterLast' : 'WordDelimiter'));
}
MOUNT_CLASS_TO.I18n = I18n;

29
src/scss/partials/_audio.scss

@ -454,9 +454,6 @@ @@ -454,9 +454,6 @@
&-subtitle {
font-size: .875rem;
color: var(--secondary-text-color);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: flex;
@include respond-to(handhelds) {
@ -464,6 +461,12 @@ @@ -464,6 +461,12 @@
}
}
&-title,
&-time,
&-subtitle {
@include text-overflow();
}
// * for audio
&-subtitle {
align-items: center;
@ -483,12 +486,9 @@ @@ -483,12 +486,9 @@
// * for audio
&-title,
&:not(.audio-show-progress) &-subtitle {
white-space: nowrap;
overflow: hidden;
max-width: 100%;
text-overflow: ellipsis;
}
&.is-voice {
.audio-time {
line-height: 1;
@ -535,9 +535,10 @@ @@ -535,9 +535,10 @@
.audio-play-icon {
z-index: 1;
background-color: transparent;
opacity: 1;
@include animation-level(2) {
transition: transform .25 ease-in-out, background-color .2s ease-in-out;
transition: transform .25s ease-in-out, background-color .2s ease-in-out, opacity .2s ease-in-out;
}
.part {
@ -555,5 +556,17 @@ @@ -555,5 +556,17 @@
width: inherit;
height: inherit;
}
&:not(.corner-download) {
.audio-download {
background-color: rgba(0, 0, 0, .3);
}
&.downloading {
.audio-play-icon {
opacity: 0;
}
}
}
}
}

4
src/scss/partials/_chatBubble.scss

@ -1691,6 +1691,10 @@ $bubble-margin: .25rem; @@ -1691,6 +1691,10 @@ $bubble-margin: .25rem;
.replies {
user-select: none;
.c-ripple__circle {
background-color: var(--light-primary-color);
}
.rp {
width: 100%;
height: 100%;

4
src/scss/partials/_document.scss

@ -189,6 +189,10 @@ @@ -189,6 +189,10 @@
//transform: scale(0);
}
}
&-description {
@include text-overflow();
}
&:not(.corner-download) .preloader-container:not(.preloader-streamable) {
transform: scale(1) !important;

9
src/scss/partials/_rightSidebar.scss

@ -514,16 +514,17 @@ @@ -514,16 +514,17 @@
white-space: pre-wrap;
text-overflow: ellipsis;
word-break: break-word;
&.sender {
margin-top: .125rem;
}
}
.sent-time {
margin: 1px 0 0;
}
.sender-title {
display: block;
margin-top: .25rem;
}
.checkbox-field {
padding: 0 !important;
margin: 2rem 0 0 -1.75rem !important;

Loading…
Cancel
Save