Browse Source

Font changed to 16px; sign in page fixes; prepare auth animation

master
morethanwords 4 years ago
parent
commit
4c500ada12
  1. 6
      package-lock.json
  2. 3
      package.json
  3. 50
      src/components/emoticonsDropdown.ts
  4. 6
      src/components/pageSignIn.ts
  5. 6
      src/components/scrollable.ts
  6. 2
      src/components/wrappers.ts
  7. 2
      src/lib/appManagers/appImManager.ts
  8. 6
      src/lib/appManagers/appSidebarLeft.ts
  9. 18
      src/lib/appManagers/appStickersManager.ts
  10. 26
      src/lib/bin_utils.ts
  11. 2184
      src/lib/crypto/crypto.js
  12. 78
      src/lib/crypto/crypto_utils.ts
  13. 38
      src/lib/crypto/cryptoworker.ts
  14. 18
      src/lib/mtproto/apiManager.ts
  15. 13
      src/lib/mtproto/transports/abridged.ts
  16. 4
      src/lib/mtproto/transports/codec.ts
  17. 49
      src/lib/mtproto/transports/intermediate.ts
  18. 79
      src/lib/mtproto/transports/websocket.ts
  19. 7
      src/lib/polyfill.ts
  20. 35
      src/scss/partials/_chat.scss
  21. 28
      src/scss/partials/_chatlist.scss
  22. 48
      src/scss/partials/_emojiDropdown.scss
  23. 2
      src/scss/partials/_sidebar.scss
  24. 72
      src/scss/style.scss

6
package-lock.json generated

