Browse Source

Fix moving avatars after chat jump

Handle instant peer changing correctly
Display unread badge only in chats
Delay opening on ESG hover
Lazy load sticker sets thumbs
Fix onchanging profile info flick
Hide shared media menu tabs
Scroll on click to shared media tab
Fix media tabs scroll position
Fix voice messages playback in search on iOS
Hover & play on waveform
Better input fields border animation
Don't lock scroll on horizontal scrollables if unnecessary
Restrict editing & deleting outgoing messages
Display avatars in notifications
Open chat on notification click
Don't close country selector on scrolling
Select single country by enter
Fix jumping text in context menus
Changed preloader color
master
Eduard Kuzmenko 4 years ago
parent
commit
198eea41ee
  1. 4
      README.md
  2. 65
      src/components/appSearchSuper..ts
  3. 5
      src/components/audio.ts
  4. 16
      src/components/chat/bubbles.ts
  5. 2
      src/components/chat/chat.ts
  6. 9
      src/components/chat/input.ts
  7. 36
      src/components/emoticonsDropdown/index.ts
  8. 13
      src/components/emoticonsDropdown/tabs/stickers.ts
  9. 9
      src/components/horizontalMenu.ts
  10. 6
      src/components/inputField.ts
  11. 2
      src/components/inputSearch.ts
  12. 4
      src/components/scrollable.ts
  13. 4
      src/components/sidebarRight/tabs/sharedMedia.ts
  14. 4
      src/config/app.ts
  15. 9
      src/lang.ts
  16. 4
      src/lib/appManagers/appChatsManager.ts
  17. 1
      src/lib/appManagers/appDialogsManager.ts
  18. 5
      src/lib/appManagers/appImManager.ts
  19. 70
      src/lib/appManagers/appMessagesManager.ts
  20. 10
      src/lib/appManagers/appNotificationsManager.ts
  21. 28
      src/lib/appManagers/appProfileManager.ts
  22. 19
      src/lib/mtproto/networker.ts
  23. 5
      src/lib/mtproto/serverTimeManager.ts
  24. 2
      src/lib/rootScope.ts
  25. 3
      src/pages/pageAuthCode.ts
  26. 63
      src/pages/pageSignIn.ts
  27. 4
      src/scss/partials/_audio.scss
  28. 4
      src/scss/partials/_button.scss
  29. 12
      src/scss/partials/_chat.scss
  30. 4
      src/scss/partials/_chatBubble.scss
  31. 50
      src/scss/partials/_input.scss
  32. 2
      src/scss/partials/_preloader.scss
  33. 5
      src/scss/partials/_rightSidebar.scss
  34. 11
      src/scss/partials/popups/_createPoll.scss

4
README.md

