From 6356b9152c1a458d0a40a580e39076073c0a818a Mon Sep 17 00:00:00 2001 From: morethanwords Date: Sat, 6 Jun 2020 13:53:23 +0300 Subject: [PATCH] Voice recorder check support Delay for open cached chat --- src/components/chatInput.ts | 190 ++++++++++++---------- src/components/lazyLoadQueue.ts | 2 +- src/components/misc.ts | 50 +++--- src/lib/appManagers/appDialogsManager.ts | 6 +- src/lib/appManagers/appImManager.ts | 70 +++++--- src/lib/appManagers/appMessagesManager.ts | 11 ++ src/lib/config.ts | 6 +- src/lib/mtproto/transports/websocket.ts | 6 +- src/pages/page.ts | 10 +- src/pages/pageIm.ts | 70 +++++--- src/scss/components/_typography.scss | 2 +- src/scss/partials/_chat.scss | 3 +- src/scss/partials/_chatBubble.scss | 5 +- src/scss/style.scss | 4 + 14 files changed, 260 insertions(+), 175 deletions(-) diff --git a/src/components/chatInput.ts b/src/components/chatInput.ts index 65fbeee1..ed52315f 100644 --- a/src/components/chatInput.ts +++ b/src/components/chatInput.ts @@ -59,14 +59,7 @@ export class ChatInput { public editMsgID = 0; public noWebPage = false; - private recorder = new Recorder({ - //encoderBitRate: 32, - //encoderPath: "../dist/encoderWorker.min.js", - encoderSampleRate: 48000, - monitorGain: 0, - numberOfChannels: 1, - recordingGain: 1 - }); + private recorder: any; private recording = false; private recordCanceled = false; private recordTimeEl = this.inputContainer.querySelector('.record-time') as HTMLDivElement; @@ -92,6 +85,21 @@ export class ChatInput { this.replyElements.titleEl = this.replyElements.container.querySelector('.reply-title') as HTMLDivElement; this.replyElements.subtitleEl = this.replyElements.container.querySelector('.reply-subtitle') as HTMLDivElement; + try { + this.recorder = new Recorder({ + //encoderBitRate: 32, + //encoderPath: "../dist/encoderWorker.min.js", + encoderSampleRate: 48000, + monitorGain: 0, + numberOfChannels: 1, + recordingGain: 1 + }); + } catch(err) { + this.btnSend.classList.remove('tgico-microphone2'); + this.btnSend.classList.add('tgico-send'); + console.error('Recorder constructor error:', err); + } + this.messageInput.addEventListener('keydown', (e: KeyboardEvent) => { if(e.key == 'Enter') { /* if(e.ctrlKey || e.metaKey) { @@ -144,13 +152,17 @@ export class ChatInput { if(!value.trim() && !this.serializeNodes(Array.from(this.messageInput.childNodes)).trim()) { this.messageInput.innerHTML = ''; - this.btnSend.classList.remove('tgico-send'); - this.btnSend.classList.add('tgico-microphone2'); + 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.btnSend.classList.add('tgico-send'); - this.btnSend.classList.remove('tgico-microphone2'); + } 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(); if(time - this.lastTimeType >= 6000) { @@ -462,7 +474,7 @@ export class ChatInput { }); this.btnSend.addEventListener('click', () => { - if(this.btnSend.classList.contains('tgico-send')) { + if(this.btnSend.classList.contains('tgico-send') || !this.recorder) { if(this.recording) { this.recorder.stop(); } else { @@ -527,74 +539,76 @@ export class ChatInput { opusDecodeController.setKeepAlive(false); }); - this.recorder.onstop = () => { - this.recording = false; - this.inputContainer.classList.remove('is-recording'); - this.btnSend.classList.remove('tgico-send'); - this.recordRippleEl.style.transform = ''; - }; - - this.recorder.ondataavailable = (typedArray: Uint8Array) => { - if(this.recordCanceled) return; - - const duration = (Date.now() - this.recordStartTime) / 1000 | 0; - const dataBlob = new Blob([typedArray], {type: 'audio/ogg'}); - /* const fileName = new Date().toISOString() + ".opus"; - console.log('Recorder data received', typedArray, dataBlob); */ - - /* var url = URL.createObjectURL( dataBlob ); - - var audio = document.createElement('audio'); - audio.controls = true; - audio.src = url; - - var link = document.createElement('a'); - link.href = url; - link.download = fileName; - link.innerHTML = link.download; - - var li = document.createElement('li'); - li.appendChild(link); - li.appendChild(audio); - - document.body.append(li); - - return; */ - - let perf = performance.now(); - opusDecodeController.decode(typedArray, true).then(result => { - console.log('WAVEFORM!:', /* waveform, */performance.now() - perf); - - opusDecodeController.setKeepAlive(false); - - let peerID = appImManager.peerID; - // тут objectURL ставится уже с audio/wav - appMessagesManager.sendFile(peerID, dataBlob, { - isVoiceMessage: true, - isMedia: true, - duration, - waveform: result.waveform, - objectURL: result.url + if(this.recorder) { + this.recorder.onstop = () => { + this.recording = false; + this.inputContainer.classList.remove('is-recording'); + this.btnSend.classList.remove('tgico-send'); + this.recordRippleEl.style.transform = ''; + }; + + this.recorder.ondataavailable = (typedArray: Uint8Array) => { + if(this.recordCanceled) return; + + const duration = (Date.now() - this.recordStartTime) / 1000 | 0; + const dataBlob = new Blob([typedArray], {type: 'audio/ogg'}); + /* const fileName = new Date().toISOString() + ".opus"; + console.log('Recorder data received', typedArray, dataBlob); */ + + /* var url = URL.createObjectURL( dataBlob ); + + var audio = document.createElement('audio'); + audio.controls = true; + audio.src = url; + + var link = document.createElement('a'); + link.href = url; + link.download = fileName; + link.innerHTML = link.download; + + var li = document.createElement('li'); + li.appendChild(link); + li.appendChild(audio); + + document.body.append(li); + + return; */ + + let perf = performance.now(); + opusDecodeController.decode(typedArray, true).then(result => { + console.log('WAVEFORM!:', /* waveform, */performance.now() - perf); + + opusDecodeController.setKeepAlive(false); + + let peerID = appImManager.peerID; + // тут objectURL ставится уже с audio/wav + appMessagesManager.sendFile(peerID, dataBlob, { + isVoiceMessage: true, + isMedia: true, + duration, + waveform: result.waveform, + objectURL: result.url + }); }); - }); - - /* const url = URL.createObjectURL(dataBlob); - - var audio = document.createElement('audio'); - audio.controls = true; - audio.src = url; - - var link = document.createElement('a'); - link.href = url; - link.download = fileName; - link.innerHTML = link.download; - - var li = document.createElement('li'); - li.appendChild(link); - li.appendChild(audio); - - recordingslist.appendChild(li); */ - }; + + /* const url = URL.createObjectURL(dataBlob); + + var audio = document.createElement('audio'); + audio.controls = true; + audio.src = url; + + var link = document.createElement('a'); + link.href = url; + link.download = fileName; + link.innerHTML = link.download; + + var li = document.createElement('li'); + li.appendChild(link); + li.appendChild(audio); + + recordingslist.appendChild(li); */ + }; + } let emoticonsDisplayTimeout = 0; this.toggleEmoticons.onmouseover = (e) => { @@ -650,8 +664,11 @@ export class ChatInput { } else { this.editMsgID = 0; this.messageInput.innerHTML = ''; - this.btnSend.classList.remove('tgico-send'); - this.btnSend.classList.add('tgico-microphone2'); + + if(this.recorder) { + this.btnSend.classList.remove('tgico-send'); + this.btnSend.classList.add('tgico-microphone2'); + } } } @@ -686,8 +703,11 @@ export class ChatInput { this.replyElements.container.classList.remove('active'); this.willSendWebPage = null; this.messageInput.innerText = ''; - this.btnSend.classList.remove('tgico-send'); - this.btnSend.classList.add('tgico-microphone2'); + + if(this.recorder) { + this.btnSend.classList.remove('tgico-send'); + this.btnSend.classList.add('tgico-microphone2'); + } } } diff --git a/src/components/lazyLoadQueue.ts b/src/components/lazyLoadQueue.ts index a9ce6f26..4bdb5871 100644 --- a/src/components/lazyLoadQueue.ts +++ b/src/components/lazyLoadQueue.ts @@ -13,7 +13,7 @@ export default class LazyLoadQueue { private unlockResolve: () => void = null; private log = console.log.bind(console, '[LL]:'); - private debug = false; + private debug = true; private observer: IntersectionObserver; diff --git a/src/components/misc.ts b/src/components/misc.ts index ccf72e6f..fbd03811 100644 --- a/src/components/misc.ts +++ b/src/components/misc.ts @@ -1,9 +1,9 @@ import { whichChild, findUpTag, cancelEvent } from "../lib/utils"; -import Config from "../lib/config"; +import Config, { touchSupport } from "../lib/config"; let rippleClickID = 0; export function ripple(elem: HTMLElement, callback: (id: number) => Promise = () => Promise.resolve(), onEnd: (id: number) => void = null) { - return; + //return; if(elem.querySelector('.c-ripple')) return; let r = document.createElement('div'); @@ -95,30 +95,32 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise { - handler && handler(); - }; - let touchStartFired = false; - elem.addEventListener('touchstart', (e) => { - if(e.touches.length > 1) { - return; - } - - console.log('touchstart', e); - touchStartFired = true; - - let {clientX, clientY} = e.touches[0]; - drawRipple(clientX, clientY); - window.addEventListener('touchend', touchEnd, {once: true}); - - window.addEventListener('touchmove', (e) => { - e.cancelBubble = true; - e.stopPropagation(); + if(touchSupport) { + let touchEnd = () => { handler && handler(); - window.removeEventListener('touchend', touchEnd); - }, {once: true}); - }); + }; + + elem.addEventListener('touchstart', (e) => { + if(e.touches.length > 1) { + return; + } + + console.log('touchstart', e); + touchStartFired = true; + + let {clientX, clientY} = e.touches[0]; + drawRipple(clientX, clientY); + window.addEventListener('touchend', touchEnd, {once: true}); + + window.addEventListener('touchmove', (e) => { + e.cancelBubble = true; + e.stopPropagation(); + handler && handler(); + window.removeEventListener('touchend', touchEnd); + }, {once: true}); + }, {passive: true}); + } elem.addEventListener('mousedown', (e) => { if(elem.dataset.ripple == '0') { diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index ac957f28..eeef4e5d 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -291,6 +291,8 @@ export class AppDialogsManager { private contextMenu = new DialogsContextMenu([this.chatList, this.chatListArchived]); + private debug = false; + constructor() { this.chatsPreloader = putPreloader(null, true); @@ -493,7 +495,7 @@ export class AppDialogsManager { else this.loadedAll = true; } - this.log('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, this.scroll.length, archived); + this.debug && this.log('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, this.scroll.length, archived); this.scroll.onScroll(); } catch(err) { this.log.error(err); @@ -585,7 +587,7 @@ export class AppDialogsManager { (dialog.folder_id == 1 ? this.scrollArchived : this.scroll).reorder(); - this.log('setDialogPosition:', dialog, dom, pos); + this.debug && this.log('setDialogPosition:', dialog, dom, pos); } public setPinnedDelimiter() { diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 6615d3c4..f915210a 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -15,7 +15,6 @@ import lottieLoader from "../lottieLoader"; import appMediaViewer from "./appMediaViewer"; import appSidebarLeft from "./appSidebarLeft"; import appChatsManager from "./appChatsManager"; -import apiUpdatesManager from './apiUpdatesManager'; import { wrapDocument, wrapPhoto, wrapVideo, wrapSticker, wrapReply, wrapAlbum, wrapPoll } from '../../components/wrappers'; import ProgressivePreloader from '../../components/preloader'; import { openBtnMenu, formatPhoneNumber, positionMenu, ripple, parseMenuButtonsTo, horizontalMenu } from '../../components/misc'; @@ -31,13 +30,13 @@ import AvatarElement from '../../components/avatar'; import appInlineBotsManager from './AppInlineBotsManager'; import StickyIntersector from '../../components/stickyIntersector'; import { PopupPeerButton, PopupPeer } from '../../components/popup'; -import { mediaSizes } from '../config'; +import { mediaSizes, touchSupport } from '../config'; -console.log('appImManager included!'); +console.log('appImManager included33!'); appSidebarLeft; // just to include -let testScroll = false; +const testScroll = true; const IGNOREACTIONS = ['messageActionChannelMigrateFrom']; @@ -195,14 +194,7 @@ class ChatContextMenu { }); this.buttons.pin.addEventListener('click', () => { - apiManager.invokeApi('messages.updatePinnedMessage', { - flags: 0, - peer: appPeersManager.getInputPeerByID($rootScope.selectedPeerID), - id: this.msgID - }).then(updates => { - /////this.log('pinned updates:', updates); - apiUpdatesManager.processUpdateMessage(updates); - }); + appMessagesManager.updatePinnedMessage($rootScope.selectedPeerID, this.msgID); }); } } @@ -765,6 +757,7 @@ export class AppImManager { public setPinnedMessage(message: any) { /////this.log('setting pinned message', message); + return; this.pinnedMessageContainer.dataset.mid = '' + message.mid; this.topbar.classList.add('is-pinned-shown'); this.pinnedMessageContent.innerHTML = message.rReply; @@ -826,16 +819,18 @@ export class AppImManager { this.onScrollRAF = window.requestAnimationFrame(() => { lottieLoader.checkAnimations(false, 'chat'); - if(this.isScrollingTimeout) { - clearTimeout(this.isScrollingTimeout); - } else if(!this.chatInner.classList.contains('is-scrolling')) { - this.chatInner.classList.add('is-scrolling'); + if(!touchSupport) { + if(this.isScrollingTimeout) { + clearTimeout(this.isScrollingTimeout); + } else if(!this.chatInner.classList.contains('is-scrolling')) { + this.chatInner.classList.add('is-scrolling'); + } + + this.isScrollingTimeout = setTimeout(() => { + this.chatInner.classList.remove('is-scrolling'); + this.isScrollingTimeout = 0; + }, 300); } - - this.isScrollingTimeout = setTimeout(() => { - this.chatInner.classList.remove('is-scrolling'); - this.isScrollingTimeout = 0; - }, 300); if(this.scroll.scrollHeight - Math.round(this.scroll.scrollTop + this.scroll.offsetHeight) <= 1/* <= 5 */) { this.scroll.parentElement.classList.add('scrolled-down'); @@ -861,6 +856,27 @@ export class AppImManager { this.scroll.addEventListener('scroll', this.onScroll.bind(this)); this.scroll.parentElement.classList.add('scrolled-down'); + + if(touchSupport) { + this.scroll.addEventListener('touchmove', () => { + if(this.isScrollingTimeout) { + clearTimeout(this.isScrollingTimeout); + } else if(!this.chatInner.classList.contains('is-scrolling')) { + this.chatInner.classList.add('is-scrolling'); + } + + this.scroll.addEventListener('touchend', () => { + if(this.isScrollingTimeout) { + clearTimeout(this.isScrollingTimeout); + } + + this.isScrollingTimeout = setTimeout(() => { + this.chatInner.classList.remove('is-scrolling'); + this.isScrollingTimeout = 0; + }, 300); + }, {passive: true}) + }, {passive: true}); + } } public setPeerStatus(needClear = false) { @@ -1026,10 +1042,6 @@ export class AppImManager { this.log('setPeer peerID:', this.peerID, dialog, lastMsgID, topMessage); - if(mediaSizes.isMobile) { - this.selectTab(1); - } - const isJump = lastMsgID != topMessage; // add last message, bc in getHistory will load < max_id const additionMsgID = isJump ? 0 : topMessage; @@ -1066,6 +1078,10 @@ export class AppImManager { //oldChatInner.remove(); !samePeer && this.finishPeerChange(); this.preloader.attach(this.bubblesContainer); + + if(mediaSizes.isMobile) { + this.selectTab(1); + } } //console.timeEnd('appImManager setPeer pre promise'); @@ -1083,6 +1099,10 @@ export class AppImManager { if(pinned && !pinned.deleted) { this.setPinnedMessage(pinned); } + + if(mediaSizes.isMobile) { + this.selectTab(1); + } } else { this.preloader.detach(); } diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 727ed443..6f06e32b 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -1678,6 +1678,17 @@ export class AppMessagesManager { public getPinnedMessage(peerID: number) { return this.getMessage(this.pinnedMessages[peerID] || 0); } + + public updatePinnedMessage(peerID: number, msgID: number) { + apiManager.invokeApi('messages.updatePinnedMessage', { + flags: 0, + peer: appPeersManager.getInputPeerByID(peerID), + id: msgID + }).then(updates => { + /////console.log('pinned updates:', updates); + apiUpdatesManager.processUpdateMessage(updates); + }); + } public saveMessages(apiMessages: any[], options: { isNew?: boolean, diff --git a/src/lib/config.ts b/src/lib/config.ts index ab0cf0a4..ad2cb6bf 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -89,12 +89,16 @@ class MediaSizes { export const mediaSizes = new MediaSizes(); +// @ts-ignore +export const touchSupport = ('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch); + const Config = { Emoji, LatinizeMap, TLD, Countries, - MediaSizes: mediaSizes + MediaSizes: mediaSizes, + touchSupport }; (window as any).Config = Config; export default Config; \ No newline at end of file diff --git a/src/lib/mtproto/transports/websocket.ts b/src/lib/mtproto/transports/websocket.ts index b1f8f731..82fd1fca 100644 --- a/src/lib/mtproto/transports/websocket.ts +++ b/src/lib/mtproto/transports/websocket.ts @@ -146,9 +146,9 @@ export default class Socket extends MTTransport { this.ws = new WebSocket(this.url, 'binary'); this.ws.binaryType = 'arraybuffer'; - this.ws.onopen = this.handleOpen; - this.ws.onclose = this.handleClose; - this.ws.onmessage = this.handleMessage; + this.ws.addEventListener('open', this.handleOpen); + this.ws.addEventListener('close', this.handleClose); + this.ws.addEventListener('message', this.handleMessage); }; handleOpen = () => { diff --git a/src/pages/page.ts b/src/pages/page.ts index 3753bd54..fb1fcbd7 100644 --- a/src/pages/page.ts +++ b/src/pages/page.ts @@ -17,9 +17,13 @@ export default class Page { if(!this.installed) { if(this.onFirstMount) { - let res = this.onFirstMount(...args); - if(res instanceof Promise) { - await res; + try { + const res = this.onFirstMount(...args); + if(res instanceof Promise) { + await res; + } + } catch(err) { + console.error('PAGE MOUNT ERROR:', err); } } diff --git a/src/pages/pageIm.ts b/src/pages/pageIm.ts index 2cc2e5e4..3def6ba9 100644 --- a/src/pages/pageIm.ts +++ b/src/pages/pageIm.ts @@ -5,7 +5,48 @@ import Page from "./page"; let onFirstMount = () => { //return; - let promise = import('../lib/appManagers/appImManager')/* Promise.resolve() */.then(() => {//import('../lib/services').then(services => { + const promise = import('../lib/appManagers/appImManager'); + promise.finally(() => { + //alert('pageIm!'); + + //AudioContext && global.navigator && global.navigator.mediaDevices && global.navigator.mediaDevices.getUserMedia && global.WebAssembly; + + /* // @ts-ignore + var AudioContext = globalThis.AudioContext || globalThis.webkitAudioContext; + alert('AudioContext:' + typeof(AudioContext)); + // @ts-ignore + alert('global.navigator:' + typeof(navigator)); + alert('navigator.mediaDevices:' + typeof(navigator.mediaDevices)); + alert('navigator.mediaDevices.getUserMedia:' + typeof(navigator.mediaDevices?.getUserMedia)); + alert('global.WebAssembly:' + typeof(WebAssembly)); */ + + // @ts-ignore + if(process.env.NODE_ENV != 'production') { + import('../lib/services'); + } + + //(Array.from(document.getElementsByClassName('rp')) as HTMLElement[]).forEach(el => ripple(el)); + + Array.from(document.getElementsByClassName('btn-menu-toggle')).forEach((el) => { + el.addEventListener('click', (e) => { + //console.log('click pageIm'); + if(!el.classList.contains('btn-menu-toggle')) return false; + + //window.removeEventListener('mousemove', onMouseMove); + let openedMenu = el.querySelector('.btn-menu') as HTMLDivElement; + e.cancelBubble = true; + + if(el.classList.contains('menu-open')) { + el.classList.remove('menu-open'); + openedMenu.classList.remove('active'); + } else { + openBtnMenu(openedMenu); + } + }); + }); + }) + + //let promise = /* Promise.resolve() */.then(() => {//import('../lib/services').then(services => { /* fetch('assets/img/camomile.jpg') .then(res => res.blob()) .then(blob => { @@ -44,32 +85,7 @@ let onFirstMount = () => { toggleEmoticons.classList.toggle('active'); }; */ - - // @ts-ignore - if(process.env.NODE_ENV != 'production') { - import('../lib/services'); - } - - //(Array.from(document.getElementsByClassName('rp')) as HTMLElement[]).forEach(el => ripple(el)); - - Array.from(document.getElementsByClassName('btn-menu-toggle')).forEach((el) => { - el.addEventListener('click', (e) => { - //console.log('click pageIm'); - if(!el.classList.contains('btn-menu-toggle')) return false; - - //window.removeEventListener('mousemove', onMouseMove); - let openedMenu = el.querySelector('.btn-menu') as HTMLDivElement; - e.cancelBubble = true; - - if(el.classList.contains('menu-open')) { - el.classList.remove('menu-open'); - openedMenu.classList.remove('active'); - } else { - openBtnMenu(openedMenu); - } - }); - }); - }); + //}); return promise; }; diff --git a/src/scss/components/_typography.scss b/src/scss/components/_typography.scss index 97a4be83..1bbc33ce 100644 --- a/src/scss/components/_typography.scss +++ b/src/scss/components/_typography.scss @@ -4,9 +4,9 @@ a { html { line-height: 1.5; - font-weight: normal; } + h1, h2, h3, h4, h5, h6 { line-height: 1.3; } diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index 731f8f7a..ba1feaad 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -599,6 +599,7 @@ $time-background: rgba(0, 0, 0, .35); overflow: hidden; position: relative; padding: 0 .5rem; + -webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix safari overflow > .scrollable { padding: 0 .75rem; @@ -683,7 +684,7 @@ $time-background: rgba(0, 0, 0, .35); } &.is-scrolling .is-sticky { - opacity: 1; + opacity: 0.99999; // 0.99999 сделано для сафари, т.к. без этого будет прыжок при скролле в самом низу или верху } } diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 4ce8709a..d9726928 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -90,10 +90,11 @@ //z-index: 3; z-index: 2; pointer-events: none; + transition: opacity .3s ease; + opacity: 0.99999; // for safari &.is-sticky { - transition: opacity .3s ease; - opacity: 0; + opacity: 0.00001; // for safari } } diff --git a/src/scss/style.scss b/src/scss/style.scss index 485462fb..62e11ec2 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -211,6 +211,10 @@ input { caret-color: $button-primary-background; } +input, textarea { + -webkit-appearance: none; +} + .subtitle { /* font-weight: 500; */ color: #707579;