@ -1312,6 +1312,12 @@
"minimist": "^1.2.0" "minimist": "^1.2.0"
} }
}, },
"@cryptography/aes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@cryptography/aes/-/aes-0.1.1.tgz",
"integrity": "sha512-PcYz4FDGblO6tM2kSC+VzhhK62vml6k6/YAkiWtyPvrgJVfnDRoHGDtKn5UiaRRUrvUTTocBpvc2rRgTCqxjsg==",
"dev": true
},
"@cryptography/sha1": { "@cryptography/sha1": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/@cryptography/sha1/-/sha1-0.1.0.tgz", "resolved": "https://registry.npmjs.org/@cryptography/sha1/-/sha1-0.1.0.tgz",

3
package.json

@ -6,7 +6,7 @@
"scripts": { "scripts": {
"start": "webpack-dev-server --open --config webpack.dev.js", "start": "webpack-dev-server --open --config webpack.dev.js",
"start-production": "webpack-dev-server --open --config webpack.prod.js", "start-production": "webpack-dev-server --open --config webpack.prod.js",
"serve-serve": "webpack-serve --compress --port 9001 --host localhost --no-watch --static ./public --config webpack.prod.js", "serve": "node server.js",
"build": "webpack --config webpack.prod.js", "build": "webpack --config webpack.prod.js",
"test": "jest --config=jest.config.js", "test": "jest --config=jest.config.js",
"profile": "webpack --profile --json > stats.json --config webpack.prod.js" "profile": "webpack --profile --json > stats.json --config webpack.prod.js"
@ -21,6 +21,7 @@
"@babel/core": "^7.9.0", "@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5", "@babel/preset-env": "^7.9.5",
"@babel/preset-typescript": "^7.9.0", "@babel/preset-typescript": "^7.9.0",
"@cryptography/aes": "^0.1.1",
"@cryptography/sha1": "^0.1.0", "@cryptography/sha1": "^0.1.0",
"@cryptography/sha256": "^0.2.0", "@cryptography/sha256": "^0.2.0",
"@types/aes-js": "^3.1.1", "@types/aes-js": "^3.1.1",

50
src/components/emoticonsDropdown.ts

@ -71,9 +71,9 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
if(menuScroll) { if(menuScroll) {
if(i < heights.length - 4) { if(i < heights.length - 4) {
menuScroll.container.scrollLeft = (i - 3) * 50; menuScroll.container.scrollLeft = (i - 3) * 47;
} else { } else {
menuScroll.container.scrollLeft = i * 50; menuScroll.container.scrollLeft = i * 47;
} }
} }
@ -85,7 +85,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
}; };
{ {
let categories = ["Smileys & Emotion", "Animals & Nature", "Food & Drink", "Travel & Places", "Activities", "Objects", "Symbols", "Flags", "Skin Tones"]; let categories = ["Smileys & Emotion", "Animals & Nature", "Food & Drink", "Travel & Places", "Activities", "Objects", /* "Symbols", */"Flags", "Skin Tones"];
let divs: { let divs: {
[category: string]: HTMLDivElement [category: string]: HTMLDivElement
} = {}; } = {};
@ -100,12 +100,16 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
let details = Config.Emoji.emoji[unified]; let details = Config.Emoji.emoji[unified];
let category = details[keyCategory]; let category = details[keyCategory];
if(category == 'Symbols') category = 'Objects';
details.unified = unified; details.unified = unified;
if(!sorted[category]) sorted[category] = []; if(!sorted[category]) sorted[category] = [];
sorted[category][details.sort_order] = details; sorted[category][details.sort_order] = details;
} }
//console.log('emoticons sorted:', sorted);
Object.keys(sorted).forEach(c => sorted[c].sort()); Object.keys(sorted).forEach(c => sorted[c].sort());
categories.pop(); categories.pop();
@ -143,20 +147,29 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
} }
//console.timeEnd('emojiParse'); //console.timeEnd('emojiParse');
let contentEmojiDiv = document.getElementById('content-emoji') as HTMLDivElement;
let heights: number[] = [0]; let heights: number[] = [0];
let contentEmojiDiv = document.getElementById('content-emoji') as HTMLDivElement; let prevCategoryIndex = 1;
categories.forEach(category => { let menu = contentEmojiDiv.nextElementSibling as HTMLUListElement;
let emojiScroll = new Scrollable(contentEmojiDiv, 'y', 500, 'EMOJI', null);
emojiScroll.container.addEventListener('scroll', (e) => {
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, emojiScroll.container);
});
//emojiScroll.setVirtualContainer(emojiScroll.container);
categories.map(category => {
let div = divs[category]; let div = divs[category];
if(!div) { if(!div) {
console.error('no div by category:', category); console.error('no div by category:', category);
} }
contentEmojiDiv.append(div); emojiScroll.append(div);
return div;
}).forEach(div => {
//console.log('emoji heights push: ', (heights[heights.length - 1] || 0) + div.scrollHeight, div, div.scrollHeight);
heights.push((heights[heights.length - 1] || 0) + div.scrollHeight); heights.push((heights[heights.length - 1] || 0) + div.scrollHeight);
//console.log(div, div.scrollHeight);
}); });
contentEmojiDiv.addEventListener('click', function(e) { contentEmojiDiv.addEventListener('click', function(e) {
@ -184,14 +197,6 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
btnSend.classList.remove('tgico-microphone2'); btnSend.classList.remove('tgico-microphone2');
}); });
let prevCategoryIndex = 1;
let menu = contentEmojiDiv.nextElementSibling as HTMLUListElement;
let emojiScroll = new Scrollable(contentEmojiDiv, 'y', 500, 'EMOJI', contentEmojiDiv);
emojiScroll.container.addEventListener('scroll', (e) => {
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, emojiScroll.container);
});
//emojiScroll.setVirtualContainer(emojiScroll.container);
emoticonsMenuOnClick(menu, heights, emojiScroll); emoticonsMenuOnClick(menu, heights, emojiScroll);
} }
@ -273,13 +278,15 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
heightRAF = window.requestAnimationFrame(() => { heightRAF = window.requestAnimationFrame(() => {
heightRAF = 0; heightRAF = 0;
let paddingTop = parseInt(window.getComputedStyle(stickersScroll.container).getPropertyValue('padding-top')) || 0;
heights.length = 0; heights.length = 0;
let concated = stickersScroll.hiddenElements.up.concat(stickersScroll.visibleElements, stickersScroll.hiddenElements.down); let concated = stickersScroll.hiddenElements.up.concat(stickersScroll.visibleElements, stickersScroll.hiddenElements.down);
concated.forEach((el, i) => { concated.forEach((el, i) => {
heights[i] = (heights[i - 1] || 0) + el.height; heights[i] = (heights[i - 1] || 0) + el.height + (i == 0 ? paddingTop : 0);
}); });
console.log('stickers concated', concated, heights); //console.log('stickers concated', concated, heights);
}); });
/* Array.from(stickersDiv.children).forEach((div, i) => { /* Array.from(stickersDiv.children).forEach((div, i) => {
@ -343,12 +350,13 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
//stickersScroll.append(categoryDiv); //stickersScroll.append(categoryDiv);
let stickerSet = await appStickersManager.getStickerSet(set); let stickerSet = await appStickersManager.getStickerSet(set);
//console.log('got stickerSet', stickerSet, li);
if(stickerSet.set.thumb) { if(stickerSet.set.thumb) {
let thumb = stickerSet.set.thumb;
appStickersManager.getStickerSetThumb(stickerSet.set).then((blob) => { appStickersManager.getStickerSetThumb(stickerSet.set).then((blob) => {
if(thumb.w == 1 && thumb.h == 1) { // means animated //console.log('setting thumb', stickerSet, blob);
if(stickerSet.set.pFlags.animated) { // means animated
const reader = new FileReader(); const reader = new FileReader();
reader.addEventListener('loadend', async(e) => { reader.addEventListener('loadend', async(e) => {

6
src/components/pageSignIn.ts

@ -38,7 +38,7 @@ export default () => {
installed = true; installed = true;
//const countries: Country[] = _countries.default.filter(c => c.emoji); //const countries: Country[] = _countries.default.filter(c => c.emoji);
const countries: Country[] = Config.Countries.filter(c => c.emoji); const countries: Country[] = Config.Countries.filter(c => c.emoji).sort((a, b) => a.name.localeCompare(b.name));
let lastCountrySelected = ''; let lastCountrySelected = '';
@ -174,9 +174,9 @@ export default () => {
} }
if(country && (this.value.length - 1) >= (country.pattern ? country.pattern.length : 9)) { if(country && (this.value.length - 1) >= (country.pattern ? country.pattern.length : 9)) {
btnNext.style.display = ''; btnNext.style.visibility = '';
} else { } else {
btnNext.style.display = 'none'; btnNext.style.visibility = 'hidden';
} }
}); });

6
src/components/scrollable.ts

@ -104,6 +104,10 @@ export default class Scrollable {
constructor(public el: HTMLElement, axis: 'y' | 'x' = 'y', public splitOffset = 300, logPrefix = '', public appendTo = el, public onScrollOffset = splitOffset) { constructor(public el: HTMLElement, axis: 'y' | 'x' = 'y', public splitOffset = 300, logPrefix = '', public appendTo = el, public onScrollOffset = splitOffset) {
this.container = document.createElement('div'); this.container = document.createElement('div');
this.container.classList.add('scrollable'); this.container.classList.add('scrollable');
if(!appendTo) {
this.appendTo = this.container;
}
this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : '')); this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''));
@ -413,7 +417,7 @@ export default class Scrollable {
let appendTo = this.splitUp || this.appendTo; let appendTo = this.splitUp || this.appendTo;
clearTimeout(this.disableHoverTimeout); clearTimeout(this.disableHoverTimeout);
if(this.el != this.appendTo) { if(this.el != this.appendTo && this.appendTo != this.container) {
if(!appendTo.classList.contains('disable-hover')) { if(!appendTo.classList.contains('disable-hover')) {
appendTo.classList.add('disable-hover'); appendTo.classList.add('disable-hover');
} }

2
src/components/wrappers.ts

@ -543,7 +543,7 @@ export function wrapSticker(doc: MTDocument, div: HTMLDivElement, middleware?: (
if(doc.thumbs && !div.firstElementChild && (!doc.downloaded || stickerType == 2)) { if(doc.thumbs && !div.firstElementChild && (!doc.downloaded || stickerType == 2)) {
let thumb = doc.thumbs[0]; let thumb = doc.thumbs[0];
console.log('wrap sticker', thumb, div); //console.log('wrap sticker', thumb, div);
if(thumb.bytes) { if(thumb.bytes) {
apiFileManager.saveSmallFile(thumb.location, thumb.bytes); apiFileManager.saveSmallFile(thumb.location, thumb.bytes);

2
src/lib/appManagers/appImManager.ts

@ -1336,7 +1336,7 @@ export class AppImManager {
this.log('messageMediaDocument', doc, bubble); this.log('messageMediaDocument', doc, bubble);
if(doc.sticker && doc.size <= 1e6) { if(doc.sticker/* && doc.size <= 1e6 */) {
bubble.classList.add('sticker'); bubble.classList.add('sticker');
if(doc.animated) { if(doc.animated) {

6
src/lib/appManagers/appSidebarLeft.ts

@ -8,6 +8,7 @@ import appMessagesIDsManager from "./appMessagesIDsManager";
import appImManager from "./appImManager"; import appImManager from "./appImManager";
import appUsersManager from "./appUsersManager"; import appUsersManager from "./appUsersManager";
import { appPeersManager } from "../services"; import { appPeersManager } from "../services";
import apiManager from "../mtproto/apiManager";
let testScroll = false; let testScroll = false;
@ -50,6 +51,7 @@ class AppSidebarLeft {
private menuEl = this.toolsBtn.querySelector('.btn-menu'); private menuEl = this.toolsBtn.querySelector('.btn-menu');
private savedBtn = this.menuEl.querySelector('.menu-saved'); private savedBtn = this.menuEl.querySelector('.menu-saved');
private archivedBtn = this.menuEl.querySelector('.menu-archive'); private archivedBtn = this.menuEl.querySelector('.menu-archive');
private logOutBtn = this.menuEl.querySelector('.menu-logout');
public archivedCount = this.archivedBtn.querySelector('.archived-count') as HTMLSpanElement; public archivedCount = this.archivedBtn.querySelector('.archived-count') as HTMLSpanElement;
private listsContainer: HTMLDivElement = null; private listsContainer: HTMLDivElement = null;
@ -130,6 +132,10 @@ class AppSidebarLeft {
//this.toolsBtn.classList.remove('tgico-menu', 'btn-menu-toggle'); //this.toolsBtn.classList.remove('tgico-menu', 'btn-menu-toggle');
//this.toolsBtn.classList.add('tgico-back'); //this.toolsBtn.classList.add('tgico-back');
}); });
this.logOutBtn.addEventListener('click', (e) => {
apiManager.logOut();
});
if(testScroll) { if(testScroll) {
for(let i = 0; i < 1000; ++i) { for(let i = 0; i < 1000; ++i) {

18
src/lib/appManagers/appStickersManager.ts

@ -2,6 +2,7 @@ import AppStorage from '../storage';
import { MTDocument } from '../../components/wrappers'; import { MTDocument } from '../../components/wrappers';
import apiManager from '../mtproto/apiManager'; import apiManager from '../mtproto/apiManager';
import apiFileManager from '../mtproto/apiFileManager'; import apiFileManager from '../mtproto/apiFileManager';
import appDocsManager from './appDocsManager';
export type MTStickerSet = { export type MTStickerSet = {
_: 'stickerSet', _: 'stickerSet',
@ -27,6 +28,9 @@ export type MTStickerSet = {
h: number, h: number,
size: number size: number
}, },
pFlags: {
animated?: boolean
}
thumb_dc_id?: number, thumb_dc_id?: number,
count: number, count: number,
hash: number hash: number
@ -52,12 +56,23 @@ class appStickersManager {
[stickerSetID: string]: MTStickerSetFull [stickerSetID: string]: MTStickerSetFull
}>('stickerSets').then((sets) => { }>('stickerSets').then((sets) => {
if(sets) { if(sets) {
for(let id in sets) {
let set = sets[id];
set.documents.forEach(doc => {
delete doc.downloaded;
delete doc.url;
this.saveSticker(doc);
});
}
this.stickerSets = sets; this.stickerSets = sets;
} }
}); });
} }
public saveSticker(doc: MTDocument) { public saveSticker(doc: MTDocument) {
if(this.documents[doc.id]) return this.documents[doc.id];
/* Object.keys(doc).forEach(key => { /* Object.keys(doc).forEach(key => {
if(doc[key] instanceof Uint8Array) { if(doc[key] instanceof Uint8Array) {
doc[key] = Array.from(doc[key]); doc[key] = Array.from(doc[key]);
@ -66,7 +81,10 @@ class appStickersManager {
doc.file_reference = Array.from(doc.file_reference); doc.file_reference = Array.from(doc.file_reference);
appDocsManager.saveDoc(doc);
this.documents[doc.id] = doc; this.documents[doc.id] = doc;
return doc;
} }
public getSticker(fileID: string) { public getSticker(fileID: string) {

26
src/lib/bin_utils.ts

@ -282,7 +282,7 @@ export function longToInts(sLong: string) {
return [divRem[0].intValue(), divRem[1].intValue()]; return [divRem[0].intValue(), divRem[1].intValue()];
} }
export function bytesFromWords(wordArray: any) { export function bytesFromWords(wordArray: {words: number[] | Uint8Array | Uint32Array, sigBytes: number}) {
var words = wordArray.words; var words = wordArray.words;
var sigBytes = wordArray.sigBytes; var sigBytes = wordArray.sigBytes;
var bytes = []; var bytes = [];
@ -294,6 +294,30 @@ export function bytesFromWords(wordArray: any) {
return bytes; return bytes;
} }
export function bytesFromWordss(input: Uint32Array) {
var o = [];
for(var i = 0; i < input.length * 4; i++) {
o.push((input[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
}
return o;
}
export function bytesToWordss(input: ArrayBuffer | Uint8Array) {
let bytes: Uint8Array;
if(input instanceof ArrayBuffer) bytes = new Uint8Array(input);
else bytes = input;
var len = bytes.length;
var words: number[] = [];
var i;
for(i = 0; i < len; i++) {
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
}
return new Uint32Array(words);
}
export function longToBytes(sLong: string) { export function longToBytes(sLong: string) {
return bytesFromWords({words: longToInts(sLong), sigBytes: 8}).reverse(); return bytesFromWords({words: longToInts(sLong), sigBytes: 8}).reverse();
} }

2184
src/lib/crypto/crypto.js

File diff suppressed because it is too large Load Diff

78
src/lib/crypto/crypto_utils.ts

@ -1,5 +1,6 @@
import sha1 from '@cryptography/sha1'; import sha1 from '@cryptography/sha1';
import sha256 from '@cryptography/sha256'; import sha256 from '@cryptography/sha256';
import {IGE} from '@cryptography/aes';
import {str2bigInt, bpe, equalsInt, greater, import {str2bigInt, bpe, equalsInt, greater,
copy_, eGCD_, add_, rightShift_, sub_, copyInt_, isZero, copy_, eGCD_, add_, rightShift_, sub_, copyInt_, isZero,
@ -9,43 +10,13 @@ import {str2bigInt, bpe, equalsInt, greater,
// @ts-ignore // @ts-ignore
import {BigInteger} from 'jsbn'; import {BigInteger} from 'jsbn';
import CryptoJS from './crypto.js'; import { addPadding, bytesToHex, bytesFromHex, nextRandomInt, bytesFromBigInt, dT, bytesFromWords, bytesToWordss, bytesFromWordss } from '../bin_utils';
import { addPadding, bytesToHex, bytesFromHex, nextRandomInt, bytesFromBigInt, dT, bytesFromWords } from '../bin_utils';
export function bytesFromLeemonBigInt(bigInt: BigInteger) { export function bytesFromLeemonBigInt(bigInt: BigInteger) {
var str = bigInt2str(bigInt, 16); var str = bigInt2str(bigInt, 16);
return bytesFromHex(str); return bytesFromHex(str);
} }
export function bytesToWordss(input: ArrayBuffer | Uint8Array) {
let bytes: Uint8Array;
if(input instanceof ArrayBuffer) bytes = new Uint8Array(input);
else bytes = input;
var len = bytes.length;
var words: number[] = [];
var i;
for(i = 0; i < len; i++) {
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
}
return new Uint32Array(words);
}
export function bytesToWords(bytes: any) {
if(bytes instanceof ArrayBuffer) {
bytes = new Uint8Array(bytes);
}
var len = bytes.length;
var words: any = [];
var i;
for(i = 0; i < len; i++) {
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
}
return new CryptoJS.lib.WordArray.init(words, len);
}
export function sha1HashSync(bytes: number[] | ArrayBuffer | Uint8Array) { export function sha1HashSync(bytes: number[] | ArrayBuffer | Uint8Array) {
//console.trace(dT(), 'SHA-1 hash start', bytes); //console.trace(dT(), 'SHA-1 hash start', bytes);
@ -68,54 +39,35 @@ export function sha256HashSync(bytes: Uint8Array | ArrayBuffer) {
let words = bytesToWordss(bytes); let words = bytesToWordss(bytes);
let hash = sha256(words); let hash = sha256(words);
// bytesFromWords below
var o = [];
for(var i = 0; i < hash.length * 4; i++) {
o.push((hash[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
}
//console.log(dT(), 'SHA-256 hash finish'); //console.log(dT(), 'SHA-256 hash finish');
return o; return bytesFromWordss(hash);
} }
export function aesEncryptSync(bytes: any, keyBytes: any, ivBytes: any) { export function aesEncryptSync(bytes: ArrayBuffer, keyBytes: ArrayBuffer, ivBytes: ArrayBuffer) {
// console.log(dT(), 'AES encrypt start', len/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/) //console.log(dT(), 'AES encrypt start', bytes, keyBytes, ivBytes);
// console.log('aes before padding bytes:', bytesToHex(bytes)); // console.log('aes before padding bytes:', bytesToHex(bytes));
bytes = addPadding(bytes); bytes = addPadding(bytes);
// console.log('aes after padding bytes:', bytesToHex(bytes)); // console.log('aes after padding bytes:', bytesToHex(bytes));
let mode = CryptoJS.mode.IGE; const cipher = new IGE(bytesToWordss(keyBytes), bytesToWordss(ivBytes));
const encryptedBytes = cipher.encrypt(bytesToWordss(bytes));
//console.log(dT(), 'AES encrypt finish');
let encryptedWords = CryptoJS.AES.encrypt(bytesToWords(bytes), bytesToWords(keyBytes), { return bytesFromWordss(encryptedBytes);
iv: bytesToWords(ivBytes),
padding: CryptoJS.pad.NoPadding,
mode//: CryptoJS.mode.IGE
}).ciphertext;
let encryptedBytes = bytesFromWords(encryptedWords);
// console.log(dT(), 'AES encrypt finish')
return encryptedBytes;
} }
export function aesDecryptSync(encryptedBytes: any, keyBytes: any, ivBytes: any) { export function aesDecryptSync(bytes: Uint8Array, keyBytes: Uint8Array, ivBytes: Uint8Array) {
//console.log(dT(), 'AES decrypt start', bytes, keyBytes, ivBytes);
let mode = CryptoJS.mode.IGE; const cipher = new IGE(bytesToWordss(keyBytes), bytesToWordss(ivBytes));
// console.log(dT(), 'AES decrypt start', encryptedBytes.length) const decryptedBytes = cipher.decrypt(bytesToWordss(bytes));
var decryptedWords = CryptoJS.AES.decrypt({ciphertext: bytesToWords(encryptedBytes)}, bytesToWords(keyBytes), {
iv: bytesToWords(ivBytes),
padding: CryptoJS.pad.NoPadding,
mode//: CryptoJS.mode.IGE
});
var bytes = bytesFromWords(decryptedWords); //console.log(dT(), 'AES decrypt finish');
// console.log(dT(), 'AES decrypt finish')
return bytes; return bytesFromWordss(decryptedBytes);
} }
export function rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes: any): number[] { export function rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes: any): number[] {
console.log(dT(), 'RSA encrypt start', publicKey, bytes); console.log(dT(), 'RSA encrypt start', publicKey, bytes);

38
src/lib/crypto/cryptoworker.ts

@ -88,36 +88,26 @@ class CryptoWorker {
} }
public sha1Hash(bytes: number[] | ArrayBuffer | Uint8Array): Promise<Uint8Array> { public sha1Hash(bytes: number[] | ArrayBuffer | Uint8Array): Promise<Uint8Array> {
//if(this.webWorker) { return this.performTaskWorker<Uint8Array>('sha1-hash', bytes);
return this.performTaskWorker<Uint8Array>('sha1-hash', bytes);
//}
} }
public sha256Hash(bytes: any) { public sha256Hash(bytes: any) {
//if(this.webWorker) { return this.performTaskWorker<number[]>('sha256-hash', bytes);
return this.performTaskWorker<number[]>('sha256-hash', bytes);
//}
} }
public pbkdf2(buffer: Uint8Array, salt: Uint8Array, iterations: number) { public pbkdf2(buffer: Uint8Array, salt: Uint8Array, iterations: number) {
//if(this.webWorker) { return this.performTaskWorker<ArrayBuffer>('pbkdf2', buffer, salt, iterations);
return this.performTaskWorker<ArrayBuffer>('pbkdf2', buffer, salt, iterations);
//}
} }
public aesEncrypt(bytes: any, keyBytes: any, ivBytes: any) { public aesEncrypt(bytes: any, keyBytes: any, ivBytes: any) {
//if(this.webWorker) { return this.performTaskWorker<ArrayBuffer>('aes-encrypt', convertToArrayBuffer(bytes),
return this.performTaskWorker<ArrayBuffer>('aes-encrypt', convertToArrayBuffer(bytes), convertToArrayBuffer(keyBytes), convertToArrayBuffer(ivBytes));
convertToArrayBuffer(keyBytes), convertToArrayBuffer(ivBytes));
//}
} }
public aesDecrypt(encryptedBytes: any, keyBytes: any, ivBytes: any): Promise<ArrayBuffer> { public aesDecrypt(encryptedBytes: any, keyBytes: any, ivBytes: any): Promise<ArrayBuffer> {
//if(this.webWorker) { return this.performTaskWorker<ArrayBuffer>('aes-decrypt',
return this.performTaskWorker<ArrayBuffer>('aes-decrypt', encryptedBytes, keyBytes, ivBytes)
encryptedBytes, keyBytes, ivBytes) .then(bytes => convertToArrayBuffer(bytes));
.then(bytes => convertToArrayBuffer(bytes));
//}
} }
public rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes: any): Promise<number[]> { public rsaEncrypt(publicKey: {modulus: string, exponent: string}, bytes: any): Promise<number[]> {
@ -127,21 +117,15 @@ class CryptoWorker {
public factorize(bytes: any) { public factorize(bytes: any) {
bytes = convertToByteArray(bytes); bytes = convertToByteArray(bytes);
//if(this.webWorker) { return this.performTaskWorker<[number[], number[], number]>('factorize', bytes);
return this.performTaskWorker<[number[], number[], number]>('factorize', bytes);
//}
} }
public modPow(x: any, y: any, m: any) { public modPow(x: any, y: any, m: any) {
//if(this.webWorker) { return this.performTaskWorker<number[]>('mod-pow', x, y, m);
return this.performTaskWorker<number[]>('mod-pow', x, y, m);
//}
} }
public gzipUncompress<T>(bytes: ArrayBuffer, toString?: boolean) { public gzipUncompress<T>(bytes: ArrayBuffer, toString?: boolean) {
//if(this.webWorker) { return this.performTaskWorker<T>('unzip', bytes, toString);
return this.performTaskWorker<T>('unzip', bytes, toString);
//}
} }
} }

18
src/lib/mtproto/apiManager.ts

@ -78,14 +78,16 @@ export class ApiManager {
AppStorage.remove('dc', 'user_auth', 'stickerSets'); AppStorage.remove('dc', 'user_auth', 'stickerSets');
this.baseDcID = 0; this.baseDcID = 0;
this.telegramMeNotify(false); this.telegramMeNotify(false);
return this.mtpClearStorage(); this.mtpClearStorage();
}, (error) => { }, (error) => {
storageKeys.push('dc', 'user_auth', 'stickerSets'); storageKeys.push('dc', 'user_auth', 'stickerSets');
AppStorage.remove(storageKeys); AppStorage.remove(storageKeys);
this.baseDcID = 0; this.baseDcID = 0;
error.handled = true; error.handled = true;
this.telegramMeNotify(false); this.telegramMeNotify(false);
return this.mtpClearStorage(); this.mtpClearStorage();
}).then(() => {
location.pathname = '/';
}); });
} }
@ -224,17 +226,7 @@ export class ApiManager {
setTimeout(() => { setTimeout(() => {
if(!error.handled) { if(!error.handled) {
if(error.code == 401) { if(error.code == 401) {
// @ts-ignore WARNING! this.logOut();
this.logOut().finally(() => {
if(location.protocol == 'http:' &&
!Config.Modes.http &&
Config.App.domains.indexOf(location.hostname) != -1) {
location.href = location.href.replace(/^http:/, 'https:');
} else {
location.hash = '/login';
// AppRuntimeManager.reload(); // WARNING
}
})
} else { } else {
// ErrorService.show({error: error}); // WARNING // ErrorService.show({error: error}); // WARNING
} }

13
src/lib/mtproto/transports/abridged.ts

@ -1,6 +1,7 @@
import { bytesFromHex, addPadding } from "../../bin_utils"; //import { bytesFromHex, addPadding } from "../../bin_utils";
import { Codec } from "./codec";
class AbridgedPacketCodec { class AbridgedPacketCodec implements Codec {
public tag = 0xef; public tag = 0xef;
public obfuscateTag = new Uint8Array([this.tag, this.tag, this.tag, this.tag]); public obfuscateTag = new Uint8Array([this.tag, this.tag, this.tag, this.tag]);
@ -9,12 +10,14 @@ class AbridgedPacketCodec {
let header: Uint8Array; let header: Uint8Array;
if(len < 127) { if(len < 127) {
header = new Uint8Array([len]); header = new Uint8Array([len]);
} else { } else { // Length: payload length, divided by four, and encoded as 3 length bytes (little endian)
header = new Uint8Array([0x7f, ...addPadding(bytesFromHex(len.toString(16)).reverse(), 3, true)/* .reverse() */]); //header = new Uint8Array([0x7f, ...addPadding(bytesFromHex(len.toString(16)).reverse(), 3, true)/* .reverse() */]);
header = new Uint8Array([0x7f, len & 0xFF, (len >> 8) & 0xFF, (len >> 16) & 0xFF]);
//console.log('got nobody cause im braindead', header, len); //console.log('got nobody cause im braindead', header, len);
} }
return new Uint8Array([...header, ...data]); return header.concat(data);
//return new Uint8Array([...header, ...data]);
} }
public readPacket(data: Uint8Array) { public readPacket(data: Uint8Array) {

4
src/lib/mtproto/transports/codec.ts

@ -1,6 +1,6 @@
export interface Codec { export interface Codec {
tag?: number; tag: number;
obfuscateTag?: Uint8Array; obfuscateTag: Uint8Array;
encodePacket: (data: Uint8Array) => Uint8Array; encodePacket: (data: Uint8Array) => Uint8Array;
readPacket: (data: Uint8Array) => Uint8Array; readPacket: (data: Uint8Array) => Uint8Array;

49
src/lib/mtproto/transports/intermediate.ts

@ -0,0 +1,49 @@
import { nextRandomInt } from "../../bin_utils";
import { Codec } from "./codec";
class IntermediatePacketCodec implements Codec {
public tag = 0xee;
public obfuscateTag = new Uint8Array([this.tag, this.tag, this.tag, this.tag]);
public encodePacket(data: Uint8Array) {
let len = data.byteLength;
let header = new Uint8Array(new Uint32Array([len]).buffer);
return header.concat(data);
}
public readPacket(data: Uint8Array) {
let length = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
return data.slice(4, 4 + length);
}
}
/* Data packets are aligned to 4bytes. This codec adds random bytes of size
from 0 to 3 bytes, which are ignored by decoder. */
class PaddedIntermediatePacketCodec extends IntermediatePacketCodec {
public tag = 0xdd;
public obfuscateTag = new Uint8Array([this.tag, this.tag, this.tag, this.tag]);
public encodePacket(data: Uint8Array) {
let padding = new Uint8Array(nextRandomInt(3)).randomize();
let len = data.byteLength + padding.byteLength;
let header = new Uint8Array(new Uint32Array([len]).buffer);
console.log('encodePacket', padding, len, data, header);
return header.concat(data, padding);
}
public readPacket(data: Uint8Array) {
let padLength = data.byteLength % 4;
if(padLength > 0) {
return data.slice(4, -padLength);
}
return data.slice(4);
}
}
export default new IntermediatePacketCodec();
//export default new PaddedIntermediatePacketCodec();

79
src/lib/mtproto/transports/websocket.ts

@ -1,15 +1,25 @@
import MTTransport from './transport'; import MTTransport from './transport';
import aesjs from 'aes-js'; //import aesjs from 'aes-js';
import abridgetPacketCodec from './abridged'; import {CTR} from '@cryptography/aes';
//import abridgetPacketCodec from './abridged';
import intermediatePacketCodec from './intermediate';
import {MTPNetworker} from '../networker'; import {MTPNetworker} from '../networker';
import { logger } from '../../polyfill'; import { logger } from '../../polyfill';
import { bytesFromWordss } from '../../bin_utils';
import { Codec } from './codec';
/*
@cryptography/aes не работает с массивами которые не кратны 4, поэтому использую intermediate а не abridged
*/
export class Obfuscation { export class Obfuscation {
public enc: aesjs.ModeOfOperation.ModeOfOperationCTR; /* public enc: aesjs.ModeOfOperation.ModeOfOperationCTR;
public dec: aesjs.ModeOfOperation.ModeOfOperationCTR; public dec: aesjs.ModeOfOperation.ModeOfOperationCTR; */
public init() { public encNew: CTR;
public decNew: CTR;
public init(codec: Codec) {
const initPayload = new Uint8Array(64); const initPayload = new Uint8Array(64);
initPayload.randomize(); initPayload.randomize();
@ -22,6 +32,7 @@ export class Obfuscation {
val != 0x20544547 && val != 0x20544547 &&
val != 0x4954504f && val != 0x4954504f &&
val != 0xeeeeeeee && val != 0xeeeeeeee &&
val != 0xdddddddd &&
val2 != 0x00000000) { val2 != 0x00000000) {
//initPayload[56] = initPayload[57] = initPayload[58] = initPayload[59] = transport; //initPayload[56] = initPayload[57] = initPayload[58] = initPayload[59] = transport;
break; break;
@ -38,10 +49,13 @@ export class Obfuscation {
let decKey = reversedPayload.slice(8, 40); let decKey = reversedPayload.slice(8, 40);
let decIv = reversedPayload.slice(40, 56); let decIv = reversedPayload.slice(40, 56);
this.enc = new aesjs.ModeOfOperation.ctr(encKey, new aesjs.Counter(encIv as any)); /* this.enc = new aesjs.ModeOfOperation.ctr(encKey, new aesjs.Counter(encIv as any));
this.dec = new aesjs.ModeOfOperation.ctr(decKey, new aesjs.Counter(decIv as any)); this.dec = new aesjs.ModeOfOperation.ctr(decKey, new aesjs.Counter(decIv as any)); */
this.encNew = new CTR(encKey, encIv);
this.decNew = new CTR(decKey, decIv);
initPayload.set(abridgetPacketCodec.obfuscateTag, 56); initPayload.set(intermediatePacketCodec.obfuscateTag, 56);
const encrypted = this.encode(initPayload); const encrypted = this.encode(initPayload);
initPayload.set(encrypted.slice(56, 64), 56); initPayload.set(encrypted.slice(56, 64), 56);
@ -49,12 +63,47 @@ export class Obfuscation {
return initPayload; return initPayload;
} }
/* public encode(payload: Uint8Array) {
let res = this.enc.encrypt(payload);
try {
let arr = this.encNew.encrypt(payload);
//let resNew = bytesFromWords({words: arr, sigBytes: arr.length});
let resNew = new Uint8Array(bytesFromWordss(arr));
console.log('Obfuscation: encode comparison:', res, arr, resNew, res.hex == resNew.hex);
} catch(err) {
console.error('Obfuscation: error:', err);
}
return res;
}
public decode(payload: Uint8Array) {
let res = this.dec.encrypt(payload);
try {
let arr = this.decNew.decrypt(payload);
//let resNew = bytesFromWords({words: arr, sigBytes: arr.length});
let resNew = new Uint8Array(bytesFromWordss(arr));
console.log('Obfuscation: decode comparison:', res, arr, resNew, res.hex == resNew.hex);
} catch(err) {
console.error('Obfuscation: error:', err);
}
return res;
} */
public encode(payload: Uint8Array) { public encode(payload: Uint8Array) {
return this.enc.encrypt(payload); let res = this.encNew.encrypt(payload);
let bytes = new Uint8Array(bytesFromWordss(res));
return bytes;
} }
public decode(data: Uint8Array) { public decode(payload: Uint8Array) {
return this.dec.encrypt(data); let res = this.decNew.decrypt(payload);
let bytes = new Uint8Array(bytesFromWordss(res));
return bytes;
} }
} }
@ -75,6 +124,8 @@ export default class Socket extends MTTransport {
debug = false; debug = false;
codec = intermediatePacketCodec;
constructor(dcID: number, url: string) { constructor(dcID: number, url: string) {
super(dcID, url); super(dcID, url);
@ -103,7 +154,7 @@ export default class Socket extends MTTransport {
handleOpen = () => { handleOpen = () => {
this.log('opened'); this.log('opened');
this.ws.send(this.obfuscation.init()); this.ws.send(this.obfuscation.init(this.codec));
this.connected = true; this.connected = true;
this.releasePending(); this.releasePending();
@ -126,7 +177,7 @@ export default class Socket extends MTTransport {
this.debug && this.log('<-', 'handleMessage', event); this.debug && this.log('<-', 'handleMessage', event);
let data = this.obfuscation.decode(new Uint8Array(event.data)); let data = this.obfuscation.decode(new Uint8Array(event.data));
data = abridgetPacketCodec.readPacket(data); data = this.codec.readPacket(data);
if(this.networker) { // authenticated! if(this.networker) { // authenticated!
//this.pending = this.pending.filter(p => p.body); // clear pending //this.pending = this.pending.filter(p => p.body); // clear pending
@ -175,7 +226,7 @@ export default class Socket extends MTTransport {
let pending = this.pending[i]; let pending = this.pending[i];
let {body} = pending; let {body} = pending;
if(body) { if(body) {
let toEncode = abridgetPacketCodec.encodePacket(body); let toEncode = this.codec.encodePacket(body);
//console.log('send before obf:', /* body.hex, nonce.hex, */ toEncode.hex); //console.log('send before obf:', /* body.hex, nonce.hex, */ toEncode.hex);
let enc = this.obfuscation.encode(toEncode); let enc = this.obfuscation.encode(toEncode);

7
src/lib/polyfill.ts

@ -80,6 +80,10 @@ Uint8Array.prototype.concat = function(...args: Array<Uint8Array | ArrayBuffer |
return bufferConcats(this, ...args); return bufferConcats(this, ...args);
}; };
Uint8Array.prototype.toString = function() {
return String.fromCharCode.apply(null, [...this]);
};
Array.prototype.forEachReverse = function<T>(callback: (value: T, index?: number, array?: Array<T>) => void) { Array.prototype.forEachReverse = function<T>(callback: (value: T, index?: number, array?: Array<T>) => void) {
let length = this.length; let length = this.length;
for(var i = length - 1; i >= 0; --i) { for(var i = length - 1; i >= 0; --i) {
@ -108,7 +112,8 @@ declare global {
interface Uint8Array { interface Uint8Array {
hex: string; hex: string;
randomize: () => Uint8Array, randomize: () => Uint8Array,
concat: (...args: Array<Uint8Array | ArrayBuffer | number[]>) => Uint8Array concat: (...args: Array<Uint8Array | ArrayBuffer | number[]>) => Uint8Array,
toString: () => string
} }
interface Array<T> { interface Array<T> {

35
src/scss/partials/_chat.scss

@ -1,3 +1,5 @@
$chat-max-width: 696px;
.chat-container { .chat-container {
display: flex; display: flex;
// padding: 200px; // padding: 200px;
@ -57,7 +59,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
box-shadow: 0 1px 2px 0 rgba(16, 35, 47, 0.07); box-shadow: 0 1px 2px 0 rgba(16, 35, 47, 0.07);
padding: .5rem 1rem; padding: .5rem 17px;
flex: 0 0 auto; /* Forces side columns to stay same width */ flex: 0 0 auto; /* Forces side columns to stay same width */
min-height: 60px; min-height: 60px;
max-height: 60px; max-height: 60px;
@ -71,8 +73,9 @@
} }
.content { .content {
padding-left: 1rem;
flex: 1; flex: 1;
padding-left: 17px;
line-height: 1.6;
} }
.person { .person {
@ -93,7 +96,7 @@
.bottom { .bottom {
font-size: 14px; font-size: 14px;
line-height: 18px; line-height: 18px;
color: $placeholder-color; color: #707579;
.online { .online {
color: $darkblue; color: $darkblue;
@ -220,7 +223,7 @@
.bubble { .bubble {
padding-top: 5px; padding-top: 5px;
display: grid; display: grid;
grid-template-columns: 1fr 700px 1fr; grid-template-columns: 1fr $chat-max-width 1fr;
grid-row-gap: 0px; grid-row-gap: 0px;
&:before, &:after { &:before, &:after {
@ -1069,9 +1072,9 @@
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%; width: 100%;
max-width: 700px; max-width: $chat-max-width;
padding-top: .35rem; padding-top: .35rem;
padding-bottom: 1rem; padding-bottom: 21px;
justify-content: space-between; justify-content: space-between;
flex: 0 0 auto; /* Forces side columns to stay same width */ flex: 0 0 auto; /* Forces side columns to stay same width */
position: relative; position: relative;
@ -1080,8 +1083,8 @@
background: none; background: none;
border: none; border: none;
width: 100%; width: 100%;
padding: .5rem .5rem; font-size: 16px;
font-size: .95rem; padding: 10px 9px;
/* height: 100%; */ /* height: 100%; */
max-height: 30rem; max-height: 30rem;
overflow-y: none; overflow-y: none;
@ -1093,7 +1096,7 @@
[contenteditable=true]:empty:before { [contenteditable=true]:empty:before {
content: attr(data-placeholder); content: attr(data-placeholder);
color: #9e9e9e; color: #a2acb4;
display: block; /* For Firefox By Ariel Flesler */ display: block; /* For Firefox By Ariel Flesler */
} }
@ -1101,8 +1104,8 @@
flex: 0 0 auto; flex: 0 0 auto;
font-size: 1.5rem; font-size: 1.5rem;
line-height: 1.5rem; line-height: 1.5rem;
height: 3.25rem; height: 54px;
width: 3.25rem; width: 54px;
color: #9e9e9e; color: #9e9e9e;
background-color: #fff; background-color: #fff;
align-self: flex-end; align-self: flex-end;
@ -1130,7 +1133,7 @@
margin-right: 9px; margin-right: 9px;
padding: 4.5px .5rem; padding: 4.5px .5rem;
/* padding: 3px .5rem 6px .5rem; */ /* padding: 3px .5rem 6px .5rem; */
min-height: 3.25rem; min-height: 54px;
max-height: 30rem; max-height: 30rem;
caret-color: $button-primary-background; caret-color: $button-primary-background;
flex: 1; flex: 1;
@ -1204,11 +1207,13 @@
.btn-icon { .btn-icon {
display: block; display: block;
color: $placeholder-color;
font-size: 1.5rem;
line-height: 1.5rem;
transition: .2s color; transition: .2s color;
flex: 0 0 auto; flex: 0 0 auto;
font-size: 24px;
line-height: 24px;
padding: 10px 7px 9px 7.5px;
color: #8d969c;
&.active { &.active {
color: $blue; color: $blue;

28
src/scss/partials/_chatlist.scss

@ -2,18 +2,19 @@
.input-search { .input-search {
position: relative; position: relative;
width: 100%; width: 100%;
margin-left: 1rem; margin-left: 22px;
input { input {
background-color: rgba(112, 117, 121, .08); background-color: rgba(112, 117, 121, .08);
border: 2px solid transparent; border: 2px solid transparent;
height: 3rem; height: 44px;
border-radius: 22px; border-radius: 22px;
box-sizing: border-box; box-sizing: border-box;
padding: 0 1.5rem 0 3rem; padding: 0px 1.5rem 0 40px;
-webkit-transition: all .15s ease-out; -webkit-transition: all .15s ease-out;
transition: all .15s ease-out; transition: all .15s ease-out;
width: 100%; width: 100%;
font-size: 16px;
&:focus { &:focus {
background-color: rgba(112, 117, 121, 0); background-color: rgba(112, 117, 121, 0);
@ -28,15 +29,19 @@
.tgico { .tgico {
position: absolute; position: absolute;
left: 1rem; left: 12px;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
text-align: center; text-align: center;
font-size: 1.65rem; font-size: 24px;
color: $color-gray; color: $color-gray;
opacity: .6; opacity: .6;
-webkit-transition: all .15s ease-out; -webkit-transition: all .15s ease-out;
transition: all .15s ease-out; transition: all .15s ease-out;
&:before {
vertical-align: middle;
}
} }
} }
@ -69,8 +74,8 @@
flex-direction: row; flex-direction: row;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
padding: 8px 8.5px; padding: 7px 8.5px;
margin: 0 8.5px 0 8px; margin: 0px 8px 2px 7px;
overflow: hidden; overflow: hidden;
&:hover { &:hover {
@ -84,7 +89,7 @@
.pinned-delimiter { .pinned-delimiter {
display: flex; display: flex;
padding: 8px 0 4px; padding: 6px 0 6px;
span { span {
margin: 0; margin: 0;
@ -111,7 +116,9 @@
/* span:not(.tgico-pinnedchat):not(.emoji):last-child { */ /* span:not(.tgico-pinnedchat):not(.emoji):last-child { */
.user-title + span { .user-title + span {
/* font-size: .9rem; */ /* font-size: .9rem; */
font-size: .8rem; //font-size: .8rem;
font-size: .75rem;
padding: 2px 0px 0px 0px;
} }
.user-last-message + span:not(.tgico-pinnedchat) { .user-last-message + span:not(.tgico-pinnedchat) {
@ -143,7 +150,7 @@
overflow: hidden; overflow: hidden;
color: $color-gray; color: $color-gray;
flex: 1 1 auto; flex: 1 1 auto;
padding-right: 6.5px; padding-right: 3.5px;
padding-left: 10px; padding-left: 10px;
} }
@ -209,6 +216,7 @@
color: #fff; color: #fff;
border-radius: 12px; border-radius: 12px;
margin-top: 1.5px; margin-top: 1.5px;
margin-right: -2px;
} }
.unread, .unread-muted { .unread, .unread-muted {

48
src/scss/partials/_emojiDropdown.scss

@ -8,7 +8,7 @@
height: 420px; height: 420px;
background: #fff; background: #fff;
box-shadow: 0px 5px 10px 5px rgba(16, 35, 47, 0.14); box-shadow: 0px 5px 10px 5px rgba(16, 35, 47, 0.14);
border-radius: 12px; border-radius: 10px;
z-index: 2; z-index: 2;
/* display: none; */ /* display: none; */
display: flex; display: flex;
@ -25,7 +25,14 @@
} }
> .menu-horizontal { > .menu-horizontal {
padding: 0 3rem; padding: 0px 58px 0px 58px;
font-weight: 500;
margin-top: 2px;
> li.active:after {
left: 29px;
right: 28px;
}
} }
.emoji-container { .emoji-container {
@ -34,6 +41,10 @@
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
} }
.btn-icon {
color: #8d969c;
}
.tabs-container { .tabs-container {
/* width: 300%; */ /* width: 300%; */
@ -42,19 +53,27 @@
.emoji-category { .emoji-category {
font-size: 2.25rem; font-size: 2.25rem;
line-height: 2.25rem; line-height: 2.25rem;
margin-top: 1px; padding-top: 1px;
display: grid;
grid-column-gap: 2.44px;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
&:first-child {
padding-top: 5px;
}
> * { > * {
margin: 0; margin: 0;
padding: 5px 5px; padding: 4px 4px;
line-height: inherit; line-height: inherit;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
width: 44px; width: 42px;
height: 44px; height: 42px;
.emoji { .emoji {
width: 100%; width: 100%;
@ -72,7 +91,7 @@
} */ } */
} }
.emoji-category, .sticker-category { .sticker-category {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
@ -104,6 +123,7 @@
padding: 1px 2.5px; padding: 1px 2.5px;
justify-content: center; justify-content: center;
border-radius: 12px; border-radius: 12px;
padding: 0;
&:hover { &:hover {
background-color: rgba(112, 117, 121, 0.08); background-color: rgba(112, 117, 121, 0.08);
@ -129,7 +149,7 @@
> div:first-of-type { > div:first-of-type {
flex: 1; flex: 1;
padding: 0; padding: 0;
padding-top: 10px; //padding-top: 10px;
} }
} }
@ -146,9 +166,9 @@
.emoji-padding, .stickers-padding { .emoji-padding, .stickers-padding {
.menu-horizontal { .menu-horizontal {
height: 50px; height: 47px;
border-bottom: none; border-bottom: none;
padding: 0; padding: 2px 2px 2px 2px;
width: 100%; width: 100%;
li { li {
@ -168,9 +188,11 @@
} }
} }
/* #content-stickers { #content-stickers {
padding: 0; .scrollable {
} */ padding: 15px 5px 0;
}
}
.emoji-padding, .stickers-padding { .emoji-padding, .stickers-padding {
.menu-horizontal > li { .menu-horizontal > li {

2
src/scss/partials/_sidebar.scss

@ -15,7 +15,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: .5rem 1.25rem; padding: 7.5px 20px 7.5px 15px;
min-height: 60px; min-height: 60px;
.sidebar-title { .sidebar-title {

72
src/scss/style.scss

@ -48,7 +48,7 @@ html, body {
} }
html { html {
font-size: 15px; font-size: $text-size;
} }
a { a {
@ -83,8 +83,8 @@ h1, h2, h3, h4, h5, h6 {
} }
h4 { h4 {
font-size: 2.28rem; font-size: 2rem;
margin: 1.52rem 0 .912rem 0; margin: 1.5rem 0 1rem 0;
line-height: 110%; line-height: 110%;
} }
@ -94,7 +94,6 @@ input {
.subtitle { .subtitle {
/* font-weight: 500; */ /* font-weight: 500; */
font-size: 1rem;
color: $placeholder-color; color: $placeholder-color;
line-height: 1.25; line-height: 1.25;
} }
@ -103,7 +102,8 @@ input {
.page-authCode .input-wrapper, .page-authCode .input-wrapper,
.page-signUp .input-wrapper, .page-signUp .input-wrapper,
.page-password .input-wrapper { .page-password .input-wrapper {
margin-top: 3rem; /* margin-top: 3rem; */
margin-top: 50px;
} }
.page-authCode { .page-authCode {
@ -281,7 +281,7 @@ input {
&[class*=" tgico-"] { &[class*=" tgico-"] {
line-height: 52px; line-height: 52px;
font-size: 1.5rem; font-size: 28px;
} }
path { path {
@ -496,6 +496,14 @@ input {
} }
} }
.page-sign {
.auth-image {
width: 160px;
height: 160px;
margin-bottom: 45px;
}
}
.page-signUp { .page-signUp {
.auth-image { .auth-image {
border-radius: 50%; border-radius: 50%;
@ -524,7 +532,7 @@ input {
} }
.input-wrapper { .input-wrapper {
width: 350px; width: 360px;
margin: 0 auto; margin: 0 auto;
} }
@ -536,7 +544,7 @@ input {
content: " "; content: " ";
top: 50%; top: 50%;
bottom: 0; bottom: 0;
right: 15px; right: 21px;
cursor: pointer; cursor: pointer;
height: 0; height: 0;
@ -546,12 +554,12 @@ input {
border-radius: 1px; border-radius: 1px;
border-width: 0 2px 2px 0; border-width: 0 2px 2px 0;
display: inline-block; display: inline-block;
padding: 4px; padding: 5px;
vertical-align: middle; vertical-align: middle;
z-index: 2; z-index: 2;
margin-top: -7px; margin-top: -9px;
transform: rotate(45deg); transform: rotate(45deg);
-webkit-transform: rotate(45deg); -webkit-transform: rotate(45deg);
transition: .2s all; transition: .2s all;
@ -560,13 +568,12 @@ input {
label { label {
position: absolute; position: absolute;
color: $placeholder-color; color: $placeholder-color;
left: 12.5px; left: 1rem;
right: auto; right: auto;
z-index: 2; z-index: 2;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
background-color: #fff; background-color: #fff;
font-size: 0.85rem;
transition: .2s all, .1s opacity; transition: .2s all, .1s opacity;
display: inline-block; display: inline-block;
cursor: text; cursor: text;
@ -574,12 +581,11 @@ input {
input { input {
border: 1px solid #DADCE0; border: 1px solid #DADCE0;
border-radius: $border-radius; border-radius: $border-radius-big;
padding: 0 12.5px; padding: 0 1rem;
box-sizing: border-box; box-sizing: border-box;
font-size: 0.85rem;
width: 100%; width: 100%;
height: 45px; height: 54px;
transition: .2s border-color; transition: .2s border-color;
position: relative; position: relative;
z-index: 1; z-index: 1;
@ -587,8 +593,8 @@ input {
&:focus { &:focus {
border-color: $button-primary-background; border-color: $button-primary-background;
border-width: 1.5px; border-width: 2px;
padding: 0 12px; padding: 0 calc(1rem - 1px);
} }
&:disabled { &:disabled {
@ -606,7 +612,7 @@ input {
} }
&:focus ~ .arrow-down { &:focus ~ .arrow-down {
margin-top: -2px; margin-top: -4px;
transform: rotate(225deg); transform: rotate(225deg);
-webkit-transform: rotate(225deg); -webkit-transform: rotate(225deg);
border-color: $button-primary-background; border-color: $button-primary-background;
@ -617,21 +623,21 @@ input {
} }
&:focus + label, &:valid + label, &:disabled + label { &:focus + label, &:valid + label, &:disabled + label {
top: -8.5px; top: -.5rem;
transform: none; transform: none;
padding: 0 5px; padding: 0 5px;
left: 7.5px; left: .75rem;
font-size: 0.85rem!important; font-size: 0.75rem!important;
opacity: 1; opacity: 1;
} }
} }
} }
.checkbox-field { .checkbox-field {
margin: 1rem 0; margin: 1.25rem 0;
display: block; display: block;
text-align: left; text-align: left;
padding: 0 1rem; padding: 0 19px;
/* font-weight: 500; */ /* font-weight: 500; */
position: relative; position: relative;
} }
@ -646,12 +652,11 @@ input {
& + span { & + span {
position: relative; position: relative;
padding-left: 35px; padding-left: calc(18px + 2.25rem);
cursor: pointer; cursor: pointer;
display: inline-block; display: inline-block;
height: 25px; height: 25px;
line-height: 25px; line-height: 25px;
font-size: 1rem;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
@ -718,7 +723,7 @@ input {
} }
.input-wrapper > * ~ * { .input-wrapper > * ~ * {
margin-top: 1.2rem; margin-top: 1.5rem;
} }
.select-wrapper { .select-wrapper {
@ -726,7 +731,7 @@ input {
/* height: auto; */ /* height: auto; */
position: absolute; position: absolute;
width: 100%; width: 100%;
top: calc(100% + 10px); top: calc(100% + .5rem);
left: 0; left: 0;
overflow: hidden; overflow: hidden;
background-color: #fff; background-color: #fff;
@ -737,15 +742,15 @@ input {
flex-wrap: wrap; flex-wrap: wrap;
ul { ul {
margin: 10px 0; margin: .5rem 0;
} }
li { li {
/* display: flex; */ /* display: flex; */
align-items: center; align-items: center;
padding: 0 12.5px; padding: 0 1rem;
justify-content: space-between; justify-content: space-between;
height: 50px; height: 3.5rem;
cursor: pointer; cursor: pointer;
font-weight: 500; font-weight: 500;
@ -850,8 +855,7 @@ input:focus, button:focus {
border-radius: $border-radius; border-radius: $border-radius;
width: 100%; width: 100%;
text-align: center; text-align: center;
font-size: 0.85rem; height: 54px;
height: 45px;
border: none; border: none;
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
@ -1385,7 +1389,7 @@ div.scrollable::-webkit-scrollbar-thumb {
text-align: center; text-align: center;
flex: 1; flex: 1;
user-select: none; user-select: none;
font-size: 16px; font-size: 1rem;
&.active { &.active {
position: relative; position: relative;

Loading…
Cancel
Save