diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index d8ceb18c..b9ee0345 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -14,6 +14,7 @@ import { touchSupport } from "../../lib/config"; import appDocsManager from "../../lib/appManagers/appDocsManager"; import emoticonsDropdown from "../emoticonsDropdown"; import PopupCreatePoll from "../popupCreatePoll"; +import { toast } from "../toast"; export class ChatInput { public pageEl = document.getElementById('page-chats') as HTMLDivElement; @@ -95,11 +96,11 @@ export class ChatInput { reuseWorker: true }); } catch(err) { - this.btnSend.classList.remove('tgico-microphone2'); - this.btnSend.classList.add('tgico-send'); console.error('Recorder constructor error:', err); } + this.updateSendBtn(); + this.messageInput.addEventListener('keydown', (e: KeyboardEvent) => { if(e.key == 'Enter' && !touchSupport) { /* if(e.ctrlKey || e.metaKey) { @@ -130,14 +131,14 @@ export class ChatInput { this.messageInput.addEventListener('input', (e) => { //console.log('messageInput input', this.messageInput.innerText, this.serializeNodes(Array.from(this.messageInput.childNodes))); - let value = this.messageInput.innerText; + const value = this.messageInput.innerText; - let entities = RichTextProcessor.parseEntities(value); + const entities = RichTextProcessor.parseEntities(value); //console.log('messageInput entities', entities); - let entityUrl = entities.find(e => e._ == 'messageEntityUrl'); + const entityUrl = entities.find(e => e._ == 'messageEntityUrl'); if(entityUrl) { // need to get webpage - let url = value.slice(entityUrl.offset, entityUrl.offset + entityUrl.length); + const url = value.slice(entityUrl.offset, entityUrl.offset + entityUrl.length); //console.log('messageInput url:', url); @@ -147,40 +148,35 @@ export class ChatInput { apiManager.invokeApi('messages.getWebPage', { url: url, hash: 0 - }).then((webpage: any) => { - appWebPagesManager.saveWebPage(webpage); - if(this.lastUrl != url) return; - //console.log('got webpage: ', webpage); - - this.setTopInfo(webpage.site_name || webpage.title, webpage.description || webpage.url); - - this.replyToMsgID = 0; - this.noWebPage = false; - this.willSendWebPage = webpage; + }).then((webpage) => { + webpage = appWebPagesManager.saveWebPage(webpage); + if(webpage._ == 'webPage') { + if(this.lastUrl != url) return; + //console.log('got webpage: ', webpage); + + this.setTopInfo(webpage.site_name || webpage.title, webpage.description || webpage.url); + + this.replyToMsgID = 0; + this.noWebPage = false; + this.willSendWebPage = webpage; + } }); } } if(!value.trim() && !this.serializeNodes(Array.from(this.messageInput.childNodes)).trim()) { this.messageInput.innerHTML = ''; - if(this.recorder) { - this.btnSend.classList.remove('tgico-send'); - this.btnSend.classList.add('tgico-microphone2'); - } - + appMessagesManager.setTyping('sendMessageCancelAction'); - } else if(!this.btnSend.classList.contains('tgico-send') || !this.recorder) { - if(this.recorder) { - this.btnSend.classList.add('tgico-send'); - this.btnSend.classList.remove('tgico-microphone2'); - } - - let time = Date.now(); + } else { + const time = Date.now(); if(time - this.lastTimeType >= 6000) { this.lastTimeType = time; appMessagesManager.setTyping('sendMessageTypingAction'); } } + + this.updateSendBtn(); }); if(!RichTextProcessor.emojiSupported) { @@ -517,7 +513,7 @@ export class ChatInput { const onBtnSendClick = (e: Event) => { cancelEvent(e); - if(this.btnSend.classList.contains('tgico-send') || !this.recorder) { + if(!this.recorder || this.recording || !this.isInputEmpty()) { if(this.recording) { this.recorder.stop(); } else { @@ -526,9 +522,9 @@ export class ChatInput { } else { this.recorder.start().then(() => { this.recordCanceled = false; - this.btnSend.classList.add('tgico-send'); - this.chatInput.classList.add('is-recording'); + this.chatInput.classList.add('is-recording', 'is-locked'); this.recording = true; + this.updateSendBtn(); opusDecodeController.setKeepAlive(true); this.recordStartTime = Date.now(); @@ -571,7 +567,22 @@ export class ChatInput { r(); }).catch((e: Error) => { - console.error('Recorder start error:', e); + switch(e.name as string) { + case 'NotAllowedError': { + toast('Please allow access to your microphone'); + break; + } + + case 'NotReadableError': { + toast(e.message); + break; + } + + default: + console.error('Recorder start error:', e, e.name, e.message); + toast(e.message); + break; + } }); } }; @@ -591,8 +602,8 @@ export class ChatInput { this.recorder.onstop = () => { this.recording = false; - this.chatInput.classList.remove('is-recording'); - this.btnSend.classList.remove('tgico-send'); + this.chatInput.classList.remove('is-recording', 'is-locked'); + this.updateSendBtn(); this.recordRippleEl.style.transform = ''; }; @@ -674,10 +685,7 @@ export class ChatInput { this.editMsgID = 0; this.messageInput.innerHTML = ''; - if(this.recorder) { - this.btnSend.classList.remove('tgico-send'); - this.btnSend.classList.add('tgico-microphone2'); - } + this.updateSendBtn(); } } @@ -685,6 +693,22 @@ export class ChatInput { this.willSendWebPage = null; }); } + + private isInputEmpty() { + let value = this.messageInput.innerText; + + return !value.trim() && !this.serializeNodes(Array.from(this.messageInput.childNodes)).trim(); + } + + public updateSendBtn() { + let icon: 'send' | 'record'; + + if(!this.recorder || this.recording || !this.isInputEmpty()) icon = 'send'; + else icon = 'record'; + + this.btnSend.classList.toggle('send', icon == 'send'); + this.btnSend.classList.toggle('record', icon == 'record'); + } public serializeNodes(nodes: Node[]): string { return nodes.reduce((str, child: any) => { @@ -711,10 +735,7 @@ export class ChatInput { this.willSendWebPage = null; this.messageInput.innerText = ''; - if(this.recorder) { - this.btnSend.classList.remove('tgico-send'); - this.btnSend.classList.add('tgico-microphone2'); - } + this.updateSendBtn(); } if(clearReply || clearInput) { @@ -776,8 +797,7 @@ export class ChatInput { if(input !== undefined) { this.messageInput.innerHTML = input ? RichTextProcessor.wrapRichText(input) : ''; - this.btnSend.classList.remove('tgico-microphone2'); - this.btnSend.classList.add('tgico-send'); + this.updateSendBtn(); } //appImManager.scrollPosition.restore(); diff --git a/src/components/ripple.ts b/src/components/ripple.ts index facc125a..3312ca3f 100644 --- a/src/components/ripple.ts +++ b/src/components/ripple.ts @@ -1,3 +1,4 @@ +import mediaSizes from "../helpers/mediaSizes"; import { touchSupport } from "../lib/config"; import { findUpClassName } from "../lib/utils"; @@ -15,20 +16,22 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise void; - let drawRipple = (clientX: number, clientY: number) => { - let startTime = Date.now(); - let span = document.createElement('span'); + let animationEndPromise: Promise; + const drawRipple = (clientX: number, clientY: number) => { + const startTime = Date.now(); + const span = document.createElement('span'); - let clickID = rippleClickID++; + const clickID = rippleClickID++; //console.log('ripple drawRipple'); + let duration: number; + handler = () => { + //const duration = isSquare || mediaSizes.isMobile ? 200 : 700; //return; let elapsedTime = Date.now() - startTime; if(elapsedTime < duration) { @@ -96,8 +99,16 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise { + span.addEventListener('animationend', resolve, {once: true}); + }); + r.append(span); + + duration = +window.getComputedStyle(span).getPropertyValue('animation-duration').replace('s', '') * 1000; + //r.classList.add('active'); //handler(); }); @@ -121,12 +132,12 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise { e.cancelBubble = true; e.stopPropagation(); - handler && handler(); + touchEnd(); window.removeEventListener('touchend', touchEnd); }, {once: true}); }, {passive: true}); diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 2647afab..95047f56 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -20,6 +20,7 @@ import { PhotoSize } from '../layer'; import { deferredPromise } from '../helpers/cancellablePromise'; import mediaSizes from '../helpers/mediaSizes'; import { isSafari } from '../helpers/userAgent'; +import { months } from '../helpers/date'; export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: { doc: MyDocument, @@ -279,7 +280,6 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai } export const formatDate = (timestamp: number, monthShort = false, withYear = true) => { - const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'Octomber', 'November', 'December']; const date = new Date(timestamp * 1000); let month = months[date.getMonth()]; diff --git a/src/helpers/date.ts b/src/helpers/date.ts new file mode 100644 index 00000000..5021de21 --- /dev/null +++ b/src/helpers/date.ts @@ -0,0 +1,32 @@ +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']; + +const ONE_DAY = 86400e3; + +// https://stackoverflow.com/a/6117889 +export const getWeekNumber = (date: Date) => { + const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); + const dayNum = d.getUTCDay() || 7; + d.setUTCDate(d.getUTCDate() + 4 - dayNum); + const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); + 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; + const timestamp = date.getTime() / 1000; + + 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((now - timestamp) < (ONE_DAY * 7) && getWeekNumber(date) == getWeekNumber(time)) { // current week + timeStr = days[time.getDay()].slice(0, 3); + } else if(date.getFullYear() != time.getFullYear()) { // different year + timeStr = time.getDate() + '.' + (time.getMonth() + 1) + '.' + time.getFullYear(); + } else { // same year + timeStr = months[time.getMonth()].slice(0, 3) + ' ' + ('0' + time.getDate()).slice(-2); + } + + return timeStr; +}; \ No newline at end of file diff --git a/src/helpers/mediaSizes.ts b/src/helpers/mediaSizes.ts index 1b7a53f7..4aafa600 100644 --- a/src/helpers/mediaSizes.ts +++ b/src/helpers/mediaSizes.ts @@ -80,7 +80,7 @@ class MediaSizes extends EventListenerBase<{ } } - if(this.activeScreen != activeScreen) { + if(this.activeScreen != activeScreen && this.activeScreen !== undefined) { //console.log('changeScreen', this.activeScreen, activeScreen); this.setListenerResult('changeScreen', this.activeScreen, activeScreen); } @@ -102,4 +102,7 @@ class MediaSizes extends EventListenerBase<{ } const mediaSizes = new MediaSizes(); +if(process.env.NODE_ENV != 'production') { + (window as any).mediaSizes = mediaSizes; +} export default mediaSizes; \ No newline at end of file diff --git a/src/index.hbs b/src/index.hbs index b9f71666..240f79f3 100644 --- a/src/index.hbs +++ b/src/index.hbs @@ -452,11 +452,11 @@
- - - - - + + + + +
@@ -492,8 +492,8 @@
+
-
@@ -535,7 +535,10 @@
- +