@ -42,9 +42,9 @@ Source maps are included in production build for your convenience. @@ -42,9 +42,9 @@ Source maps are included in production build for your convenience.
Should be applied like that: http://localhost:8080/?test=1
### Troubleshooting
### Troubleshooting & Suggesting
If you find an issue with this app, let Telegram know using the [Suggestions Platform](https://bugs.telegram.org/c/4002).
If you find an issue with this app or wish something to be added, let Telegram know using the [Suggestions Platform](https://bugs.telegram.org/c/4002).
### Licensing

65
src/components/appSearchSuper..ts

@ -70,10 +70,10 @@ export default class AppSearchSuper { @@ -70,10 +70,10 @@ export default class AppSearchSuper {
public tabs: {[t in SearchSuperType]: HTMLDivElement} = {} as any;
public mediaTab: SearchSuperMediaTab;
public tabSelected: HTMLElement;
public container: HTMLElement;
public nav: HTMLElement;
private navScrollableContainer: HTMLDivElement;
private tabsContainer: HTMLElement;
private tabsMenu: HTMLElement;
private prevTabId = -1;
@ -112,6 +112,8 @@ export default class AppSearchSuper { @@ -112,6 +112,8 @@ export default class AppSearchSuper {
private membersList: SortedUserList;
private skipScroll: boolean;
// * arguments
public mediaTabs: SearchSuperMediaTab[];
public scrollable: Scrollable;
@ -128,7 +130,7 @@ export default class AppSearchSuper { @@ -128,7 +130,7 @@ export default class AppSearchSuper {
this.container = document.createElement('div');
this.container.classList.add('search-super');
const navScrollableContainer = document.createElement('div');
const navScrollableContainer = this.navScrollableContainer = document.createElement('div');
navScrollableContainer.classList.add('search-super-tabs-scrollable', 'menu-horizontal-scrollable', 'sticky');
const navScrollable = new ScrollableX(navScrollableContainer);
@ -185,48 +187,67 @@ export default class AppSearchSuper { @@ -185,48 +187,67 @@ export default class AppSearchSuper {
this.searchGroupMedia = new SearchGroup('', 'messages', true);
this.scrollable.onScrolledBottom = () => {
if(this.tabSelected && this.tabSelected.childElementCount/* && false */) {
if(this.mediaTab.contentTab && this.mediaTab.contentTab.childElementCount/* && false */) {
//this.log('onScrolledBottom will load media');
this.load(true);
}
};
//this.scroll.attachSentinels(undefined, 400);
this.selectTab = horizontalMenu(this.tabsMenu, this.tabsContainer, (id, tabContent) => {
if(this.prevTabId === id) return;
this.selectTab = horizontalMenu(this.tabsMenu, this.tabsContainer, (id, tabContent, animate) => {
if(this.prevTabId === id && !this.skipScroll) {
this.scrollable.scrollIntoViewNew(this.container, 'start');
return;
}
const newMediaTab = this.mediaTabs[id];
if(this.onChangeTab) {
this.onChangeTab(newMediaTab);
}
const fromMediaTab = this.mediaTab;
this.mediaTab = newMediaTab;
if(this.prevTabId !== -1) {
if(this.prevTabId !== -1 && animate) {
this.onTransitionStart();
}
this.mediaTab.scroll = {scrollTop: this.scrollable.scrollTop, scrollHeight: this.scrollable.scrollHeight};
if(this.skipScroll) {
this.skipScroll = false;
} else {
const offsetTop = this.container.offsetTop;
let scrollTop = this.scrollable.scrollTop;
if(scrollTop < offsetTop) {
this.scrollable.scrollIntoViewNew(this.container, 'start');
scrollTop = offsetTop;
}
const newMediaTab = this.mediaTabs[id];
this.tabSelected = tabContent.firstElementChild as HTMLDivElement;
fromMediaTab.scroll = {scrollTop: scrollTop, scrollHeight: this.scrollable.scrollHeight};
if(newMediaTab.scroll === undefined) {
const rect = this.container.getBoundingClientRect();
const rect2 = this.container.parentElement.getBoundingClientRect();
const diff = rect.y - rect2.y;
if(this.scrollable.scrollTop > diff) {
if(scrollTop > diff) {
newMediaTab.scroll = {scrollTop: diff, scrollHeight: 0};
}
}
if(newMediaTab.scroll) {
const diff = this.mediaTab.scroll.scrollTop - newMediaTab.scroll.scrollTop;
const diff = fromMediaTab.scroll.scrollTop - newMediaTab.scroll.scrollTop;
//console.log('what you gonna do', this.goingHard, diff);
//this.scrollable.scrollTop = scrollTop;
if(diff/* && diff < 0 */) {
this.tabSelected.style.transform = `translateY(${diff}px)`;
/* if(diff > -(fromMediaTab.contentTab.scrollHeight + this.nav.scrollHeight)) {
fromMediaTab.contentTab.style.transform = `translateY(${diff}px)`;
this.scrollable.scrollTop = scrollTop - diff;
} else { */
newMediaTab.contentTab.style.transform = `translateY(${diff}px)`;
//}
}
}
this.mediaTab = newMediaTab;
if(this.onChangeTab) {
this.onChangeTab(this.mediaTab);
}
/* if(this.prevTabId !== -1 && nav.offsetTop) {
@ -236,7 +257,7 @@ export default class AppSearchSuper { @@ -236,7 +257,7 @@ export default class AppSearchSuper {
/* this.log('setVirtualContainer', id, this.sharedMediaSelected, this.sharedMediaSelected.childElementCount);
this.scroll.setVirtualContainer(this.sharedMediaSelected); */
if(this.prevTabId !== -1 && !this.tabSelected.childElementCount) { // quick brown fix
if(this.prevTabId !== -1 && !newMediaTab.contentTab.childElementCount) { // quick brown fix
//this.contentContainer.classList.remove('loaded');
this.load(true);
}
@ -247,7 +268,7 @@ export default class AppSearchSuper { @@ -247,7 +268,7 @@ export default class AppSearchSuper {
//console.log('what y', this.tabSelected.style.transform);
if(this.mediaTab.scroll !== undefined) {
this.tabSelected.style.transform = '';
this.mediaTab.contentTab.style.transform = '';
this.scrollable.scrollTop = this.mediaTab.scroll.scrollTop;
}
@ -1097,6 +1118,7 @@ export default class AppSearchSuper { @@ -1097,6 +1118,7 @@ export default class AppSearchSuper {
}
let firstMediaTab: SearchSuperMediaTab;
let count = 0;
mediaTabs.forEach(mediaTab => {
const counter = counters.find(c => c.filter._ === mediaTab.inputFilter);
@ -1107,6 +1129,8 @@ export default class AppSearchSuper { @@ -1107,6 +1129,8 @@ export default class AppSearchSuper {
if(counter.count && firstMediaTab === undefined) {
firstMediaTab = mediaTab;
}
if(counter.count) ++count;
});
const membersTab = this.mediaTabsMap.get('members');
@ -1120,8 +1144,11 @@ export default class AppSearchSuper { @@ -1120,8 +1144,11 @@ export default class AppSearchSuper {
this.container.classList.toggle('hide', !firstMediaTab);
this.container.parentElement.classList.toggle('search-empty', !firstMediaTab);
if(firstMediaTab) {
this.skipScroll = true;
this.selectTab(this.mediaTabs.indexOf(firstMediaTab), false);
firstMediaTab.menuTab.classList.add('active');
this.navScrollableContainer.classList.toggle('hide', count <= 1);
}
}

5
src/components/audio.ts

@ -217,9 +217,10 @@ function wrapVoiceMessage(audioEl: AudioElement) { @@ -217,9 +217,10 @@ function wrapVoiceMessage(audioEl: AudioElement) {
e.preventDefault();
if(!audio.paused) {
audio.pause();
}
scrub(e);
mousedown = true;
}
});
progress.addEventListener('mouseup', (e) => {
if (mousemove && mousedown) {
@ -437,7 +438,7 @@ export default class AudioElement extends HTMLElement { @@ -437,7 +438,7 @@ export default class AudioElement extends HTMLElement {
const getDownloadPromise = () => appDocsManager.downloadDoc(doc);
if(isVoice) {
if(isRealVoice) {
if(!preloader) {
preloader = new ProgressivePreloader({
cancelable: true

16
src/components/chat/bubbles.ts

@ -168,6 +168,7 @@ export default class ChatBubbles { @@ -168,6 +168,7 @@ export default class ChatBubbles {
const message = this.chat.getMessage(mid);
if(+bubble.dataset.timestamp >= (message.date + serverTimeManager.serverTimeOffset - 1)) {
this.bubbleGroups.addBubble(bubble, message, false);
return;
}
@ -1025,6 +1026,7 @@ export default class ChatBubbles { @@ -1025,6 +1026,7 @@ export default class ChatBubbles {
// if scroll down after search
if(history.indexOf(historyStorage.maxId) !== -1) {
this.scrollable.loadedAll.bottom = true;
return;
}
@ -1414,6 +1416,10 @@ export default class ChatBubbles { @@ -1414,6 +1416,10 @@ export default class ChatBubbles {
const samePeer = this.peerId === peerId;
/* if(samePeer && this.chat.setPeerPromise) {
return {cached: true, promise: this.chat.setPeerPromise};
} */
const historyStorage = this.appMessagesManager.getHistoryStorage(peerId, this.chat.threadId);
let topMessage = this.chat.type === 'pinned' ? this.appMessagesManager.pinnedMessages[peerId].maxId : historyStorage.maxId ?? 0;
const isTarget = lastMsgId !== undefined;
@ -1497,9 +1503,15 @@ export default class ChatBubbles { @@ -1497,9 +1503,15 @@ export default class ChatBubbles {
this.isFirstLoad = true;
}
const oldChatInner = this.chatInner;
this.cleanup();
this.chatInner = document.createElement('div');
if(samePeer) {
this.chatInner.className = oldChatInner.className;
this.chatInner.classList.remove('disable-hover', 'is-scrolling');
} else {
this.chatInner.classList.add('bubbles-inner');
}
this.lazyLoadQueue.lock();
@ -1590,6 +1602,8 @@ export default class ChatBubbles { @@ -1590,6 +1602,8 @@ export default class ChatBubbles {
this.scrollable.scrollTop = 99999;
}
this.onScroll();
this.chat.dispatchEvent('setPeer', lastMsgId, !isJump);
// warning
@ -1718,7 +1732,7 @@ export default class ChatBubbles { @@ -1718,7 +1732,7 @@ export default class ChatBubbles {
public setBubblePosition(bubble: HTMLElement, message: any, reverse: boolean) {
const dateMessage = this.getDateContainerByMessage(message, reverse);
if(this.chat.type === 'scheduled' || this.chat.type === 'pinned') {
if(this.chat.type === 'scheduled' || this.chat.type === 'pinned' || true) {
const offset = this.stickyIntersector ? 2 : 1;
let children = Array.from(dateMessage.container.children).slice(offset) as HTMLElement[];
let i = 0, foundMidOnSameTimestamp = 0;

2
src/components/chat/chat.ts

@ -206,6 +206,8 @@ export default class Chat extends EventListenerBase<{ @@ -206,6 +206,8 @@ export default class Chat extends EventListenerBase<{
if(!samePeer) {
rootScope.broadcast('peer_changing', this);
this.peerId = peerId;
} else if(this.setPeerPromise) {
return;
}
//console.time('appImManager setPeer');

9
src/components/chat/input.ts

@ -157,9 +157,6 @@ export default class ChatInput { @@ -157,9 +157,6 @@ export default class ChatInput {
this.chatInput.append(this.inputContainer);
this.goDownBtn = Button('bubbles-go-down btn-corner btn-circle z-depth-1 hide', {icon: 'arrow_down'});
this.goDownUnreadBadge = document.createElement('span');
this.goDownUnreadBadge.classList.add('badge', 'badge-24', 'badge-primary');
this.goDownBtn.append(this.goDownUnreadBadge);
this.inputContainer.append(this.goDownBtn);
attachClickEvent(this.goDownBtn, (e) => {
@ -257,6 +254,10 @@ export default class ChatInput { @@ -257,6 +254,10 @@ export default class ChatInput {
this.inputMessageContainer.classList.add('input-message-container');
if(this.chat.type === 'chat') {
this.goDownUnreadBadge = document.createElement('span');
this.goDownUnreadBadge.classList.add('badge', 'badge-24', 'badge-primary');
this.goDownBtn.append(this.goDownUnreadBadge);
this.btnScheduled = ButtonIcon('scheduled', {noRipple: true});
this.btnScheduled.classList.add('btn-scheduled', 'hide');
@ -376,7 +377,7 @@ export default class ChatInput { @@ -376,7 +377,7 @@ export default class ChatInput {
this.inputContainer.append(this.btnCancelRecord, this.btnSendContainer);
emoticonsDropdown.attachButtonListener(this.btnToggleEmoticons);
emoticonsDropdown.attachButtonListener(this.btnToggleEmoticons, this.listenerSetter);
emoticonsDropdown.events.onOpen.push(this.onEmoticonsOpen);
emoticonsDropdown.events.onClose.push(this.onEmoticonsClose);

36
src/components/emoticonsDropdown/index.ts

@ -8,7 +8,7 @@ import { isTouchSupported } from "../../helpers/touchSupport"; @@ -8,7 +8,7 @@ import { isTouchSupported } from "../../helpers/touchSupport";
import appChatsManager from "../../lib/appManagers/appChatsManager";
import appImManager from "../../lib/appManagers/appImManager";
import rootScope from "../../lib/rootScope";
import { blurActiveElement, whichChild } from "../../helpers/dom";
import { attachClickEvent, blurActiveElement, whichChild } from "../../helpers/dom";
import animationIntersector from "../animationIntersector";
import { horizontalMenu } from "../horizontalMenu";
import LazyLoadQueue, { LazyLoadQueueIntersector } from "../lazyLoadQueue";
@ -24,6 +24,7 @@ import AppGifsTab from "../sidebarRight/tabs/gifs"; @@ -24,6 +24,7 @@ import AppGifsTab from "../sidebarRight/tabs/gifs";
import AppStickersTab from "../sidebarRight/tabs/stickers";
import findUpClassName from "../../helpers/dom/findUpClassName";
import findUpTag from "../../helpers/dom/findUpTag";
import ListenerSetter from "../../helpers/listenerSetter";
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
@ -32,7 +33,9 @@ export interface EmoticonsTab { @@ -32,7 +33,9 @@ export interface EmoticonsTab {
onCloseAfterTimeout?: () => void
}
const test = false;
const KEEP_OPEN = false;
const TOGGLE_TIMEOUT = 200;
const ANIMATION_DURATION = 200;
export class EmoticonsDropdown {
public static lazyLoadQueue = new LazyLoadQueue();
@ -72,36 +75,36 @@ export class EmoticonsDropdown { @@ -72,36 +75,36 @@ export class EmoticonsDropdown {
this.element = document.getElementById('emoji-dropdown') as HTMLDivElement;
}
public attachButtonListener(button: HTMLElement) {
public attachButtonListener(button: HTMLElement, listenerSetter: ListenerSetter) {
let firstTime = true;
if(isTouchSupported) {
button.addEventListener('click', () => {
attachClickEvent(button, () => {
if(firstTime) {
firstTime = false;
this.toggle(true);
} else {
this.toggle();
}
});
}, {listenerSetter});
} else {
button.onmouseover = (e) => {
listenerSetter.add(button, 'mouseover', (e) => {
//console.log('onmouseover button');
clearTimeout(this.displayTimeout);
//this.displayTimeout = setTimeout(() => {
if(firstTime) {
button.onmouseout = this.onMouseOut;
listenerSetter.add(button, 'mouseout', this.onMouseOut);
firstTime = false;
}
clearTimeout(this.displayTimeout);
this.displayTimeout = window.setTimeout(() => {
this.toggle(true);
//}, 0/* 200 */);
};
}, TOGGLE_TIMEOUT);
});
}
}
private onMouseOut = (e: MouseEvent) => {
if(test) return;
if(KEEP_OPEN) return;
clearTimeout(this.displayTimeout);
if(!this.element.classList.contains('active')) return;
const toElement = (e as any).toElement as Element;
@ -109,10 +112,9 @@ export class EmoticonsDropdown { @@ -109,10 +112,9 @@ export class EmoticonsDropdown {
return;
}
clearTimeout(this.displayTimeout);
this.displayTimeout = window.setTimeout(() => {
this.toggle(false);
}, 200);
}, TOGGLE_TIMEOUT);
};
private init() {
@ -265,7 +267,7 @@ export class EmoticonsDropdown { @@ -265,7 +267,7 @@ export class EmoticonsDropdown {
this.container.classList.remove('disable-hover');
this.events.onOpenAfter.forEach(cb => cb());
}, isTouchSupported ? 0 : 200);
}, isTouchSupported ? 0 : ANIMATION_DURATION);
// ! can't use together with resizeObserver
/* if(isTouchSupported) {
@ -302,7 +304,7 @@ export class EmoticonsDropdown { @@ -302,7 +304,7 @@ export class EmoticonsDropdown {
this.container.classList.remove('disable-hover');
this.events.onCloseAfter.forEach(cb => cb());
}, isTouchSupported ? 0 : 200);
}, isTouchSupported ? 0 : ANIMATION_DURATION);
/* if(isTouchSupported) {
const scrollHeight = this.container.scrollHeight;

13
src/components/emoticonsDropdown/tabs/stickers.ts

@ -209,11 +209,14 @@ export default class StickersTab implements EmoticonsTab { @@ -209,11 +209,14 @@ export default class StickersTab implements EmoticonsTab {
//console.log('got stickerSet', stickerSet, li);
if(stickerSet.set.thumbs?.length) {
EmoticonsDropdown.lazyLoadQueue.push({
div: button,
load: () => {
const downloadOptions = appStickersManager.getStickerSetThumbDownloadOptions(stickerSet.set);
const promise = appDownloadManager.download(downloadOptions);
if(stickerSet.set.pFlags.animated) {
promise
return promise
.then(readBlobAsText)
//.then(JSON.parse)
.then(json => {
@ -229,17 +232,21 @@ export default class StickersTab implements EmoticonsTab { @@ -229,17 +232,21 @@ export default class StickersTab implements EmoticonsTab {
});
} else {
const image = new Image();
promise.then(blob => {
return promise.then(blob => {
renderImageFromUrl(image, URL.createObjectURL(blob), () => {
button.append(image);
});
});
}
}
});
} else if(stickerSet.documents[0]._ !== 'documentEmpty') { // as thumb will be used first sticker
wrapSticker({
doc: stickerSet.documents[0],
div: button as any,
group: EMOTICONSSTICKERGROUP
group: EMOTICONSSTICKERGROUP,
lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue
}); // kostil
}
}

9
src/components/horizontalMenu.ts

@ -12,7 +12,7 @@ import { fastRaf } from "../helpers/schedulers"; @@ -12,7 +12,7 @@ import { fastRaf } from "../helpers/schedulers";
import { FocusDirection } from "../helpers/fastSmoothScroll";
import findUpAsChild from "../helpers/dom/findUpAsChild";
export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?: (id: number, tabContent: HTMLDivElement) => void, onTransitionEnd?: () => void, transitionTime = 250, scrollableX?: ScrollableX) {
export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?: (id: number, tabContent: HTMLDivElement, animate: boolean) => void | boolean, onTransitionEnd?: () => void, transitionTime = 250, scrollableX?: ScrollableX) {
const selectTab = TransitionSlider(content, tabs || content.dataset.animation === 'tabs' ? 'tabs' : 'navigation', transitionTime, onTransitionEnd);
if(tabs) {
@ -29,7 +29,12 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick? @@ -29,7 +29,12 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
const selectTarget = (target: HTMLElement, id: number, animate = true) => {
const tabContent = content.children[id] as HTMLDivElement;
if(onClick) onClick(id, tabContent);
if(onClick) {
const canChange = onClick(id, tabContent, animate);
if(canChange !== undefined && !canChange) {
return;
}
}
if(scrollableX) {
scrollableX.scrollIntoViewNew(target.parentElement.children[id] as HTMLElement, 'center', undefined, undefined, animate ? undefined : FocusDirection.Static, transitionTime, 'x');

6
src/components/inputField.ts

@ -159,6 +159,12 @@ class InputField { @@ -159,6 +159,12 @@ class InputField {
}
}
if(label || placeholder) {
const border = document.createElement('div');
border.classList.add('input-field-border');
this.container.append(border);
}
if(label) {
this.label = document.createElement('label');
this.setLabel();

2
src/components/inputSearch.ts

@ -43,7 +43,7 @@ export default class InputSearch { @@ -43,7 +43,7 @@ export default class InputSearch {
this.input.addEventListener('input', this.onInput);
this.clearBtn.addEventListener('click', this.onClearClick);
this.container.append(this.input, searchIcon, this.clearBtn);
this.container.append(searchIcon, this.clearBtn);
}
onInput = () => {

4
src/components/scrollable.ts

@ -250,8 +250,8 @@ export class ScrollableX extends ScrollableBase { @@ -250,8 +250,8 @@ export class ScrollableX extends ScrollableBase {
if(!isTouchSupported) {
const scrollHorizontally = (e: any) => {
if(!e.deltaX) {
this.container!.scrollLeft += e.deltaY / 4;
if(!e.deltaX && this.container.scrollWidth > this.container.clientWidth) {
this.container.scrollLeft += e.deltaY / 4;
cancelEvent(e);
}
};

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

@ -46,10 +46,10 @@ import Scrollable from "../../scrollable"; @@ -46,10 +46,10 @@ import Scrollable from "../../scrollable";
import { isTouchSupported } from "../../../helpers/touchSupport";
let setText = (text: string, row: Row) => {
fastRaf(() => {
//fastRaf(() => {
row.title.innerHTML = text;
row.container.style.display = '';
});
//});
};
type ListLoaderResult<T> = {count: number, items: any[]};

4
src/config/app.ts

@ -12,8 +12,8 @@ @@ -12,8 +12,8 @@
const App = {
id: 1025907,
hash: '452b0359b988148995f22ff0f4229750',
version: '0.4.1',
langPackVersion: '0.1.3',
version: '0.4.2',
langPackVersion: '0.1.4',
langPack: 'macos',
langPackCode: 'en',
domains: [] as string[],

9
src/lang.ts

@ -91,6 +91,15 @@ const lang = { @@ -91,6 +91,15 @@ const lang = {
"Profile": "Profile",
"Saved": "Saved",
"ReportBug": "Report Bug",
"Notifications.Count": {
"one_value": "%d notification",
"other_value": "%d notifications",
},
"Notifications.Forwarded": {
"one_value": "Forwarded %d message",
"other_value": "Forwarded %d messages"
},
"Notifications.New": "New notification",
// * android
"ActionCreateChannel": "Channel created",

4
src/lib/appManagers/appChatsManager.ts

@ -477,7 +477,7 @@ export class AppChatsManager { @@ -477,7 +477,7 @@ export class AppChatsManager {
apiUpdatesManager.processUpdateMessage(updates);
const channelId = updates.chats[0].id;
rootScope.broadcast('history_focus', -channelId);
rootScope.broadcast('history_focus', {peerId: -channelId});
return channelId;
});
@ -503,7 +503,7 @@ export class AppChatsManager { @@ -503,7 +503,7 @@ export class AppChatsManager {
apiUpdatesManager.processUpdateMessage(updates);
const chatId = (updates as any as Updates.updates).chats[0].id;
rootScope.broadcast('history_focus', -chatId);
rootScope.broadcast('history_focus', {peerId: -chatId});
return chatId;
});

1
src/lib/appManagers/appDialogsManager.ts

@ -233,6 +233,7 @@ export class AppDialogsManager { @@ -233,6 +233,7 @@ export class AppDialogsManager {
};
this.setListClickListener(archivedChatList, null, true);
//this.setListClickListener(archivedChatList, null, true); // * to test peer changing
this.chatsPreloader = putPreloader(null, true);

5
src/lib/appManagers/appImManager.ts

@ -169,8 +169,9 @@ export class AppImManager { @@ -169,8 +169,9 @@ export class AppImManager {
}
});
rootScope.on('history_focus', (peerId) => {
this.setInnerPeer(peerId);
rootScope.on('history_focus', (e) => {
const {peerId, mid} = e;
this.setInnerPeer(peerId, mid);
});
rootScope.on('peer_changing', (chat) => {

70
src/lib/appManagers/appMessagesManager.ts

@ -2953,7 +2953,11 @@ export class AppMessagesManager { @@ -2953,7 +2953,11 @@ export class AppMessagesManager {
}
}
public canMessageBeEdited(message: any, kind: 'text' | 'poll') {
private canMessageBeEdited(message: any, kind: 'text' | 'poll') {
if(message.pFlags.is_outgoing) {
return false;
}
const goodMedias = [
'messageMediaPhoto',
'messageMediaDocument',
@ -2992,7 +2996,7 @@ export class AppMessagesManager { @@ -2992,7 +2996,7 @@ export class AppMessagesManager {
return true;
}
if((message.date < tsNow(true) - (2 * 86400) && message.media?._ !== 'messageMediaPoll') || !message.pFlags.out) {
if((message.date < (tsNow(true) - (2 * 86400)) && message.media?._ !== 'messageMediaPoll') || !message.pFlags.out) {
return false;
}
@ -3005,7 +3009,7 @@ export class AppMessagesManager { @@ -3005,7 +3009,7 @@ export class AppMessagesManager {
|| message.fromId === rootScope.myId
|| appChatsManager.getChat(message.peerId)._ === 'chat'
|| appChatsManager.hasRights(message.peerId, 'delete_messages')
);
) && !message.pFlags.is_outgoing;
}
public applyConversations(dialogsResult: MessagesPeerDialogs.messagesPeerDialogs) {
@ -4919,60 +4923,31 @@ export class AppMessagesManager { @@ -4919,60 +4923,31 @@ export class AppMessagesManager {
peerTypeNotifySettings: PeerNotifySettings
}> = {}) {
const peerId = this.getMessagePeer(message);
let peerString: string;
const notification: NotifyOptions = {};
var notificationMessage: string,
notificationPhoto: any;
const _ = (str: string) => str;
const peerString = appPeersManager.getPeerString(peerId);
let notificationMessage: string;
const localSettings = appNotificationsManager.getLocalSettings();
if(options.peerTypeNotifySettings.show_previews) {
if(message._ === 'message' && message.fwd_from && options.fwdCount) {
notificationMessage = 'Forwarded ' + options.fwdCount + ' messages';//fwdMessagesPluralize(options.fwd_count);
notificationMessage = I18n.format('Notifications.Forwarded', true, [options.fwdCount]);
} else {
notificationMessage = this.wrapMessageForReply(message, undefined, undefined, true);
}
} else {
notificationMessage = 'New notification';
notificationMessage = I18n.format('Notifications.New', true);
}
if(peerId > 0) {
const fromUser = appUsersManager.getUser(message.fromId);
const fromPhoto = appUsersManager.getUserPhoto(message.fromId);
notification.title = (fromUser.first_name || '') +
(fromUser.first_name && fromUser.last_name ? ' ' : '') +
(fromUser.last_name || '');
if(!notification.title) {
notification.title = fromUser.phone || _('conversation_unknown_user_raw');
}
notificationPhoto = fromPhoto;
peerString = appUsersManager.getUserString(peerId);
} else {
notification.title = appChatsManager.getChat(-peerId).title || _('conversation_unknown_chat_raw');
if(message.fromId) {
var fromUser = appUsersManager.getUser(message.fromId);
notification.title = (fromUser.first_name || fromUser.last_name || _('conversation_unknown_user_raw')) +
notification.title = appPeersManager.getPeerTitle(peerId, true);
if(peerId < 0 && message.fromId !== message.peerId) {
notification.title = appPeersManager.getPeerTitle(message.fromId, true) +
' @ ' +
notification.title;
}
notificationPhoto = appChatsManager.getChatPhoto(-peerId);
peerString = appChatsManager.getChatString(-peerId);
}
notification.title = RichTextProcessor.wrapPlainText(notification.title);
notification.onclick = () => {
/* rootScope.broadcast('history_focus', {
peerString: peerString,
messageID: message.flags & 16 ? message.mid : 0
}); */
rootScope.broadcast('history_focus', {peerId, mid: message.mid});
};
notification.message = notificationMessage;
@ -4980,16 +4955,17 @@ export class AppMessagesManager { @@ -4980,16 +4955,17 @@ export class AppMessagesManager {
notification.tag = peerString;
notification.silent = true;//message.pFlags.silent || false;
/* if(notificationPhoto.location && !notificationPhoto.location.empty) {
apiManager.downloadSmallFile(notificationPhoto.location, notificationPhoto.size).then(function (blob) {
const peerPhoto = appPeersManager.getPeerPhoto(peerId);
if(peerPhoto) {
appProfileManager.loadAvatar(peerId, peerPhoto, 'photo_small').loadPromise.then(url => {
if(message.pFlags.unread) {
notification.image = blob
NotificationsManager.notify(notification)
notification.image = url;
appNotificationsManager.notify(notification);
}
})
} else { */
});
} else {
appNotificationsManager.notify(notification);
//}
}
}
public getScheduledMessagesStorage(peerId: number) {

10
src/lib/appManagers/appNotificationsManager.ts

@ -17,6 +17,7 @@ import { copy, deepEqual } from "../../helpers/object"; @@ -17,6 +17,7 @@ import { copy, deepEqual } from "../../helpers/object";
import { convertInputKeyToKey } from "../../helpers/string";
import { isMobile } from "../../helpers/userAgent";
import { InputNotifyPeer, InputPeerNotifySettings, NotifyPeer, PeerNotifySettings, Update } from "../../layer";
import I18n from "../langPack";
import apiManager from "../mtproto/mtprotoworker";
import rootScope from "../rootScope";
import sessionStorage from "../sessionStorage";
@ -55,10 +56,9 @@ export class AppNotificationsManager { @@ -55,10 +56,9 @@ export class AppNotificationsManager {
notifyChats: null as ImSadAboutIt,
notifyBroadcasts: null as ImSadAboutIt
};
private exceptions: {[peerId: string]: PeerNotifySettings} = {};
//private exceptions: {[peerId: string]: PeerNotifySettings} = {};
private notifyContactsSignUp: Promise<boolean>;
private faviconEl: HTMLLinkElement = document.head.querySelector('link[rel="icon"]');
private langNotificationsPluralize = 'notifications';//_.pluralize('page_title_pluralize_notifications');
private titleBackup = document.title;
private titleChanged = false;
@ -221,7 +221,7 @@ export class AppNotificationsManager { @@ -221,7 +221,7 @@ export class AppNotificationsManager {
resetTitle();
} else {
this.titleChanged = true;
document.title = this.notificationsCount + ' ' + this.langNotificationsPluralize;
document.title = I18n.format('Notifications.Count', true, [this.notificationsCount]);
//this.setFavicon('assets/img/favicon_unread.ico');
// fetch('assets/img/favicon.ico')
@ -499,8 +499,8 @@ export class AppNotificationsManager { @@ -499,8 +499,8 @@ export class AppNotificationsManager {
data.image = FileManager.getUrl(data.image, 'image/jpeg')
}
}
else if (!data.image) */ {
data.image = 'assets/img/logo.svg';
else */ if(!data.image) {
data.image = 'assets/img/logo_filled_rounded.png';
}
// console.log('notify image', data.image)

28
src/lib/appManagers/appProfileManager.ts

@ -37,7 +37,7 @@ export class AppProfileManager { @@ -37,7 +37,7 @@ export class AppProfileManager {
private savedAvatarURLs: {
[peerId: number]: {
[size in PeerPhotoSize]?: string | Promise<any>
[size in PeerPhotoSize]?: string | Promise<string>
}
} = {};
@ -455,11 +455,11 @@ export class AppProfileManager { @@ -455,11 +455,11 @@ export class AppProfileManager {
}
}
public putAvatar(div: HTMLElement, peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize, img = new Image()) {
public loadAvatar(peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize) {
const inputPeer = appPeersManager.getInputPeerById(peerId);
let needFadeIn = true;
let getAvatarPromise: Promise<any>;
let cached = false;
let getAvatarPromise: Promise<string>;
let saved = this.savedAvatarURLs[peerId];
if(!saved || !saved[size]) {
if(!saved) {
@ -489,7 +489,7 @@ export class AppProfileManager { @@ -489,7 +489,7 @@ export class AppProfileManager {
const promise = appDownloadManager.download(downloadOptions);
getAvatarPromise = saved[size] = promise.then(blob => {
saved[size] = URL.createObjectURL(blob);
return saved[size] = URL.createObjectURL(blob);
/* if(str) {
console.log(str, Date.now() / 1000, Date.now() - time);
@ -498,12 +498,18 @@ export class AppProfileManager { @@ -498,12 +498,18 @@ export class AppProfileManager {
} else if(typeof(saved[size]) !== 'string') {
getAvatarPromise = saved[size] as Promise<any>;
} else {
getAvatarPromise = Promise.resolve();
needFadeIn = false;
getAvatarPromise = Promise.resolve(saved[size]);
cached = true;
}
return {cached, loadPromise: getAvatarPromise};
}
public putAvatar(div: HTMLElement, peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize, img = new Image()) {
const {cached, loadPromise} = this.loadAvatar(peerId, photo, size);
let callback: () => void;
if(!needFadeIn) {
if(cached) {
// смотри в misc.ts: renderImageFromUrl
callback = () => {
replaceContent(div, img);
@ -532,16 +538,16 @@ export class AppProfileManager { @@ -532,16 +538,16 @@ export class AppProfileManager {
};
}
const loadPromise = getAvatarPromise.then(() => {
const renderPromise = loadPromise.then((url) => {
return new Promise<void>((resolve) => {
renderImageFromUrl(img, saved[size] as string, () => {
renderImageFromUrl(img, url, () => {
callback();
resolve();
}/* , false */);
});
});
return {cached: !needFadeIn, loadPromise};
return {cached, loadPromise: renderPromise};
}
// peerId === peerId || title

19
src/lib/mtproto/networker.ts

@ -74,6 +74,7 @@ export type MTMessage = InvokeApiOptions & MTMessageOptions & { @@ -74,6 +74,7 @@ export type MTMessage = InvokeApiOptions & MTMessageOptions & {
};
const CONNECTION_TIMEOUT = 5000;
let invokeAfterMsgConstructor: number;
export default class MTPNetworker {
private authKeyUint8: Uint8Array;
@ -320,15 +321,21 @@ export default class MTPNetworker { @@ -320,15 +321,21 @@ export default class MTPNetworker {
}
if(options.afterMessageId) {
const invokeAfterMsg = Schema.API.methods.find(m => m.method === 'invokeAfterMsg');
if(!invokeAfterMsg) throw new Error('no invokeAfterMsg!');
if(this.debug) {
this.log('Api call options.afterMessageId!');
if(invokeAfterMsgConstructor === undefined) {
const m = Schema.API.methods.find(m => m.method === 'invokeAfterMsg');
invokeAfterMsgConstructor = m ? +m.id >>> 0 : 0;
}
serializer.storeInt(+invokeAfterMsg.id >>> 0, 'invokeAfterMsg');
if(invokeAfterMsgConstructor) {
//if(this.debug) {
//this.log('Api call options.afterMessageId!');
//}
serializer.storeInt(invokeAfterMsgConstructor, 'invokeAfterMsg');
serializer.storeLong(options.afterMessageId, 'msg_id');
} else {
this.log.error('no invokeAfterMsg!');
}
}
options.resultType = serializer.storeMethod(method, params);

5
src/lib/mtproto/serverTimeManager.ts

@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
import { MOUNT_CLASS_TO } from '../../config/debug';
import { tsNow } from '../../helpers/date';
import sessionStorage from '../sessionStorage';
@ -37,4 +38,6 @@ export class ServerTimeManager { @@ -37,4 +38,6 @@ export class ServerTimeManager {
}
}
export default new ServerTimeManager();
const serverTimeManager = new ServerTimeManager();
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.serverTimeManager = serverTimeManager);
export default serverTimeManager;

2
src/lib/rootScope.ts

@ -51,7 +51,7 @@ export type BroadcastEvents = { @@ -51,7 +51,7 @@ export type BroadcastEvents = {
'history_delete': {peerId: number, msgs: {[mid: number]: true}},
'history_forbidden': number,
'history_reload': number,
'history_focus': number,
'history_focus': {peerId: number, mid?: number},
//'history_request': void,
'message_edit': {storage: MessagesStorage, peerId: number, mid: number},

3
src/pages/pageAuthCode.ts

@ -17,6 +17,7 @@ import TrackingMonkey from '../components/monkeys/tracking'; @@ -17,6 +17,7 @@ import TrackingMonkey from '../components/monkeys/tracking';
import CodeInputField from '../components/codeInputField';
import { replaceContent } from '../helpers/dom';
import { i18n, LangPackKey } from '../lib/langPack';
import { randomLong } from '../helpers/random';
let authCode: AuthSentCode.authSentCode = null;
@ -29,7 +30,7 @@ let onFirstMount = (): Promise<any> => { @@ -29,7 +30,7 @@ let onFirstMount = (): Promise<any> => {
const codeInputField = new CodeInputField({
label: 'Code',
name: 'code',
name: randomLong(),
length: CODELENGTH,
onFill: (code) => {
submitCode('' + code);

63
src/pages/pageSignIn.ts

@ -25,6 +25,8 @@ import { LangPackString } from "../layer"; @@ -25,6 +25,8 @@ import { LangPackString } from "../layer";
import lottieLoader from "../lib/lottieLoader";
import { ripple } from "../components/ripple";
import findUpTag from "../helpers/dom/findUpTag";
import findUpClassName from "../helpers/dom/findUpClassName";
import { randomLong } from "../helpers/random";
type Country = _Country & {
li?: HTMLLIElement[]
@ -56,14 +58,14 @@ let onFirstMount = () => { @@ -56,14 +58,14 @@ let onFirstMount = () => {
const countryInputField = new InputField({
label: 'Login.CountrySelectorLabel',
name: 'countryCode',
name: randomLong(),
plainText: true
});
countryInputField.container.classList.add('input-select');
const countryInput = countryInputField.input as HTMLInputElement;
countryInput.autocomplete = 'rrRandomRR';
countryInput.autocomplete = randomLong();
const selectWrapper = document.createElement('div');
selectWrapper.classList.add('select-wrapper', 'z-depth-3', 'hide');
@ -77,38 +79,25 @@ let onFirstMount = () => { @@ -77,38 +79,25 @@ let onFirstMount = () => {
const scroll = new Scrollable(selectWrapper);
let initedSelect = false;
let initSelect = () => {
initSelect = null;
countries.forEach((c) => {
initedSelect = true;
/* let unified = unifiedCountryCodeEmoji(c.code);
let emoji = unified.split('-').reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), ''); */
//let emoji = countryCodeEmoji(c.code);
let emoji = c.emoji;
const emoji = c.emoji;
let liArr: Array<HTMLLIElement> = [];
const liArr: Array<HTMLLIElement> = [];
c.phoneCode.split(' and ').forEach((phoneCode: string) => {
let li = document.createElement('li');
var spanEmoji = document.createElement('span');
/* spanEmoji.innerHTML = countryCodeEmoji(c.code); */
//spanEmoji.classList.add('emoji-outer', 'emoji-sizer');
//spanEmoji.innerHTML = `<span class="emoji-inner" style="background: url(${sheetUrl}${sheetNo}.png);background-position:${xPos}% ${yPos}%;background-size:${sizeX}% ${sizeY}%" data-codepoints="${unified}"></span>`;
const li = document.createElement('li');
const spanEmoji = document.createElement('span');
let kek = RichTextProcessor.wrapRichText(emoji);
//console.log(c.name, emoji, kek, spanEmoji.innerHTML);
const kek = RichTextProcessor.wrapRichText(emoji);
li.appendChild(spanEmoji);
spanEmoji.outerHTML = kek;
li.append(c.name);
var span = document.createElement('span');
const span = document.createElement('span');
span.classList.add('phone-code');
span.innerText = '+' + phoneCode;
li.appendChild(span);
@ -120,22 +109,31 @@ let onFirstMount = () => { @@ -120,22 +109,31 @@ let onFirstMount = () => {
c.li = liArr;
});
selectList.addEventListener('mousedown', function(e) {
selectList.addEventListener('mousedown', (e) => {
if(e.button !== 0) { // other buttons but left shall not pass
return;
}
let target = e.target as HTMLElement;
if(target.tagName !== 'LI') target = findUpTag(target, 'LI');
let countryName = target.childNodes[1].textContent;//target.innerText.split('\n').shift();
let phoneCode = target.querySelector<HTMLElement>('.phone-code').innerText;
selectCountryByTarget(target);
//console.log('clicked', e, countryName, phoneCode);
});
countryInputField.container.appendChild(selectWrapper);
};
const selectCountryByTarget = (target: HTMLElement) => {
const countryName = target.childNodes[1].textContent;//target.innerText.split('\n').shift();
const phoneCode = target.querySelector<HTMLElement>('.phone-code').innerText;
countryInput.value = countryName;
lastCountrySelected = countries.find(c => c.name === countryName);
telEl.value = lastValue = phoneCode;
hidePicker();
setTimeout(() => telEl.focus(), 0);
//console.log('clicked', e, countryName, phoneCode);
});
countryInputField.container.appendChild(selectWrapper);
};
initSelect();
@ -152,6 +150,7 @@ let onFirstMount = () => { @@ -152,6 +150,7 @@ let onFirstMount = () => {
}
clearTimeout(hideTimeout);
hideTimeout = undefined;
selectWrapper.classList.remove('hide');
void selectWrapper.offsetWidth; // reflow
@ -171,9 +170,9 @@ let onFirstMount = () => { @@ -171,9 +170,9 @@ let onFirstMount = () => {
let mouseDownHandlerAttached = false;
const onMouseDown = (e: MouseEvent) => {
/* if(findUpClassName(e.target, 'input-select')) {
if(findUpClassName(e.target, 'input-select')) {
return;
} */
}
if(e.target === countryInput) {
return;
}
@ -184,9 +183,11 @@ let onFirstMount = () => { @@ -184,9 +183,11 @@ let onFirstMount = () => {
};
const hidePicker = () => {
if(hideTimeout !== undefined) return;
selectWrapper.classList.remove('active');
hideTimeout = window.setTimeout(() => {
selectWrapper.classList.add('hide');
hideTimeout = undefined;
}, 200);
};
/* false && countryInput.addEventListener('blur', function(this: typeof countryInput, e) {
@ -221,6 +222,8 @@ let onFirstMount = () => { @@ -221,6 +222,8 @@ let onFirstMount = () => {
countries.forEach((c) => {
c.li.forEach(li => li.style.display = '');
});
} else if(matches.length === 1 && e.key === 'Enter') {
selectCountryByTarget(matches[0].li[0]);
}
});

4
src/scss/partials/_audio.scss

@ -359,6 +359,10 @@ @@ -359,6 +359,10 @@
//overflow: visible!important;
opacity: .3;
@include hover() {
opacity: 1;
}
&.active, .audio.is-unread:not(.is-out) & {
opacity: 1;
}

4
src/scss/partials/_button.scss

@ -112,7 +112,7 @@ @@ -112,7 +112,7 @@
&.active {
visibility: visible;
opacity: 1;
transform: scale(1);
transform: scale3d(1, 1, 1); // * scale3d (NOT scale) will fix jumping text
}
&:not(.active) {
@ -178,7 +178,7 @@ @@ -178,7 +178,7 @@
@include hover-background-effect();
&.danger {
@include hover-background-effect(red);
@include hover-background-effect(danger);
}
&:before {

12
src/scss/partials/_chat.scss

@ -1042,12 +1042,6 @@ $chat-helper-size: 39px; @@ -1042,12 +1042,6 @@ $chat-helper-size: 39px;
//}
}
.preloader-container {
.preloader-circular {
background-color: rgba(0, 0, 0, .3);
}
}
.search-group.search-group-messages {
padding: 0.25rem 0 .5rem;
}
@ -1139,7 +1133,7 @@ $chat-helper-size: 39px; @@ -1139,7 +1133,7 @@ $chat-helper-size: 39px;
position: absolute;
background-color: var(--surface-color);
border-radius: 50%;
color: $placeholder-color;
color: var(--secondary-text-color);
font-size: 1.5rem;
display: flex;
align-items: center;
@ -1172,6 +1166,10 @@ $chat-helper-size: 39px; @@ -1172,6 +1166,10 @@ $chat-helper-size: 39px;
top: -.25rem;
right: -.25rem;
&.badge-primary {
background-color: var(--chatlist-status-color);
}
@include respond-to(handhelds) {
top: -.75rem;
right: .1875rem;

4
src/scss/partials/_chatBubble.scss

@ -1279,8 +1279,8 @@ $bubble-margin: .25rem; @@ -1279,8 +1279,8 @@ $bubble-margin: .25rem;
visibility: visible;
}
.tgico-pinnedchat:before {
font-weight: bold;
.tgico-pinnedchat {
margin-right: .125rem;
}
}

50
src/scss/partials/_input.scss

@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
.input-field {
--height: 54px;
--border-radius: #{$border-radius-medium};
position: relative;
@include respond-to(handhelds) {
@ -63,26 +64,38 @@ @@ -63,26 +64,38 @@
}
}
&-border {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border: 2px solid var(--primary-color);
opacity: 0;
border-radius: var(--border-radius);
pointer-events: none;
@include animation-level(2) {
transition: opacity .2s;
}
}
&-input {
--padding: 1.0625rem;
--padding: 1rem;
--padding-horizontal: 1rem;
--border-width: 1px;
--border-width-top: 2px;
border: var(--border-width) solid var(--input-search-border-color);
border-radius: $border-radius-medium;
border-radius: var(--border-radius);
background-color: transparent;
//padding: 0 1rem;
padding: calc(var(--padding) - var(--border-width-top)) calc(var(--padding-horizontal) - var(--border-width));
padding: calc(var(--padding) - var(--border-width));
box-sizing: border-box;
width: 100%;
min-height: var(--height);
transition: 0s border-color;
position: relative;
z-index: 1;
line-height: 1.3125;
//line-height: calc(54px - var(--border-width));
/* overflow: hidden;
white-space: nowrap; */
line-height: var(--line-height);
@include respond-to(handhelds) {
--padding: .9375rem;
@ -116,8 +129,6 @@ @@ -116,8 +129,6 @@
} */
&:focus {
--border-width: 2px;
--border-width-top: 3px;
border-color: var(--primary-color);
//padding: 0 calc(1rem - 1px);
}
@ -133,6 +144,10 @@ @@ -133,6 +144,10 @@
& ~ label {
color: var(--danger-color) !important;
}
& ~ .input-field-border {
border-color: var(--danger-color) !important;
}
}
&.valid {
@ -141,6 +156,10 @@ @@ -141,6 +156,10 @@
& ~ label {
color: #26962F !important;
}
& ~ .input-field-border {
border-color: #26962F !important;
}
}
/* &.error, &.valid {
@ -158,11 +177,15 @@ @@ -158,11 +177,15 @@
font-weight: 500;
}
// * valid for plain text, empty for contenteditable
&:focus ~ .input-field-border {
opacity: 1;
}
/* // * valid for plain text, empty for contenteditable
&:valid ~ label,
&:not(:empty):focus ~ label {
transition-delay: 0s, 0s, 0s, 0s;
}
} */
&:focus ~ label,
&:valid ~ label,
@ -252,6 +275,7 @@ input:focus, button:focus { @@ -252,6 +275,7 @@ input:focus, button:focus {
margin-left: .4375rem;
margin-right: .4375rem;
overflow: hidden;
--border-radius: 22px;
@include respond-to(handhelds) {
margin-left: 1rem;
@ -265,7 +289,6 @@ input:focus, button:focus { @@ -265,7 +289,6 @@ input:focus, button:focus {
min-height: var(--height) !important;
max-height: var(--height) !important;
//line-height: calc(var(--height) + 2px - var(--border-width) * 2);
border-radius: 22px;
border-color: var(--input-search-border-color);
line-height: var(--height);
@ -281,7 +304,6 @@ input:focus, button:focus { @@ -281,7 +304,6 @@ input:focus, button:focus {
}
&:focus {
--border-width: 2px;
background-color: transparent;
border-color: var(--primary-color);

2
src/scss/partials/_preloader.scss

@ -70,7 +70,7 @@ $transition: .2s ease-in-out; @@ -70,7 +70,7 @@ $transition: .2s ease-in-out;
.preloader-circular {
animation: none;
background-color: rgba(0, 0, 0, .7);
background-color: rgba(0, 0, 0, .3);
border-radius: 50%;
width: 100%;
height: 100%;

5
src/scss/partials/_rightSidebar.scss

@ -173,6 +173,7 @@ @@ -173,6 +173,7 @@
justify-content: center;
font-size: 1rem;
line-height: var(--line-height);
border-radius: 0;
}
.menu-horizontal-div i {
@ -287,6 +288,10 @@ @@ -287,6 +288,10 @@
> div:first-child {
transform: translateY(0);
// * fix saving scroll on tab switching, when FROM tab has height < 100vh, and another is scrolled less than the FROM tab's height
// * adding 1 extra pixel for scroll
min-height: calc(100vh - 111px);
}
}

11
src/scss/partials/popups/_createPoll.scss

@ -45,11 +45,16 @@ @@ -45,11 +45,16 @@
.btn-icon {
position: absolute;
right: .5rem;
top: .5rem;
right: .4375rem;
top: .4375rem;
z-index: 1;
opacity: 1;
transition: opacity .2s ease;
@include respond-to(handhelds) {
right: .3125rem;
top: .3125rem;
}
}
/* &:last-child:not(:nth-child(10)) {
.btn-icon {
@ -65,7 +70,7 @@ @@ -65,7 +70,7 @@
}
.poll-create-questions {
padding: 0px 1.25rem 2.03125rem;
padding: 0px 1.25rem 1.5rem;
.input-field-input {
padding-right: 3.25rem;

Loading…
Cancel
Save