Browse Source

scroll try #10

master
Eduard Kuzmenko 5 years ago
parent
commit
c86671ce12
  1. BIN
      .DS_Store
  2. 213
      package-lock.json
  3. 4
      package.json
  4. 30
      src/components/misc.ts
  5. 349
      src/components/scrollable.ts
  6. 61
      src/components/wrappers.ts
  7. 591
      src/lib/appManagers/appImManager.ts
  8. 7
      src/lib/appManagers/appSidebarLeft.ts
  9. 193
      src/lib/appManagers/appSidebarRight.ts
  10. 12
      src/lib/ckin.js
  11. 4
      src/lib/lottieLoader.ts
  12. 22
      src/lib/polyfill.ts
  13. 2
      src/lib/utils.js
  14. 40
      src/scss/partials/_chat.scss
  15. 286
      src/scss/partials/_rightSIdebar.scss
  16. 231
      src/scss/partials/_sidebar.scss
  17. 34
      src/scss/style.scss
  18. 4
      tsconfig.json
  19. 2
      webpack.prod.js

BIN
.DS_Store vendored

Binary file not shown.

213
package-lock.json generated

@ -4926,13 +4926,13 @@ @@ -4926,13 +4926,13 @@
}
},
"globule": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
"integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
"integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
"dev": true,
"requires": {
"glob": "~7.1.1",
"lodash": "~4.17.10",
"lodash": "~4.17.12",
"minimatch": "~3.0.2"
}
},
@ -5555,13 +5555,10 @@ @@ -5555,13 +5555,10 @@
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
},
"is-finite": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
"integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
"dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
"integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
@ -6247,9 +6244,9 @@ @@ -6247,9 +6244,9 @@
}
},
"js-base64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
"integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
"integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==",
"dev": true
},
"js-sha3": {
@ -7088,9 +7085,9 @@ @@ -7088,9 +7085,9 @@
}
},
"node-sass": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.0.tgz",
"integrity": "sha512-W1XBrvoJ1dy7VsvTAS5q1V45lREbTlZQqFbiHb3R3OTTCma0XBtuG6xZ6Z4506nR4lmHPTqVRwxT6KgtWC97CA==",
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz",
"integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==",
"dev": true,
"requires": {
"async-foreach": "^0.1.3",
@ -7191,9 +7188,9 @@ @@ -7191,9 +7188,9 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
"npm": {
"version": "6.13.4",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.13.4.tgz",
"integrity": "sha512-vTcUL4SCg3AzwInWTbqg1OIaOXlzKSS8Mb8kc5avwrJpcvevDA5J9BhYSuei+fNs3pwOp4lzA5x2FVDXACvoXA==",
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.14.1.tgz",
"integrity": "sha512-2hi3UF7g5VL8VKm46Bx5GAW28DPb8BJZbj2uONMBMhY8XkJ56lSmHJNFcjTQr7KHZqWqiBT5BugaQEy+Y/4T2g==",
"dev": true,
"requires": {
"JSONStream": "^1.3.5",
@ -7202,12 +7199,12 @@ @@ -7202,12 +7199,12 @@
"ansistyles": "~0.1.3",
"aproba": "^2.0.0",
"archy": "~1.0.0",
"bin-links": "^1.1.6",
"bin-links": "^1.1.7",
"bluebird": "^3.5.5",
"byte-size": "^5.0.1",
"cacache": "^12.0.3",
"call-limit": "^1.1.1",
"chownr": "^1.1.3",
"chownr": "^1.1.4",
"ci-info": "^2.0.0",
"cli-columns": "^3.1.2",
"cli-table3": "^0.5.1",
@ -7227,7 +7224,7 @@ @@ -7227,7 +7224,7 @@
"glob": "^7.1.4",
"graceful-fs": "^4.2.3",
"has-unicode": "~2.0.1",
"hosted-git-info": "^2.8.5",
"hosted-git-info": "^2.8.7",
"iferr": "^1.0.2",
"imurmurhash": "*",
"infer-owner": "^1.0.4",
@ -7245,7 +7242,7 @@ @@ -7245,7 +7242,7 @@
"libnpmorg": "^1.0.1",
"libnpmsearch": "^2.0.2",
"libnpmteam": "^1.0.2",
"libnpx": "^10.2.0",
"libnpx": "^10.2.2",
"lock-verify": "^2.1.0",
"lockfile": "^1.0.4",
"lodash._baseindexof": "*",
@ -7264,7 +7261,7 @@ @@ -7264,7 +7261,7 @@
"mississippi": "^3.0.0",
"mkdirp": "~0.5.1",
"move-concurrently": "^1.0.1",
"node-gyp": "^5.0.5",
"node-gyp": "^5.0.7",
"nopt": "~4.0.1",
"normalize-package-data": "^2.5.0",
"npm-audit-report": "^1.3.2",
@ -7272,16 +7269,16 @@ @@ -7272,16 +7269,16 @@
"npm-install-checks": "^3.0.2",
"npm-lifecycle": "^3.1.4",
"npm-package-arg": "^6.1.1",
"npm-packlist": "^1.4.7",
"npm-packlist": "^1.4.8",
"npm-pick-manifest": "^3.0.2",
"npm-profile": "^4.0.2",
"npm-registry-fetch": "^4.0.2",
"npm-registry-fetch": "^4.0.3",
"npm-user-validate": "~1.0.0",
"npmlog": "~4.1.2",
"once": "~1.4.0",
"opener": "^1.5.1",
"osenv": "^0.1.5",
"pacote": "^9.5.11",
"pacote": "^9.5.12",
"path-is-inside": "~1.0.2",
"promise-inflight": "~1.0.1",
"qrcode-terminal": "^0.12.0",
@ -7292,7 +7289,7 @@ @@ -7292,7 +7289,7 @@
"read-installed": "~4.0.3",
"read-package-json": "^2.1.1",
"read-package-tree": "^5.3.1",
"readable-stream": "^3.4.0",
"readable-stream": "^3.6.0",
"readdir-scoped-modules": "^1.1.0",
"request": "^2.88.0",
"retry": "^0.12.0",
@ -7484,7 +7481,7 @@ @@ -7484,7 +7481,7 @@
}
},
"bin-links": {
"version": "1.1.6",
"version": "1.1.7",
"bundled": true,
"dev": true,
"requires": {
@ -7597,7 +7594,7 @@ @@ -7597,7 +7594,7 @@
}
},
"chownr": {
"version": "1.1.3",
"version": "1.1.4",
"bundled": true,
"dev": true
},
@ -8037,7 +8034,7 @@ @@ -8037,7 +8034,7 @@
}
},
"env-paths": {
"version": "1.0.0",
"version": "2.2.0",
"bundled": true,
"dev": true
},
@ -8381,7 +8378,7 @@ @@ -8381,7 +8378,7 @@
}
},
"get-caller-file": {
"version": "1.0.2",
"version": "1.0.3",
"bundled": true,
"dev": true
},
@ -8490,7 +8487,7 @@ @@ -8490,7 +8487,7 @@
"dev": true
},
"hosted-git-info": {
"version": "2.8.5",
"version": "2.8.7",
"bundled": true,
"dev": true
},
@ -8606,7 +8603,7 @@ @@ -8606,7 +8603,7 @@
}
},
"invert-kv": {
"version": "1.0.0",
"version": "2.0.0",
"bundled": true,
"dev": true
},
@ -8795,11 +8792,11 @@ @@ -8795,11 +8792,11 @@
"dev": true
},
"lcid": {
"version": "1.0.0",
"version": "2.0.0",
"bundled": true,
"dev": true,
"requires": {
"invert-kv": "^1.0.0"
"invert-kv": "^2.0.0"
}
},
"libcipm": {
@ -8972,7 +8969,7 @@ @@ -8972,7 +8969,7 @@
}
},
"libnpx": {
"version": "10.2.0",
"version": "10.2.2",
"bundled": true,
"dev": true,
"requires": {
@ -9123,17 +9120,34 @@ @@ -9123,17 +9120,34 @@
"ssri": "^6.0.0"
}
},
"map-age-cleaner": {
"version": "0.1.3",
"bundled": true,
"dev": true,
"requires": {
"p-defer": "^1.0.0"
}
},
"meant": {
"version": "1.0.1",
"bundled": true,
"dev": true
},
"mem": {
"version": "1.1.0",
"version": "4.3.0",
"bundled": true,
"dev": true,
"requires": {
"mimic-fn": "^1.0.0"
"map-age-cleaner": "^0.1.1",
"mimic-fn": "^2.0.0",
"p-is-promise": "^2.0.0"
},
"dependencies": {
"mimic-fn": {
"version": "2.1.0",
"bundled": true,
"dev": true
}
}
},
"mime-db": {
@ -9149,11 +9163,6 @@ @@ -9149,11 +9163,6 @@
"mime-db": "~1.35.0"
}
},
"mimic-fn": {
"version": "1.2.0",
"bundled": true,
"dev": true
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
@ -9241,6 +9250,11 @@ @@ -9241,6 +9250,11 @@
"bundled": true,
"dev": true
},
"nice-try": {
"version": "1.0.5",
"bundled": true,
"dev": true
},
"node-fetch-npm": {
"version": "2.0.2",
"bundled": true,
@ -9252,36 +9266,21 @@ @@ -9252,36 +9266,21 @@
}
},
"node-gyp": {
"version": "5.0.5",
"version": "5.0.7",
"bundled": true,
"dev": true,
"requires": {
"env-paths": "^1.0.0",
"glob": "^7.0.3",
"graceful-fs": "^4.1.2",
"mkdirp": "^0.5.0",
"nopt": "2 || 3",
"npmlog": "0 || 1 || 2 || 3 || 4",
"request": "^2.87.0",
"rimraf": "2",
"semver": "~5.3.0",
"env-paths": "^2.2.0",
"glob": "^7.1.4",
"graceful-fs": "^4.2.2",
"mkdirp": "^0.5.1",
"nopt": "^4.0.1",
"npmlog": "^4.1.2",
"request": "^2.88.0",
"rimraf": "^2.6.3",
"semver": "^5.7.1",
"tar": "^4.4.12",
"which": "1"
},
"dependencies": {
"nopt": {
"version": "3.0.6",
"bundled": true,
"dev": true,
"requires": {
"abbrev": "1"
}
},
"semver": {
"version": "5.3.0",
"bundled": true,
"dev": true
}
"which": "^1.3.1"
}
},
"nopt": {
@ -9381,12 +9380,13 @@ @@ -9381,12 +9380,13 @@
}
},
"npm-packlist": {
"version": "1.4.7",
"version": "1.4.8",
"bundled": true,
"dev": true,
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1"
"npm-bundled": "^1.0.1",
"npm-normalize-package-bin": "^1.0.1"
}
},
"npm-pick-manifest": {
@ -9410,7 +9410,7 @@ @@ -9410,7 +9410,7 @@
}
},
"npm-registry-fetch": {
"version": "4.0.2",
"version": "4.0.3",
"bundled": true,
"dev": true,
"requires": {
@ -9502,13 +9502,41 @@ @@ -9502,13 +9502,41 @@
"dev": true
},
"os-locale": {
"version": "2.1.0",
"version": "3.1.0",
"bundled": true,
"dev": true,
"requires": {
"execa": "^0.7.0",
"lcid": "^1.0.0",
"mem": "^1.1.0"
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
},
"dependencies": {
"cross-spawn": {
"version": "6.0.5",
"bundled": true,
"dev": true,
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"execa": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
}
}
}
},
"os-tmpdir": {
@ -9525,11 +9553,21 @@ @@ -9525,11 +9553,21 @@
"os-tmpdir": "^1.0.0"
}
},
"p-defer": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"p-finally": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"p-is-promise": {
"version": "2.1.0",
"bundled": true,
"dev": true
},
"p-limit": {
"version": "1.2.0",
"bundled": true,
@ -9563,7 +9601,7 @@ @@ -9563,7 +9601,7 @@
}
},
"pacote": {
"version": "9.5.11",
"version": "9.5.12",
"bundled": true,
"dev": true,
"requires": {
@ -9877,7 +9915,7 @@ @@ -9877,7 +9915,7 @@
}
},
"readable-stream": {
"version": "3.4.0",
"version": "3.6.0",
"bundled": true,
"dev": true,
"requires": {
@ -10261,11 +10299,18 @@ @@ -10261,11 +10299,18 @@
}
},
"string_decoder": {
"version": "1.2.0",
"version": "1.3.0",
"bundled": true,
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
"safe-buffer": "~5.2.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.2.0",
"bundled": true,
"dev": true
}
}
},
"stringify-package": {
@ -10646,7 +10691,7 @@ @@ -10646,7 +10691,7 @@
"dev": true
},
"yargs": {
"version": "11.0.0",
"version": "11.1.1",
"bundled": true,
"dev": true,
"requires": {
@ -10654,7 +10699,7 @@ @@ -10654,7 +10699,7 @@
"decamelize": "^1.1.1",
"find-up": "^2.1.0",
"get-caller-file": "^1.0.1",
"os-locale": "^2.0.0",
"os-locale": "^3.1.0",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",

4
package.json

@ -42,8 +42,8 @@ @@ -42,8 +42,8 @@
"jest": "^24.9.0",
"leemon": "^6.2.0",
"lottie-web": "^5.6.4",
"node-sass": "^4.13.0",
"npm": "^6.13.4",
"node-sass": "^4.13.1",
"npm": "^6.14.1",
"offscreen-canvas": "^0.1.1",
"on-build-webpack": "^0.1.0",
"overlayscrollbars": "^1.10.0",

30
src/components/misc.ts

@ -52,12 +52,24 @@ export function ripple(elem: Element) { @@ -52,12 +52,24 @@ export function ripple(elem: Element) {
}
}
export function putPreloader(elem: Element) {
export function putPreloader(elem: Element, returnDiv = false) {
const html = `
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
<circle class="preloader-path" cx="50" cy="50" r="20" fill="none" stroke-miterlimit="10"/>
</svg>`;
if(returnDiv) {
let div = document.createElement('div');
div.classList.add('preloader');
div.innerHTML = html;
if(elem) {
elem.appendChild(div);
}
return div;
}
elem.innerHTML += html;
}
@ -76,18 +88,19 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement, @@ -76,18 +88,19 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement,
///////console.log('tabs click:', target);
if(!target || target.classList.contains('active')) return false;
if(!target) return false;
let id = whichChild(target);
let tabContent = content.children[id] as HTMLDivElement;
if(onClick) onClick(id, tabContent);
if(target.classList.contains('active') || id == prevId) {
return false;
}
let prev = tabs.querySelector('li.active') as HTMLLIElement;
prev && prev.classList.remove('active');
target.classList.add('active');
let id = whichChild(target);
if(id == prevId) return false;
let tabContent = content.children[id] as HTMLDivElement;
tabContent.classList.add('active');
/////console.log('mambo rap', prevId, id);
@ -139,7 +152,6 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement, @@ -139,7 +152,6 @@ export function horizontalMenu(tabs: HTMLUListElement, content: HTMLDivElement,
if(onTransitionEnd) onTransitionEnd();
}, 200);
if(onClick) onClick(id, tabContent);
prevTabContent = tabContent;
});
}

349
src/components/scrollable.ts

@ -32,12 +32,13 @@ export default class Scrollable { @@ -32,12 +32,13 @@ export default class Scrollable {
public type: string;
public side: string;
public translate: string;
public scrollType: string;
public scrollSide: string;
public clientAxis: string;
public scrollSize = -1;
public size = 0;
public scrollSize = -1; // it will be scrollHeight
public size = 0; // it will be outerHeight of container (not scrollHeight)
public thumbSize = 0;
public hiddenElements: {
@ -57,11 +58,12 @@ export default class Scrollable { @@ -57,11 +58,12 @@ export default class Scrollable {
public onAddedBottom: () => void = null;
public onScrolledTop: () => void = null;
public onScrolledBottom: () => void = null;
public onScrolledTopFired = false;
public onScrolledBottomFired = false;
public topObserver: IntersectionObserver;
public bottomObserver: IntersectionObserver;
public splitObserver: IntersectionObserver;
public splitMeasureTop: Promise<{element: Element, height: number}[]> = null;
public splitMeasureBottom: Scrollable['splitMeasureTop'] = null;
public splitMeasureAdd: Promise<number> = null;
@ -86,37 +88,17 @@ export default class Scrollable { @@ -86,37 +88,17 @@ export default class Scrollable {
private log: ReturnType<typeof logger>;
private debug = false;
constructor(public el: HTMLDivElement, x = false, y = true, public splitOffset = 300, logPrefix = '') {
constructor(public el: HTMLDivElement, x = false, y = true, public splitOffset = 300, logPrefix = '', public appendTo = el, public onScrollOffset = splitOffset) {
this.container = document.createElement('div');
this.container.classList.add('scrollable');
this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''));
let arr = [];
for(let i = 0.001; i < 1; i += 0.001) arr.push(i);
this.topObserver = new IntersectionObserver(entries => {
let entry = entries[entries.length - 1];
//console.log('top intersection:', entries, entry.isIntersecting, entry.intersectionRatio > 0);
if(entry.isIntersecting) {
if(this.onScrolledTop) this.onScrolledTop();
}
// console.log('top intersection end');
}, {root: this.el});
this.bottomObserver = new IntersectionObserver(entries => {
let entry = entries[entries.length - 1];
//console.log('bottom intersection:', entries, entry, entry.isIntersecting, entry.intersectionRatio > 0);
if(entry.isIntersecting) {
if(this.onScrolledBottom) this.onScrolledBottom();
}
}, {root: this.el});
if(x) {
this.container.classList.add('scrollable-x');
this.type = 'width';
this.side = 'left';
this.translate = 'translateX';
this.scrollType = 'scrollWidth';
this.scrollSide = 'scrollLeft';
this.clientAxis = 'clientX';
@ -141,6 +123,7 @@ export default class Scrollable { @@ -141,6 +123,7 @@ export default class Scrollable {
this.container.classList.add('scrollable-y');
this.type = 'height';
this.side = 'top';
this.translate = 'translateY';
this.scrollType = 'scrollHeight';
this.scrollSide = 'scrollTop';
this.clientAxis = 'clientY';
@ -181,6 +164,7 @@ export default class Scrollable { @@ -181,6 +164,7 @@ export default class Scrollable {
window.addEventListener('resize', () => {
//this.resize.bind(this);
this.onScroll();
this.resize();
});
this.paddingTopDiv = document.createElement('div');
@ -188,9 +172,6 @@ export default class Scrollable { @@ -188,9 +172,6 @@ export default class Scrollable {
this.paddingBottomDiv = document.createElement('div');
this.paddingBottomDiv.classList.add('scroll-padding');
this.topObserver.observe(this.paddingTopDiv);
this.bottomObserver.observe(this.paddingBottomDiv);
this.container.addEventListener('scroll', this.onScroll.bind(this));
Array.from(el.children).forEach(c => this.container.append(c));
@ -280,181 +261,9 @@ export default class Scrollable { @@ -280,181 +261,9 @@ export default class Scrollable {
});
}
public detachByPrevScroll(child: Element, prevScrollTop: number, needHeight = 0) {
if(this.splitMeasureBottom) fastdom.clear(this.splitMeasureBottom);
if(this.splitMutateBottom) fastdom.clear(this.splitMutateBottom);
let attachToTop = this.paddings.up < prevScrollTop;
this.splitMeasureBottom = fastdom.measure(() => {
let sliced: {element: Element, height: number}[] = [];
do {
if(needHeight > 0) {
needHeight -= child.scrollHeight;
} else {
sliced.push({element: child, height: child.scrollHeight});
}
} while(child = child.nextElementSibling);
return sliced;
});
return this.splitMeasureBottom.then(sliced => {
if(this.splitMutateBottom) fastdom.clear(this.splitMutateBottom);
return this.splitMutateBottom = fastdom.mutate(() => {
sliced.forEachReverse((child) => {
let {element, height} = child;
if(!this.splitUp.contains(element)) return;
this.paddings.down += height;
this.hiddenElements.down.unshift(child);
this.splitUp.removeChild(element);
//element.parentElement.removeChild(element);
});
this.log('sliced down', sliced);
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
});
});
}
public splitObserve(entries: IntersectionObserverEntry[]) {
let sorted: {
intersecting: {
top?: IntersectionObserverEntry,
bottom?: IntersectionObserverEntry
},
notIntersecting: {
top?: IntersectionObserverEntry,
bottom?: IntersectionObserverEntry
}
} = {
intersecting: {},
notIntersecting: {}
};
for(let entry of entries) { // there may be duplicates (1st - not intersecting, 2nd - intersecting)
//console.log('onscroll entry 1', entry.target, entry.isIntersecting, entry);
if(!entry.target.parentElement || !entry.rootBounds) continue;
if(!entry.isIntersecting) {
let isTop = entry.boundingClientRect.top <= 0;
let isBottom = entry.rootBounds.height <= entry.boundingClientRect.top;
//console.log('onscroll entry notIntersecting', isTop, isBottom, entry.target, entry);
if(isTop) {
sorted.notIntersecting.top = entry;
} else if(isBottom && !sorted.notIntersecting.bottom) {
sorted.notIntersecting.bottom = entry;
}
//console.log('splitObserver', entry, entry.target, isTop);
} else {
let isTop = entry.boundingClientRect.top <= entry.rootBounds.top;
let isBottom = entry.boundingClientRect.bottom >= entry.rootBounds.bottom;
if(isTop) {
sorted.intersecting.top = entry;
} else if(isBottom && !sorted.intersecting.bottom) {
sorted.intersecting.bottom = entry;
}
// if(isTop) {
// this.onTopIntersection(entry.boundingClientRect.height);
// } else if(isBottom) {
// this.onTopIntersection(entry.boundingClientRect.height);
// }
}
}
console.log('splitObserve', entries, sorted);
let needHeight = this.splitOffset;
let isOutOfView: boolean;
let entry: IntersectionObserverEntry;
if(entry = sorted.notIntersecting.top) { // scrolled bottom
let child = entry.target;
let diff = entry.boundingClientRect.bottom + needHeight;
if(diff < 0) { // maybe need <=, means out of view
if(!(child = child.nextElementSibling)) {
this.detachTop(this.splitUp.lastElementChild, 0);
} else {
if(this.splitMeasureRemoveBad) fastdom.clear(this.splitMeasureRemoveBad);
this.splitMeasureRemoveBad = fastdom.measure(() => {
do {
diff += child.scrollHeight;
} while(diff < 0 && (child = child.nextElementSibling));
return child || this.splitUp.lastElementChild;
});
this.splitMeasureRemoveBad.then(child => {
this.detachTop(child, 0);
});
}
} else {
this.detachTop(child, needHeight);
}
}
if(entry = sorted.notIntersecting.bottom) { // scrolled top
isOutOfView = (entry.boundingClientRect.top - needHeight) >= entry.rootBounds.height;
this.detachBottom(entry.target, isOutOfView ? 0 : needHeight);
}
if(entry = sorted.intersecting.top) { // scrolling top
let needHeight = this.splitOffset;
let child = entry.target;
if(this.splitMeasureAdd) fastdom.clear(this.splitMeasureAdd);
this.splitMeasureAdd = fastdom.measure(() => {
while(child = child.previousElementSibling) {
needHeight -= child.scrollHeight;
}
return needHeight;
});
this.splitMeasureAdd.then(needHeight => {
this.onTopIntersection(needHeight);
});
}
if(entry = sorted.intersecting.bottom) { // scrolling bottom
let needHeight = this.splitOffset;
let child = entry.target;
if(this.splitMeasureAdd) fastdom.clear(this.splitMeasureAdd);
this.splitMeasureAdd = fastdom.measure(() => {
while(child = child.nextElementSibling) {
needHeight -= child.scrollHeight;
}
return needHeight;
});
this.splitMeasureAdd.then(needHeight => {
this.onBottomIntersection(needHeight);
});
}
}
public async resize() {
public resize() {
//console.time('scroll resize');
await fastdom.measure(() => {
// @ts-ignore
this.scrollSize = this.container[this.scrollType];
let rect = this.container.getBoundingClientRect();
// @ts-ignore
this.size = rect[this.type];
});
await fastdom.mutate(() => {
fastdom.mutate(() => {
if(!this.size || this.size == this.scrollSize) {
this.thumbSize = 0;
@ -480,7 +289,7 @@ export default class Scrollable { @@ -480,7 +289,7 @@ export default class Scrollable {
//console.log('onresize', thumb.style[type], thumbHeight, height);
}
public async setVirtualContainer(el?: HTMLElement) {
public setVirtualContainer(el?: HTMLElement) {
this.splitUp = el;
this.hiddenElements.up.length = this.hiddenElements.down.length = 0;
@ -494,12 +303,6 @@ export default class Scrollable { @@ -494,12 +303,6 @@ export default class Scrollable {
});
}
/* if(this.splitObserver) {
this.splitObserver.disconnect();
}
this.splitObserver = new IntersectionObserver((entries) => this.splitObserve(entries), {root: this.el}); */
this.log('setVirtualContainer:', el, this);
this.getScrollTopOffset();
@ -509,6 +312,9 @@ export default class Scrollable { @@ -509,6 +312,9 @@ export default class Scrollable {
el.parentElement.insertBefore(this.paddingTopDiv, el);
el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling);
});
} else {
this.paddingTopDiv.remove();
this.paddingBottomDiv.remove();
}
}
@ -529,33 +335,66 @@ export default class Scrollable { @@ -529,33 +335,66 @@ export default class Scrollable {
public onScroll() {
if(this.onScrollMeasure) fastdom.clear(this.onScrollMeasure);
this.onScrollMeasure = fastdom.measure(() => {
// @ts-ignore quick brown fix
this.size = this.parentElement[this.scrollType];
// @ts-ignore
if(this.container[this.scrollType] != this.scrollSize || this.thumbSize == 0) {
let scrollSize = this.container[this.scrollType];
if(scrollSize != this.scrollSize || this.thumbSize == 0) {
this.resize();
}
this.scrollSize = scrollSize;
// @ts-ignore
let value = this.container[this.scrollSide] / (this.scrollSize - this.size) * 100;
let maxValue = 100 - (this.thumbSize / this.size * 100);
let scrollPos = this.container[this.scrollSide];
// let value = scrollPos / (this.scrollSize - this.size) * 100;
// let maxValue = 100 - (this.thumbSize / this.size * 100);
let value = scrollPos / (this.scrollSize - this.size) * this.size;
let maxValue = this.size - this.thumbSize;
//this.log(scrollPos, this.scrollSize, this.size, value, scrollPos / (this.scrollSize - this.size) * this.size);
let ret = {value, maxValue};
let scrollTop = scrollPos - this.scrollTopOffset;
let maxScrollTop = this.scrollSize - this.scrollTopOffset - this.size;
if(this.onScrolledBottom) {
if(!this.hiddenElements.down.length && (maxScrollTop - scrollTop) <= this.onScrollOffset) {
if(!this.onScrolledBottomFired) {
this.onScrolledBottomFired = true;
this.onScrolledBottom();
}
} else {
this.onScrolledBottomFired = false;
}
}
if(this.onScrolledTop) {
//this.log('onScrolledTop:', scrollTop, this.onScrollOffset);
if(!this.hiddenElements.up.length && scrollTop <= this.onScrollOffset) {
if(!this.onScrolledTopFired) {
this.onScrolledTopFired = true;
this.onScrolledTop();
}
} else {
this.onScrolledTopFired = false;
}
}
if(!this.splitUp) {
return ret;
}
let perf = performance.now();
let scrollTop = this.scrollTop - this.scrollTopOffset;
let outerHeight = this.parentElement.scrollHeight;
let maxScrollTop = this.scrollHeight - this.scrollTopOffset - outerHeight;
if(scrollTop < 0) scrollTop = 0;
else if(scrollTop > maxScrollTop) scrollTop = maxScrollTop;
let toBottom = scrollTop > this.lastScrollTop;
let visibleFrom = /* scrollTop < this.paddings.up ? scrollTop : */scrollTop - this.paddings.up;
let visibleUntil = visibleFrom + outerHeight;
let visibleUntil = visibleFrom + this.size;
let sum = 0;
let firstVisibleElement: Element;
@ -619,10 +458,6 @@ export default class Scrollable { @@ -619,10 +458,6 @@ export default class Scrollable {
this.onBottomIntersection(needHeight);
return needHeight;
});
/* this.splitMeasureAdd.then(needHeight => {
this.onBottomIntersection(needHeight);
}); */
} else if(length) { // scrolled manually or safari
if(this.debug) {
this.log.warn('will detach all of top', length, this.splitUp.childElementCount, maxScrollTop, this.paddings, this.lastScrollTop);
@ -652,10 +487,6 @@ export default class Scrollable { @@ -652,10 +487,6 @@ export default class Scrollable {
this.onTopIntersection(needHeight);
return needHeight;
});
/* this.splitMeasureAdd.then(needHeight => {
this.onTopIntersection(needHeight);
}); */
} else if(length) { // scrolled manually or safari
if(this.debug) {
this.log.warn('will detach all of bottom', length, this.splitUp.childElementCount, maxScrollTop, this.paddings, this.lastScrollTop);
@ -681,7 +512,8 @@ export default class Scrollable { @@ -681,7 +512,8 @@ export default class Scrollable {
this.onScrollMeasure.then(({value, maxValue}) => {
fastdom.mutate(() => {
// @ts-ignore
this.thumb.style[this.side] = (value >= maxValue ? maxValue : value) + '%';
//this.thumb.style[this.side] = (value >= maxValue ? maxValue : value) + '%';
this.thumb.style.transform = this.translate + '(' + (value >= maxValue ? maxValue : value) + 'px)';
});
});
@ -691,7 +523,7 @@ export default class Scrollable { @@ -691,7 +523,7 @@ export default class Scrollable {
public onManualScrollTop(scrollTop: number, needHeight: number, maxScrollTop: number) {
//if(this.splitMutateRemoveBad) fastdom.clear(this.splitMutateRemoveBad);
this.splitMutateRemoveBad = fastdom.mutate(() => {
let h = maxScrollTop - (scrollTop + outerHeight);
let h = maxScrollTop - (scrollTop + this.size);
while(this.paddings.down < h && this.paddings.up) {
let child = this.hiddenElements.up.pop();
@ -705,11 +537,8 @@ export default class Scrollable { @@ -705,11 +537,8 @@ export default class Scrollable {
}
this.paddingBottomDiv.style.height = this.paddings.down + 'px';
this.onTopIntersection((outerHeight * 2) + (needHeight * 2));
this.onTopIntersection((this.size * 2) + (needHeight * 2));
});
/* this.splitMutateRemoveBad.then(() => {
}); */
}
public onManualScrollBottom(scrollTop: number, needHeight: number) {
@ -729,11 +558,8 @@ export default class Scrollable { @@ -729,11 +558,8 @@ export default class Scrollable {
}
this.paddingTopDiv.style.height = this.paddings.up + 'px';
this.onBottomIntersection(outerHeight + (needHeight * 2));
this.onBottomIntersection(this.size + (needHeight * 2));
});
/* this.splitMutateRemoveBad.then(() => {
}); */
}
public onTopIntersection(needHeight: number) {
@ -813,7 +639,7 @@ export default class Scrollable { @@ -813,7 +639,7 @@ export default class Scrollable {
});
if(this.hiddenElements.up.length) {
fastdom.mutate(() => {
/* fastdom.mutate(() => {
this.splitUp.append(...smth);
}).then(() => {
return fastdom.measure(() => {
@ -839,13 +665,26 @@ export default class Scrollable { @@ -839,13 +665,26 @@ export default class Scrollable {
this.onScroll();
});
}); */
this.splitUp.prepend(...smth);
smth.forEachReverse(node => {
let height = node.scrollHeight;
this.log('will append element to up hidden', node, height);
this.paddings.up += height;
this.hiddenElements.up.unshift({
element: node,
height: height
});
node.parentElement.removeChild(node);
});
this.paddingTopDiv.style.height = this.paddings.up + 'px';
this.onScroll();
} else {
this.splitUp.prepend(...smth);
this.onScroll();
}
} else {
this.container.prepend(...smth);
this.appendTo.prepend(...smth);
this.onScroll();
}
@ -891,7 +730,7 @@ export default class Scrollable { @@ -891,7 +730,7 @@ export default class Scrollable {
this.onScroll();
}
} else {
this.container.append(...smth);
this.appendTo.append(...smth);
this.onScroll();
}
@ -914,17 +753,14 @@ export default class Scrollable { @@ -914,17 +753,14 @@ export default class Scrollable {
}
}
let index = this.hiddenElements.up.findIndex(c => c.element == element);
let child: {element: Element, height: number};
let child = this.hiddenElements.up.findAndSplice(c => c.element == element);
let foundUp = false;
if(index !== -1) {
child = this.hiddenElements.up.splice(index, 1)[0];
if(child) {
this.paddings.up -= child.height;
foundUp = true;
} else {
index = this.hiddenElements.down.findIndex(c => c.element == element);
if(index !== -1) {
child = this.hiddenElements.down.splice(index, 1)[0];
child = this.hiddenElements.down.findAndSplice(c => c.element == element);
if(child) {
this.paddings.down -= child.height;
}
}
@ -943,7 +779,7 @@ export default class Scrollable { @@ -943,7 +779,7 @@ export default class Scrollable {
}
public insertBefore(newChild: Element, refChild: Element, height?: number) {
this.log('insertBefore', newChild, refChild);
//this.log('insertBefore', newChild, refChild);
return;
if(this.splitUp) {
@ -1023,6 +859,29 @@ export default class Scrollable { @@ -1023,6 +859,29 @@ export default class Scrollable {
return ret;
}
public scrollIntoView(element: Element) {
if(element.parentElement) {
element.scrollIntoView();
} else if(this.splitUp) {
let index = this.hiddenElements.up.findIndex(e => e.element == element);
let y = 0;
if(index !== -1) {
for(let i = 0; i < index; ++i) {
y += this.hiddenElements.up[i].height;
}
this.scrollTop = y;
} else if((index = this.hiddenElements.down.findIndex(e => e.element == element)) !== -1) {
y += this.paddings.up + this.size;
for(let i = 0; i < index; ++i) {
y += this.hiddenElements.down[i].height;
}
this.scrollTop = y;
}
}
}
set scrollTop(y: number) {
fastdom.mutate(() => {
this.container.scrollTop = y;

61
src/components/wrappers.ts

@ -259,15 +259,21 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { @@ -259,15 +259,21 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.classList.add('audio-waveform');
svg.setAttributeNS(null, 'width', '250');
svg.setAttributeNS(null, 'width', '190');
svg.setAttributeNS(null, 'height', '23');
svg.setAttributeNS(null, 'viewBox', '0 0 250 23');
svg.setAttributeNS(null, 'viewBox', '0 0 190 23');
div.insertBefore(svg, div.lastElementChild);
let wave = doc.attributes[0].waveform as Uint8Array;
let index = 0;
let skipped = 0;
for(let uint8 of wave) {
if (index > 0 && index % 4 == 0) {
++index;
++skipped;
continue;
}
let percents = uint8 / 255;
let height = 23 * percents;
@ -276,12 +282,14 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { @@ -276,12 +282,14 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
}
svg.insertAdjacentHTML('beforeend', `
<rect x="${index * 4}" y="${23 - height}" width="2" height="${height}" rx="1" ry="1"></rect>
<rect x="${(index - skipped) * 4}" y="${23 - height}" width="2" height="${height}" rx="1" ry="1"></rect>
`);
++index;
}
let progress = div.querySelector('.audio-waveform') as HTMLDivElement;
let onClick = () => {
if(!promise) {
if(downloadDiv.classList.contains('downloading')) {
@ -310,6 +318,7 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { @@ -310,6 +318,7 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
let toggle = div.querySelector('.audio-toggle') as HTMLDivElement;
let interval = 0;
let lastIndex = 0;
toggle.addEventListener('click', () => {
if(audio.paused) {
@ -327,7 +336,6 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { @@ -327,7 +336,6 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
(Array.from(svg.children) as HTMLElement[]).forEach(node => node.classList.remove('active'));
let lastIndex = 0;
interval = setInterval(() => {
if(lastIndex > svg.childElementCount || isNaN(audio.duration)) {
clearInterval(interval);
@ -337,10 +345,11 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { @@ -337,10 +345,11 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
// @ts-ignore
timeDiv.innerText = String(audio.currentTime | 0).toHHMMSS(true);
lastIndex = Math.round(audio.currentTime / audio.duration * 62);
lastIndex = Math.round(audio.currentTime / audio.duration * 47);
//svg.children[lastIndex].setAttributeNS(null, 'fill', '#000');
svg.children[lastIndex].classList.add('active');
//svg.children[lastIndex].classList.add('active'); #Иногда пропускает полоски..
(Array.from(svg.children) as HTMLElement[]).slice(0,lastIndex+1).forEach(node => node.classList.add('active'));
//++lastIndex;
//console.log('lastIndex:', lastIndex, audio.currentTime);
//}, duration * 1000 / svg.childElementCount | 0/* 63 * duration / 10 */);
@ -358,11 +367,51 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement { @@ -358,11 +367,51 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
toggle.classList.add('tgico-largeplay');
toggle.classList.remove('tgico-largepause');
clearInterval(interval);
(Array.from(svg.children) as HTMLElement[]).forEach(node => node.classList.remove('active'));
// @ts-ignore
timeDiv.innerText = String(audio.currentTime | 0).toHHMMSS(true);
});
let mousedown = false, mousemove = false;
progress.addEventListener('mouseleave', (e) => {
if(mousedown) {
audio.play();
mousedown = false;
}
mousemove = false;
})
progress.addEventListener('mousemove', (e) => {
mousemove = true;
if(mousedown) scrub(e, audio, progress);
});
progress.addEventListener('mousedown', (e) => {
e.preventDefault();
if(!audio.paused) {
audio.pause();
scrub(e, audio, progress);
mousedown = true;
}
});
progress.addEventListener('mouseup', (e) => {
if (mousemove && mousedown) {
audio.play();
mousedown = false;
}
});
progress.addEventListener('click', (e) => {
if(!audio.paused) scrub(e, audio, progress);
});
function scrub(e: MouseEvent, audio: HTMLAudioElement, progress: HTMLDivElement) {
let scrubTime = e.offsetX / 190 /* width */ * audio.duration;
(Array.from(svg.children) as HTMLElement[]).forEach(node => node.classList.remove('active'));
lastIndex = Math.round(scrubTime / audio.duration * 47);
(Array.from(svg.children) as HTMLElement[]).slice(0,lastIndex+1).forEach(node => node.classList.add('active'));
audio.currentTime = scrubTime;
}
audio.append(source);
audio.style.display = 'none';
div.append(audio);

591
src/lib/appManagers/appImManager.ts

@ -27,21 +27,35 @@ console.log('appImManager included!'); @@ -27,21 +27,35 @@ console.log('appImManager included!');
let testScroll = false;
class ScrollPosition {
public previousScrollHeightMinusTop = 0;
public readyFor = 'up';
public container: HTMLElement;
previousScrollHeightMinusTop = 0;
readyFor = 'up';
container: HTMLElement;
rAF: number;
debug = true;
constructor(public node: HTMLElement) {
constructor(node: HTMLElement) {
this.container = node.parentElement;
}
public restore() {
//console.log('scrollPosition restore 2', this.node.scrollHeight, (this.node.scrollHeight
//- this.previousScrollHeightMinusTop) + 'px', this.container);
restore() {
let setScrollTop = this.container.scrollHeight - this.previousScrollHeightMinusTop;
if(this.debug) appImManager.log('scrollPosition restore', this.readyFor, this.container.scrollHeight,
setScrollTop, this.container, this.container.parentElement.classList.contains('scrolled-down'));
if(this.readyFor === 'up' || appImManager.scroll.parentElement.classList.contains('scrolled-down')) {
this.container.scrollTop = this.node.scrollHeight
- this.previousScrollHeightMinusTop;
if(this.readyFor === 'up'/* || this.container.parentElement.classList.contains('scrolled-down') */) {
if(this.debug) appImManager.log('scrollPosition restore 2', this.readyFor, this.container.scrollHeight,
setScrollTop, this.container);
if(this.rAF) window.cancelAnimationFrame(this.rAF);
this.rAF = window.requestAnimationFrame(() => {
this.container.scrollTop = this.container.scrollHeight - this.previousScrollHeightMinusTop;
this.rAF = 0;
});
} else if(this.container.parentElement.classList.contains('scrolled-down')) {
if(this.debug) appImManager.log('scrollPosition restore 2', this.readyFor, this.container.scrollHeight,
setScrollTop, this.container);
this.container.scrollTop = setScrollTop;
}
// 'down' doesn't need to be special cased unless the
@ -50,12 +64,130 @@ class ScrollPosition { @@ -50,12 +64,130 @@ class ScrollPosition {
// a Facebook messages effect
}
public prepareFor(direction: string) {
this.readyFor = direction || 'up';
this.previousScrollHeightMinusTop = this.node.scrollHeight
- this.container.scrollTop;
prepareFor(direction = 'up') {
if(this.rAF) {
window.cancelAnimationFrame(this.rAF);
this.rAF = 0;
}
this.readyFor = direction;
if(direction == 'down') {
let scrollTop = this.container.scrollTop;
this.previousScrollHeightMinusTop = scrollTop > 0 ? this.container.scrollHeight - scrollTop : 0;
} else {
this.previousScrollHeightMinusTop = this.container.scrollHeight - this.container.scrollTop;
}
//let scrollTop = this.container.scrollTop;
//this.previousScrollHeightMinusTop = scrollTop > 0 || this.readyFor == 'up' ? this.container.scrollHeight - this.container.scrollTop : 0;
if(this.debug) appImManager.log.trace('scrollPosition prepareFor', direction, this.container.scrollHeight,
this.container.scrollTop, this.previousScrollHeightMinusTop);
}
}
class BubbleGroups {
bubblesByGroups: Array<{timestamp: number, fromID: number, mid: number, group: HTMLDivElement[]}> = []; // map to group
groups: Array<HTMLDivElement[]> = [];
updateRAFs: Map<HTMLDivElement[], number> = new Map();
newGroupDiff = 120;
removeBubble(bubble: HTMLDivElement, mid: number) {
let details = this.bubblesByGroups.findAndSplice(g => g.mid == mid);
if(details && details.group.length) {
details.group.findAndSplice(d => d == bubble);
if(!details.group.length) {
this.groups.findAndSplice(g => g == details.group);
}
}
}
addBubble(bubble: HTMLDivElement, message: any, reverse: boolean) {
let timestamp = message.date;
let fromID = message.fromID;
let group: HTMLDivElement[];
// try to find added
//this.removeBubble(message.mid);
if(this.bubblesByGroups.length) {
if(reverse) {
let g = this.bubblesByGroups[0];
if(g.fromID == fromID && (g.timestamp - timestamp) < this.newGroupDiff) {
group = g.group;
group.unshift(bubble);
} else {
this.groups.unshift(group = [bubble]);
}
} else {
let g = this.bubblesByGroups[this.bubblesByGroups.length - 1];
if(g.fromID == fromID && (timestamp - g.timestamp) < this.newGroupDiff) {
group = g.group;
group.push(bubble);
} else {
this.groups.push(group = [bubble]);
}
}
} else {
this.groups.push(group = [bubble]);
}
console.log('addBubble', bubble, message.mid, fromID, reverse, group);
this.bubblesByGroups[reverse ? 'unshift' : 'push']({timestamp, fromID, mid: message.mid, group});
this.updateGroup(group);
}
updateGroup(group: HTMLDivElement[]) {
if(this.updateRAFs.has(group)) {
window.cancelAnimationFrame(this.updateRAFs.get(group));
this.updateRAFs.delete(group);
}
this.updateRAFs.set(group, window.requestAnimationFrame(() => {
this.updateRAFs.delete(group);
if(!group.length) {
return;
}
let first = group[0];
console.log('updateGroup', group, first);
if(group.length == 1) {
first.classList.add('is-group-first', 'is-group-last');
return;
} else {
first.classList.remove('is-group-last');
first.classList.add('is-group-first');
}
let length = group.length - 1;
for(let i = 1; i < length; ++i) {
let bubble = group[i];
bubble.classList.remove('is-group-last', 'is-group-first');
}
let last = group[group.length - 1];
last.classList.remove('is-group-first');
last.classList.add('is-group-last');
}));
}
//console.log('scrollPosition prepareFor', direction, this.node.scrollHeight, this.previousScrollHeightMinusTop + 'px')
updateGroupByMessageID(mid: number) {
let details = this.bubblesByGroups.find(g => g.mid == mid);
if(details) {
this.updateGroup(details.group);
}
}
cleanup() {
this.bubblesByGroups = [];
for(let value of this.updateRAFs.values()) {
window.cancelAnimationFrame(value);
}
this.updateRAFs.clear();
}
}
@ -128,6 +260,8 @@ export class AppImManager { @@ -128,6 +260,8 @@ export class AppImManager {
private setPeerPromise: Promise<boolean> = null;
public bubbleGroups = new BubbleGroups();
constructor() {
this.log = logger('IM');
@ -152,12 +286,14 @@ export class AppImManager { @@ -152,12 +286,14 @@ export class AppImManager {
this.myID = userAuth ? userAuth.id : 0;
});
// will call when message is sent (only 1)
$rootScope.$on('history_append', (e: CustomEvent) => {
let details = e.detail;
this.renderMessagesByIDs([details.messageID]);
});
// will call when sent for update pos
$rootScope.$on('history_update', (e: CustomEvent) => {
let details = e.detail;
@ -169,6 +305,7 @@ export class AppImManager { @@ -169,6 +305,7 @@ export class AppImManager {
let message = appMessagesManager.getMessage(mid);
//this.log('history_update', this.bubbles[mid], mid, message);
this.renderMessage(message, false, false, bubble);
this.deleteEmptySideDivs();
@ -219,6 +356,8 @@ export class AppImManager { @@ -219,6 +356,8 @@ export class AppImManager {
bubble.classList.remove('is-sending');
bubble.classList.add('is-sent');
this.bubbleGroups.removeBubble(bubble, tempID);
delete this.bubbles[tempID];
} else {
this.log.warn('message_sent there is no bubble', e.detail);
@ -363,9 +502,7 @@ export class AppImManager { @@ -363,9 +502,7 @@ export class AppImManager {
let ids = Object.keys(this.bubbles).map(k => +k).filter(id => {
let message = appMessagesManager.getMessage(id);
return message.media && (message.media.photo
|| (message.media.document && (message.media.document.type == 'video' || message.media.document.type == 'gif'))
|| (message.media.webpage && (message.media.webpage.document || message.media.webpage.photo)));
return message.media && (message.media.photo || (message.media.document && (message.media.document.type == 'video' || message.media.document.type == 'gif')) || (message.media.webpage && (message.media.webpage.document || message.media.webpage.photo)));
}).sort();
let idx = ids.findIndex(i => i == messageID);
@ -458,8 +595,7 @@ export class AppImManager { @@ -458,8 +595,7 @@ export class AppImManager {
if(!msgID) return;
if(this.myID == this.peerID ||
(this.peerID < 0 && !appPeersManager.isChannel(this.peerID) && !appPeersManager.isMegagroup(this.peerID))) {
if(this.myID == this.peerID || (this.peerID < 0 && !appPeersManager.isChannel(this.peerID) && !appPeersManager.isMegagroup(this.peerID))) {
this.contextMenuPin.style.display = '';
} else this.contextMenuPin.style.display = 'none';
@ -646,40 +782,8 @@ export class AppImManager { @@ -646,40 +782,8 @@ export class AppImManager {
this.loadMediaQueueProcess();
}
public async loadMediaQueueProcessOld(): Promise<void[]> {
if(this.loadMediaQueuePromise /* || 1 == 1 */) return this.loadMediaQueuePromise;
let woo = this.loadMediaQueue.splice(-5, 5).reverse().map(f => f());
if(woo.length) {
///this.log('Will load more media:', woo.length);
woo.forEach(async(promise) => {
try {
await promise;
} catch(err) {
this.log.error('loadMediaQueue error:', err);
}
this.loadingMedia--;
});
try {
this.loadMediaQueuePromise = Promise.all(woo);
await this.loadMediaQueuePromise;
} catch(err) {
this.log.error('loadMediaQueue error:', err);
}
}
this.loadMediaQueuePromise = null;
if(this.loadMediaQueue.length) return this.loadMediaQueueProcess();
return this.loadMediaQueuePromise;
}
public async loadMediaQueueProcess(): Promise<void[]> {
if(this.loadingMedia >= 5) return;
if(this.loadingMedia >= 5/* || 1 == 1 */) return;
let item = this.loadMediaQueue.pop();
if(item) {
@ -709,6 +813,58 @@ export class AppImManager { @@ -709,6 +813,58 @@ export class AppImManager {
return apiManager.invokeApi('account.updateStatus', {offline: this.offline});
}
public loadMoreHistory(top: boolean) {
// load more history
// возможно нужно добавить разные таймауты для верха и низа
if(!this.getHistoryPromise && !this.getHistoryTimeout && this.peerID && !testScroll) {
this.getHistoryTimeout = setTimeout(() => { // must be
let history = Object.keys(this.bubbles).map(id => +id).sort();
/* let history = appMessagesManager.historiesStorage[this.peerID].history;
let length = history.length; */
// filter negative ids
let lastBadIndex = -1;
for(let i = 0; i < history.length; ++i) {
if(history[i] <= 0) lastBadIndex = i;
else break;
}
if(lastBadIndex != -1) {
history = history.slice(lastBadIndex + 1);
}
this.getHistoryTimeout = 0;
if(!this.scrolledAll && top) {
this.log('Will load more (up) history by id:', history[0], 'maxID:', history[history.length - 1], history);
/* false && */!testScroll && this.getHistory(history[0], true).then(() => { // uncomment
this.onScroll();
}).catch(err => {
this.log.warn('Could not load more history, err:', err);
});
}
if(this.scrolledAllDown) return;
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
/* if(!dialog) {
this.log.warn('no dialog for load history');
return;
} */
// if scroll down after search
if(!top && (!dialog || history.indexOf(dialog.top_message) === -1)) {
this.log('Will load more (down) history by maxID:', history[history.length - 1], history);
/* false && */!testScroll && this.getHistory(history[history.length - 1], false, true).then(() => { // uncomment
this.onScroll();
}).catch(err => {
this.log.warn('Could not load more history, err:', err);
});
}
}, 0);
}
}
public onScroll() {
let readed: number[] = [];
@ -744,92 +900,15 @@ export class AppImManager { @@ -744,92 +900,15 @@ export class AppImManager {
} else if(this.scroll.parentElement.classList.contains('scrolled-down')) {
this.scroll.parentElement.classList.remove('scrolled-down');
}
// load more history
if(!this.getHistoryPromise && !this.getHistoryTimeout && !testScroll) {
this.getHistoryTimeout = setTimeout(() => { // must be
let history = Object.keys(this.bubbles).map(id => +id).sort();
/* let history = appMessagesManager.historiesStorage[this.peerID].history;
let length = history.length; */
// filter negative ids
let lastBadIndex = -1;
for(let i = 0; i < history.length; ++i) {
if(history[i] <= 0) lastBadIndex = i;
else break;
}
if(lastBadIndex != -1) {
history = history.slice(lastBadIndex + 1);
}
this.getHistoryTimeout = 0;
let willLoad = false;
if(!this.scrolledAll) {
let length = history.length < 10 ? history.length : 10;
for(let i = 0; i < length; ++i) {
let msgID = history[i];
let bubble = this.bubbles[msgID];
if(!bubble) {
this.log.error('no bubble by msgID:', msgID);
continue;
}
if(isElementInViewport(bubble)) {
willLoad = true;
////this.log('Will load more (up) history by id:', history[0], 'maxID:', history[history.length - 1], history, bubble);
/* false && */!testScroll && this.getHistory(history[0], true).then(() => { // uncomment
this.onScroll();
}).catch(err => {
this.log.warn('Could not load more history, err:', err);
});
break;
}
}
}
if(this.scrolledAllDown) return;
let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0];
/* if(!dialog) {
this.log.warn('no dialog for load history');
return;
} */
// if scroll down after search
if(!willLoad && (!dialog || history.indexOf(dialog.top_message) === -1)) {
let lastMsgIDs = history.slice(-10);
for(let msgID of lastMsgIDs) {
let bubble = this.bubbles[msgID];
if(isElementInViewport(bubble)) {
willLoad = true;
////this.log('Will load more (down) history by maxID:', lastMsgIDs[lastMsgIDs.length - 1], lastMsgIDs, bubble);
/* false && */!testScroll && this.getHistory(lastMsgIDs[lastMsgIDs.length - 1], false, true).then(() => { // uncomment
this.onScroll();
}).catch(err => {
this.log.warn('Could not load more history, err:', err);
});
break;
}
}
}
}, 0);
}
}
public setScroll() {
this.scrollable = new Scrollable(this.bubblesContainer, false, true, 750, 'IM'/* 1500 */);
this.scrollable = new Scrollable(this.bubblesContainer, false, true, 750, 'IM', this.chatInner/* 1500 */, 300);
this.scroll = this.scrollable.container;
this.scrollable.setVirtualContainer(this.chatInner);
this.scrollable.onScrolledTop = () => this.loadMoreHistory(true);
this.scrollable.onScrolledBottom = () => this.loadMoreHistory(false);
this.scrollPosition = new ScrollPosition(this.chatInner);
this.scroll.addEventListener('scroll', this.onScroll.bind(this));
@ -886,33 +965,33 @@ export class AppImManager { @@ -886,33 +965,33 @@ export class AppImManager {
let subtitle = '';
switch(user.status._) {
case 'userStatusRecently':
subtitle += 'last seen recently';
break;
subtitle += 'last seen recently';
break;
case 'userStatusOffline':
subtitle = 'last seen ';
subtitle = 'last seen ';
let date = user.status.was_online;
let now = Date.now() / 1000;
let date = user.status.was_online;
let now = Date.now() / 1000;
if((now - date) < 60) {
subtitle += ' just now';
} else if((now - date) < 3600) {
subtitle += ((now - date) / 60 | 0) + ' minutes ago';
} else if(now - date < 86400) {
subtitle += ((now - date) / 3600 | 0) + ' hours ago';
} else {
let d = new Date(date * 1000);
subtitle += ('0' + d.getDate()).slice(-2) + '.' + ('0' + (d.getMonth() + 1)).slice(-2) + ' at ' +
('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2);
}
if((now - date) < 60) {
subtitle += ' just now';
} else if((now - date) < 3600) {
subtitle += ((now - date) / 60 | 0) + ' minutes ago';
} else if(now - date < 86400) {
subtitle += ((now - date) / 3600 | 0) + ' hours ago';
} else {
let d = new Date(date * 1000);
subtitle += ('0' + d.getDate()).slice(-2) + '.' + ('0' + (d.getMonth() + 1)).slice(-2) + ' at ' +
('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2);
}
break;
break;
case 'userStatusOnline':
this.subtitleEl.classList.add('online');
appSidebarRight.profileElements.subtitle.classList.add('online');
subtitle = 'online';
break;
this.subtitleEl.classList.add('online');
appSidebarRight.profileElements.subtitle.classList.add('online');
subtitle = 'online';
break;
}
appSidebarRight.profileElements.subtitle.innerText = subtitle;
@ -937,6 +1016,7 @@ export class AppImManager { @@ -937,6 +1016,7 @@ export class AppImManager {
}
this.bubbles = {};
this.dateMessages = {};
this.bubbleGroups.cleanup();
this.unreaded = [];
this.unreadOut = [];
this.loadMediaQueue = [];
@ -984,7 +1064,8 @@ export class AppImManager { @@ -984,7 +1064,8 @@ export class AppImManager {
if(dialog && lastMsgID == dialog.top_message) {
this.scroll.scrollTop = this.scroll.scrollHeight;
} else {
this.bubbles[lastMsgID].scrollIntoView();
//this.bubbles[lastMsgID].scrollIntoView();
this.scrollable.scrollIntoView(this.bubbles[lastMsgID]);
}
return Promise.resolve(true);
@ -1038,7 +1119,7 @@ export class AppImManager { @@ -1038,7 +1119,7 @@ export class AppImManager {
this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = title;
this.topbar.style.display = this.goDownBtn.style.display = '';
//appSidebarRight.toggleSidebar(true);
appSidebarRight.toggleSidebar(true);
this.chatInput.style.display = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID) ? 'none' : '';
@ -1048,6 +1129,8 @@ export class AppImManager { @@ -1048,6 +1129,8 @@ export class AppImManager {
this.chatInner.classList.remove('is-chat');
}
//this.scroll.scrollTop = this.scroll.scrollHeight;
return this.setPeerPromise = Promise.all([
this.getHistory(forwarding ? lastMsgID + 1 : lastMsgID).then(() => {
////this.log('setPeer removing preloader');
@ -1068,18 +1151,18 @@ export class AppImManager { @@ -1068,18 +1151,18 @@ export class AppImManager {
this.scroll.scrollTop = this.scroll.scrollHeight;
}
} else if(dialog && dialog.top_message) { // add last message, bc in getHistory will load < max_id
this.renderMessage(appMessagesManager.getMessage(dialog.top_message));
this.renderMessage(appMessagesManager.getMessage(dialog.top_message), false, true);
//this.scroll.scrollTop = this.scroll.scrollHeight;
}
if(this.scroll) {
this.onScroll();
}
this.onScroll();
this.scrollable.onScroll();
this.preloader.detach();
//setTimeout(() => {
//appSidebarRight.fillProfileElements();
appSidebarRight.loadSidebarMedia();
//appSidebarRight.fillProfileElements();
appSidebarRight.loadSidebarMedia(true);
//}, 500);
return true;
@ -1123,7 +1206,6 @@ export class AppImManager { @@ -1123,7 +1206,6 @@ export class AppImManager {
///////this.log('updateUnreadByDialog', maxID, dialog, this.unreadOut);
let length = this.unreadOut.length;
this.unreadOut.forEachReverse((msgID, idx) => {
if(msgID > 0 && msgID <= maxID) {
let bubble = this.bubbles[msgID];
@ -1148,13 +1230,9 @@ export class AppImManager { @@ -1148,13 +1230,9 @@ export class AppImManager {
if(!(id in this.bubbles)) return;
let bubble = this.bubbles[id];
let parent = bubble.parentNode as HTMLDivElement;
delete this.bubbles[id];
bubble.remove();
if(!parent.childNodes.length) {
parent.remove();
}
this.scrollable.removeElement(bubble);
//bubble.remove();
});
lottieLoader.checkAnimations();
@ -1210,11 +1288,36 @@ export class AppImManager { @@ -1210,11 +1288,36 @@ export class AppImManager {
//bubble.innerHTML = '';
}
if(message._ == 'messageService') {
bubble.className = 'bubble service';
let action = message.action;
let title = appPeersManager.getPeerTitle(message.fromID);
let name = document.createElement('div');
name.classList.add('name');
name.dataset.peerID = message.fromID;
name.innerHTML = title;
let _ = action._;
if(_ == "messageActionPhoneCall") {
_ += '.' + action.type;
}
// @ts-ignore
let str = (name.innerText ? name.outerHTML + ' ' : '') + langPack[_];
bubbleContainer.innerHTML = `<div class="service-msg">${str}</div>`;
if(!multipleRender) {
this.scrollPosition.restore(); // лагает из-за этого
}
return;
}
// time section
let date = new Date(message.date * 1000);
let time = ('0' + date.getHours()).slice(-2) +
':' + ('0' + date.getMinutes()).slice(-2);
let time = ('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2);
if(message.views) {
bubble.classList.add('channel-post');
@ -1382,8 +1485,8 @@ export class AppImManager { @@ -1382,8 +1485,8 @@ export class AppImManager {
if(doc.type == 'gif' || doc.type == 'video') {
//if(doc.size <= 20e6) {
bubble.classList.add('video');
wrapVideo.call(this, doc, preview, message);
bubble.classList.add('video');
wrapVideo.call(this, doc, preview, message);
//}
} else {
doc = null;
@ -1461,7 +1564,7 @@ export class AppImManager { @@ -1461,7 +1564,7 @@ export class AppImManager {
break;
} else if(doc.mime_type == 'video/mp4' && doc.size <= 20e6) {
////////this.log('never get free 2', doc);
this.log('never get free 2', doc);
if(doc.type == 'round') {
bubble.classList.add('round');
@ -1470,6 +1573,16 @@ export class AppImManager { @@ -1470,6 +1573,16 @@ export class AppImManager {
bubble.classList.add('video');
wrapVideo.call(this, doc, attachmentDiv, message, true, null, false, doc.type == 'round');
break;
} else if(doc.mime_type == 'audio/ogg') {
let docDiv = wrapDocument(doc);
messageDiv.classList.remove('message-empty');
bubble.classList.add('bubble-audio');
messageDiv.append(docDiv);
processingWebPage = true;
break;
} else {
let docDiv = wrapDocument(doc);
@ -1483,11 +1596,11 @@ export class AppImManager { @@ -1483,11 +1596,11 @@ export class AppImManager {
}
default:
messageDiv.classList.remove('message-empty');
messageDiv.innerHTML = 'unrecognized media type: ' + message.media._;
messageDiv.append(timeSpan);
this.log.warn('unrecognized media type:', message.media._, message);
break;
messageDiv.classList.remove('message-empty');
messageDiv.innerHTML = 'unrecognized media type: ' + message.media._;
messageDiv.append(timeSpan);
this.log.warn('unrecognized media type:', message.media._, message);
break;
}
if(!processingWebPage) {
@ -1513,13 +1626,8 @@ export class AppImManager { @@ -1513,13 +1626,8 @@ export class AppImManager {
if(message.savedFrom) {
let fwd = document.createElement('div');
fwd.classList.add('forward'/* , 'tgico-forward' */);
fwd.innerHTML = `
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
<defs>
<path d="M13.55 3.24L13.64 3.25L13.73 3.27L13.81 3.29L13.9 3.32L13.98 3.35L14.06 3.39L14.14 3.43L14.22 3.48L14.29 3.53L14.36 3.59L14.43 3.64L22.23 10.85L22.36 10.99L22.48 11.15L22.57 11.31L22.64 11.48L22.69 11.66L22.72 11.85L22.73 12.04L22.71 12.22L22.67 12.41L22.61 12.59L22.53 12.76L22.42 12.93L22.29 13.09L22.23 13.15L14.43 20.36L14.28 20.48L14.12 20.58L13.95 20.66L13.77 20.72L13.58 20.76L13.4 20.77L13.22 20.76L13.03 20.73L12.85 20.68L12.68 20.61L12.52 20.52L12.36 20.4L12.22 20.27L12.16 20.2L12.1 20.13L12.05 20.05L12.01 19.98L11.96 19.9L11.93 19.82L11.89 19.73L11.87 19.65L11.84 19.56L11.83 19.47L11.81 19.39L11.81 19.3L11.8 19.2L11.8 16.42L11 16.49L10.23 16.58L9.51 16.71L8.82 16.88L8.18 17.09L7.57 17.33L7.01 17.6L6.48 17.91L5.99 18.26L5.55 18.64L5.14 19.05L4.77 19.51L4.43 19.99L4.29 20.23L4.21 20.35L4.11 20.47L4 20.57L3.88 20.65L3.75 20.72L3.62 20.78L3.48 20.82L3.33 20.84L3.19 20.84L3.04 20.83L2.9 20.79L2.75 20.74L2.62 20.68L2.53 20.62L2.45 20.56L2.38 20.5L2.31 20.43L2.25 20.36L2.2 20.28L2.15 20.19L2.11 20.11L2.07 20.02L2.04 19.92L2.02 19.83L2.01 19.73L2 19.63L2.04 17.99L2.19 16.46L2.46 15.05L2.85 13.75L3.35 12.58L3.97 11.53L4.7 10.6L5.55 9.8L6.51 9.12L7.59 8.56L8.77 8.13L10.07 7.83L11.48 7.65L11.8 7.63L11.8 4.8L11.91 4.56L12.02 4.35L12.14 4.16L12.25 3.98L12.37 3.82L12.48 3.68L12.61 3.56L12.73 3.46L12.85 3.38L12.98 3.31L13.11 3.27L13.24 3.24L13.37 3.23L13.46 3.23L13.55 3.24Z" id="b13RmHDQtl"></path>
</defs>
<use xlink:href="#b13RmHDQtl" opacity="1" fill="#fff" fill-opacity="1"></use>
</svg>`;
fwd.innerHTML = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
<defs><path d="M13.55 3.24L13.64 3.25L13.73 3.27L13.81 3.29L13.9 3.32L13.98 3.35L14.06 3.39L14.14 3.43L14.22 3.48L14.29 3.53L14.36 3.59L14.43 3.64L22.23 10.85L22.36 10.99L22.48 11.15L22.57 11.31L22.64 11.48L22.69 11.66L22.72 11.85L22.73 12.04L22.71 12.22L22.67 12.41L22.61 12.59L22.53 12.76L22.42 12.93L22.29 13.09L22.23 13.15L14.43 20.36L14.28 20.48L14.12 20.58L13.95 20.66L13.77 20.72L13.58 20.76L13.4 20.77L13.22 20.76L13.03 20.73L12.85 20.68L12.68 20.61L12.52 20.52L12.36 20.4L12.22 20.27L12.16 20.2L12.1 20.13L12.05 20.05L12.01 19.98L11.96 19.9L11.93 19.82L11.89 19.73L11.87 19.65L11.84 19.56L11.83 19.47L11.81 19.39L11.81 19.3L11.8 19.2L11.8 16.42L11 16.49L10.23 16.58L9.51 16.71L8.82 16.88L8.18 17.09L7.57 17.33L7.01 17.6L6.48 17.91L5.99 18.26L5.55 18.64L5.14 19.05L4.77 19.51L4.43 19.99L4.29 20.23L4.21 20.35L4.11 20.47L4 20.57L3.88 20.65L3.75 20.72L3.62 20.78L3.48 20.82L3.33 20.84L3.19 20.84L3.04 20.83L2.9 20.79L2.75 20.74L2.62 20.68L2.53 20.62L2.45 20.56L2.38 20.5L2.31 20.43L2.25 20.36L2.2 20.28L2.15 20.19L2.11 20.11L2.07 20.02L2.04 19.92L2.02 19.83L2.01 19.73L2 19.63L2.04 17.99L2.19 16.46L2.46 15.05L2.85 13.75L3.35 12.58L3.97 11.53L4.7 10.6L5.55 9.8L6.51 9.12L7.59 8.56L8.77 8.13L10.07 7.83L11.48 7.65L11.8 7.63L11.8 4.8L11.91 4.56L12.02 4.35L12.14 4.16L12.25 3.98L12.37 3.82L12.48 3.68L12.61 3.56L12.73 3.46L12.85 3.38L12.98 3.31L13.11 3.27L13.24 3.24L13.37 3.23L13.46 3.23L13.55 3.24Z" id="b13RmHDQtl"></path></defs><use xlink:href="#b13RmHDQtl" opacity="1" fill="#fff" fill-opacity="1"></use></svg>`;
bubbleContainer.append(fwd);
bubble.dataset.savedFrom = message.savedFrom;
}
@ -1568,17 +1676,9 @@ export class AppImManager { @@ -1568,17 +1676,9 @@ export class AppImManager {
} else /* if(!message.reply_to_mid) */ {
bubble.classList.add('hide-name');
}
//bubble.prepend(avatarDiv);
/* if(messageDiv.nextElementSibling) {
bubble.insertBefore(avatarDiv, messageDiv.nextElementSibling);
} else { */
//}
}
if(!our && this.peerID < 0 &&
(!appPeersManager.isChannel(this.peerID) || appPeersManager.isMegagroup(this.peerID))) {
if(!our && this.peerID < 0 && (!appPeersManager.isChannel(this.peerID) || appPeersManager.isMegagroup(this.peerID))) {
let avatarDiv = document.createElement('div');
avatarDiv.classList.add('user-avatar');
@ -1599,26 +1699,6 @@ export class AppImManager { @@ -1599,26 +1699,6 @@ export class AppImManager {
bubble.classList.add('hide-name');
}
if(message._ == 'messageService') {
bubble.className = 'bubble service';
let action = message.action;
let title = appPeersManager.getPeerTitle(message.fromID);
let name = document.createElement('div');
name.classList.add('name');
name.dataset.peerID = message.fromID;
name.innerHTML = title;
let _ = action._;
if(_ == "messageActionPhoneCall") {
_ += '.' + action.type;
}
// @ts-ignore
let str = (name.innerText ? name.outerHTML + ' ' : '') + langPack[_];
bubbleContainer.innerHTML = `<div class="service-msg">${str}</div>`;
}
bubble.classList.add(our ? 'is-out' : 'is-in');
if(updatePosition) {
if(reverse) {
@ -1627,6 +1707,8 @@ export class AppImManager { @@ -1627,6 +1707,8 @@ export class AppImManager {
this.scrollable.append(bubble);
}
this.bubbleGroups.addBubble(bubble, message, reverse);
let justDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
let dateTimestamp = justDate.getTime();
if(!(dateTimestamp in this.dateMessages)) {
@ -1640,11 +1722,10 @@ export class AppImManager { @@ -1640,11 +1722,10 @@ export class AppImManager {
if(today < date) {
str = 'Today';
} else {
const months = ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'];
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
str = justDate.getFullYear() == new Date().getFullYear() ?
months[justDate.getMonth()] + ' ' + justDate.getDate() :
justDate.toISOString().split('T')[0].split('-').reverse().join('.');
months[justDate.getMonth()] + ' ' + justDate.getDate() :
justDate.toISOString().split('T')[0].split('-').reverse().join('.');
}
let div = document.createElement('div');
@ -1664,6 +1745,8 @@ export class AppImManager { @@ -1664,6 +1745,8 @@ export class AppImManager {
this.scrollable.insertBefore(dateMessage.div, bubble);
}
}
} else {
this.bubbleGroups.updateGroupByMessageID(message.mid);
}
/* if(bubble.classList.contains('webpage')) {
@ -1675,8 +1758,6 @@ export class AppImManager { @@ -1675,8 +1758,6 @@ export class AppImManager {
if(!multipleRender) {
this.scrollPosition.restore(); // лагает из-за этого
}
//this.log('history msg', message);
}
// reverse means scroll up
@ -1688,9 +1769,7 @@ export class AppImManager { @@ -1688,9 +1769,7 @@ export class AppImManager {
maxID = dialog.top_message/* + 1 */;
}
let loadCount = Object.keys(this.bubbles).length > 0 ?
20 :
this.scrollable.container.parentElement.scrollHeight / 30 * 1.25 | 0;
let loadCount = Object.keys(this.bubbles).length > 0 ? 20 : this.scrollable.container.parentElement.scrollHeight / 30 * 1.25 | 0;
/* if(testScroll) {
loadCount = 1;
@ -1800,7 +1879,7 @@ export class AppImManager { @@ -1800,7 +1879,7 @@ export class AppImManager {
peer: inputPeer
};
let settings: any = {
let settings = {
_: 'inputPeerNotifySettings',
flags: 0,
mute_until: 0
@ -1842,49 +1921,47 @@ export class AppImManager { @@ -1842,49 +1921,47 @@ export class AppImManager {
switch(update._) {
case 'updateUserTyping':
case 'updateChatUserTyping':
if(this.myID == update.user_id) {
return;
}
var peerID = update._ == 'updateUserTyping' ? update.user_id : -update.chat_id;
this.typingUsers[update.user_id] = peerID;
if(this.myID == update.user_id) {
return;
}
if(!appUsersManager.hasUser(update.user_id)) {
if(update.chat_id &&
appChatsManager.hasChat(update.chat_id) &&
!appChatsManager.isChannel(update.chat_id)) {
appProfileManager.getChatFull(update.chat_id);
}
var peerID = update._ == 'updateUserTyping' ? update.user_id : -update.chat_id;
this.typingUsers[update.user_id] = peerID;
//return;
if(!appUsersManager.hasUser(update.user_id)) {
if(update.chat_id && appChatsManager.hasChat(update.chat_id) && !appChatsManager.isChannel(update.chat_id)) {
appProfileManager.getChatFull(update.chat_id);
}
appUsersManager.forceUserOnline(update.user_id);
//return;
}
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
let currentPeer = this.peerID == peerID;
appUsersManager.forceUserOnline(update.user_id);
if(this.typingTimeouts[peerID]) clearTimeout(this.typingTimeouts[peerID]);
else if(dialog) {
appDialogsManager.setTyping(dialog, appUsersManager.getUser(update.user_id));
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
let currentPeer = this.peerID == peerID;
if(currentPeer) { // user
this.setPeerStatus();
}
if(this.typingTimeouts[peerID]) clearTimeout(this.typingTimeouts[peerID]);
else if(dialog) {
appDialogsManager.setTyping(dialog, appUsersManager.getUser(update.user_id));
if(currentPeer) { // user
this.setPeerStatus();
}
}
this.typingTimeouts[peerID] = setTimeout(() => {
this.typingTimeouts[peerID] = 0;
delete this.typingUsers[update.user_id];
this.typingTimeouts[peerID] = setTimeout(() => {
this.typingTimeouts[peerID] = 0;
delete this.typingUsers[update.user_id];
if(dialog) {
appDialogsManager.unsetTyping(dialog);
}
if(dialog) {
appDialogsManager.unsetTyping(dialog);
}
// лень просчитывать случаи
this.setPeerStatus();
}, 6000);
break;
// лень просчитывать случаи
this.setPeerStatus();
}, 6000);
break;
case 'updateNotifySettings': {
let {peer, notify_settings} = update;

7
src/lib/appManagers/appSidebarLeft.ts

@ -87,9 +87,7 @@ class AppSidebarLeft { @@ -87,9 +87,7 @@ class AppSidebarLeft {
};
constructor() {
this.chatsPreloader = document.createElement('div');
this.chatsPreloader.classList.add('preloader');
putPreloader(this.chatsPreloader);
this.chatsPreloader = putPreloader(null, true);
//this.chatsContainer.append(this.chatsPreloader);
//this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
@ -98,7 +96,6 @@ class AppSidebarLeft { @@ -98,7 +96,6 @@ class AppSidebarLeft {
this.scroll.setVirtualContainer(appDialogsManager.chatList);
this.scroll.onScrolledBottom = this.onChatsScroll.bind(this);
appDialogsManager.chatsHidden = this.scroll.hiddenElements;
//this.scroll.container.addEventListener('scroll', this.onChatsScroll.bind(this));
this.scrollArchived = new Scrollable(this.chatsArchivedContainer as HTMLDivElement, false, true, 300, 'CLA');
this.scrollArchived.setVirtualContainer(appDialogsManager.chatListArchived);
@ -134,7 +131,7 @@ class AppSidebarLeft { @@ -134,7 +131,7 @@ class AppSidebarLeft {
for(let i = 0; i < 1000; ++i) {
let li = document.createElement('li');
li.dataset.id = '' + i;
li.innerHTML = `<div class="rp"><div class="user-avatar" style="background-color: rgb(166, 149, 231); font-size: 0px;"><img src="blob:https://localhost:9000/ce99a2a3-f34b-4ca1-a09e-f716f89930d8"></div><div class="user-caption"><p><span class="user-title">${i}</span><span><span class="message-status"></span><span class="message-time">18:33</span></span></p><p><span class="user-last-message"><b>Ильяс: </b>Гагагагга</span><span></span></p></div></div>`;
li.innerHTML = `<div class="rp"><div class="user-avatar" style="background-color: rgb(166, 149, 231); font-size: 0px;"><img src="#"></div><div class="user-caption"><p><span class="user-title">${i}</span><span><span class="message-status"></span><span class="message-time">18:33</span></span></p><p><span class="user-last-message"><b>-_-_-_-: </b>qweasd</span><span></span></p></div></div>`;
this.scroll.append(li);
}
}

193
src/lib/appManagers/appSidebarRight.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import { horizontalMenu, formatPhoneNumber } from "../../components/misc";
import { horizontalMenu, formatPhoneNumber, putPreloader } from "../../components/misc";
import Scrollable from '../../components/scrollable';
import { isElementInViewport, $rootScope } from "../utils";
import { $rootScope } from "../utils";
import appMessagesManager from "./appMessagesManager";
import appPhotosManager from "./appPhotosManager";
import appPeersManager from "./appPeersManager";
@ -94,11 +94,23 @@ class AppSidebarRight { @@ -94,11 +94,23 @@ class AppSidebarRight {
this.sidebarScroll = new Scrollable(this.sidebarEl, false, true, 500, 'SR');
this.sidebarScroll.container.addEventListener('scroll', this.onSidebarScroll.bind(this));
this.sidebarScroll.onScrolledBottom = () => {
if(this.sharedMediaSelected && !this.sidebarScroll.hiddenElements.down.length
&& this.sharedMediaSelected.childElementCount/* && false */) {
this.loadSidebarMedia(true);
}
};
horizontalMenu(this.profileTabs, container, (id, tabContent) => {
if(this.prevTabID == id) return;
this.sharedMediaType = this.sharedMediaTypes[id];
this.sharedMediaSelected = tabContent.firstElementChild as HTMLDivElement;
if(this.prevTabID != -1 && !this.sharedMediaSelected.childElementCount) { // quick brown fix
this.loadSidebarMedia(true);
}
if(this.prevTabID != -1) {
this.savedVirtualStates[this.prevTabID] = {
hiddenElements: {
@ -114,7 +126,7 @@ class AppSidebarRight { @@ -114,7 +126,7 @@ class AppSidebarRight {
this.prevTabID = id;
//this.log('setVirtualContainer', id, this.sharedMediaSelected);
this.log('setVirtualContainer', id, this.sharedMediaSelected);
this.sidebarScroll.setVirtualContainer(this.sharedMediaSelected);
if(this.savedVirtualStates[id]) {
@ -124,8 +136,6 @@ class AppSidebarRight { @@ -124,8 +136,6 @@ class AppSidebarRight {
}
}, this.onSidebarScroll.bind(this));
//(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
let sidebarCloseBtn = this.sidebarEl.querySelector('.sidebar-close-button') as HTMLButtonElement;
sidebarCloseBtn.addEventListener('click', () => {
this.toggleSidebar(false);
@ -152,7 +162,7 @@ class AppSidebarRight { @@ -152,7 +162,7 @@ class AppSidebarRight {
});
this.profileElements.notificationsCheckbox.addEventListener('change', () => {
let checked = this.profileElements.notificationsCheckbox.checked;
//let checked = this.profileElements.notificationsCheckbox.checked;
appImManager.mutePeer();
});
@ -183,42 +193,31 @@ class AppSidebarRight { @@ -183,42 +193,31 @@ class AppSidebarRight {
public onSidebarScroll() {
this.lazyLoadQueueSidebar.check();
if(this.sharedMediaSelected && !this.sidebarScroll.hiddenElements.down.length/* && false */) {
let media = Array.from(this.sharedMediaSelected.childNodes).slice(-15);
for(let div of media) {
if(isElementInViewport(div)) {
//this.log('Will load more media');
this.loadSidebarMedia(true);
break;
}
}
}
}
public toggleSidebar(enable?: boolean) {
/////this.log('sidebarEl', this.sidebarEl, enable, isElementInViewport(this.sidebarEl));
/* if(enable !== undefined) {
this.sidebarEl.style.display = enable ? 'block' : 'none';
return;
}
this.sidebarEl.style.display = isElementInViewport(this.sidebarEl) ? 'none' : 'block'; */
if(enable !== undefined) {
this.sidebarEl.style.width = enable ? '25%' : '0%';
if(enable) this.sidebarEl.classList.add('active');
else this.sidebarEl.classList.remove('active');
return;
}
this.sidebarEl.style.width = isElementInViewport(this.sidebarEl) ? '0%' : '25%';
if(this.sidebarEl.classList.contains('active')) {
this.sidebarEl.classList.remove('active');
} else {
this.sidebarEl.classList.add('active');
}
}
public loadSidebarMedia(single = false) {
if(testScroll) {
if(testScroll /* || 1 == 1 */) {
return;
}
//this.log('loadSidebarMedia', single, this.peerID);
let peerID = this.peerID;
let typesToLoad = single ? [this.sharedMediaType] : this.sharedMediaTypes;
@ -237,12 +236,12 @@ class AppSidebarRight { @@ -237,12 +236,12 @@ class AppSidebarRight {
let maxID = history[history.length - 1] || 0;
let ids = !maxID && appMessagesManager.historiesStorage[peerID]
? appMessagesManager.historiesStorage[peerID].history.slice() : [];
? appMessagesManager.historiesStorage[peerID].history.slice() : [];
maxID = !maxID && ids.length ? ids[ids.length - 1] : maxID;
//this.log('search house of glass pre', type, ids, maxID);
return this.loadSidebarMediaPromises[type] = appMessagesManager.getSearch(peerID, '', {_: type}, maxID, 50)
return this.loadSidebarMediaPromises[type] = appMessagesManager.getSearch(peerID, '', {_: type}, maxID, history.length ? 50 : 15)
.then(value => {
ids = ids.concat(value.history);
history.push(...ids);
@ -259,33 +258,32 @@ class AppSidebarRight { @@ -259,33 +258,32 @@ class AppSidebarRight {
delete this.cleared[type];
}
ids.forEach(mid => {
//this.minMediaID[type] = mid;
let sharedMediaDiv: HTMLDivElement;
let messages: any[] = [];
for(let mid of ids) {
let message = appMessagesManager.getMessage(mid);
if(!message.media) return;
/*'inputMessagesFilterContacts',
'inputMessagesFilterPhotoVideo',
'inputMessagesFilterDocument',
'inputMessagesFilterUrl',
'inputMessagesFilterVoice'*/
switch(type) {
case 'inputMessagesFilterPhotoVideo': {
/* if(!(message.media.photo || message.media.document || message.media.webpage.document)) {
this.log.error('no media!', message);
break;
} */
if(message.media) messages.push(message);
}
let media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
/*'inputMessagesFilterContacts',
'inputMessagesFilterPhotoVideo',
'inputMessagesFilterDocument',
'inputMessagesFilterUrl',
'inputMessagesFilterVoice'*/
switch(type) {
case 'inputMessagesFilterPhotoVideo': {
sharedMediaDiv = this.sharedMedia.contentMedia;
for(let message of messages) {
let media = message.media.photo || message.media.document || (message.media.webpage && message.media.webpage.document);
if(!media) {
//this.log('no media!', message);
break;
continue;;
}
if(media._ == 'document' && media.type != 'video'/* && media.type != 'gif' */) {
//this.log('broken video', media);
break;
continue;
}
let div = document.createElement('div');
@ -310,45 +308,53 @@ class AppSidebarRight { @@ -310,45 +308,53 @@ class AppSidebarRight {
div.style.backgroundImage = 'url(' + URL.createObjectURL(blob) + ')';
});
div.setAttribute('message-id', '' + mid);
div.setAttribute('message-id', '' + message.mid);
this.lazyLoadQueueSidebar.push({div, load});
this.lastSharedMediaDiv.append(div);
if(this.lastSharedMediaDiv.childElementCount == 3) {
this.sharedMedia.contentMedia.append(this.lastSharedMediaDiv);
this.sidebarScroll.append(this.lastSharedMediaDiv);
this.lastSharedMediaDiv = document.createElement('div');
}
this.mediaDivsByIDs[mid] = div;
//this.sharedMedia.contentMedia.append(div);
this.mediaDivsByIDs[message.mid] = div;
break;
//sharedMediaDiv.append(div);
}
case 'inputMessagesFilterDocument': {
break;
}
case 'inputMessagesFilterDocument': {
sharedMediaDiv = this.sharedMedia.contentDocuments;
for(let message of messages) {
if(!message.media.document || message.media.document.type == 'voice') {
break;
continue;
}
let doc = message.media.document;
if(doc.attributes) {
if(doc.attributes.find((a: any) => a._ == "documentAttributeSticker")) {
break;
continue;
}
}
//this.log('come back down to my knees', message);
let div = wrapDocument(message.media.document, true);
this.sharedMedia.contentDocuments.append(div);
break;
this.sidebarScroll.append(div);
}
break;
}
case 'inputMessagesFilterUrl': {
sharedMediaDiv = this.sharedMedia.contentLinks;
case 'inputMessagesFilterUrl': {
for(let message of messages) {
if(!message.media.webpage || message.media.webpage._ == 'webPageEmpty') {
break;
continue;
}
let webpage = message.media.webpage;
@ -387,40 +393,48 @@ class AppSidebarRight { @@ -387,40 +393,48 @@ class AppSidebarRight {
div.append(previewDiv);
div.insertAdjacentHTML('beforeend', `
<div class="title">${title}</div>
<div class="subtitle">${subtitle}</div>
<div class="url">${url}</div>
<div class="title">${title}</div>
<div class="subtitle">${subtitle}</div>
<div class="url">${url}</div>
`);
if(div.innerText.trim().length) {
this.sharedMedia.contentLinks.append(div);
this.sidebarScroll.append(div);
}
}
break;
}
/* case 'inputMessagesFilterVoice': {
//this.log('wrapping audio', message.media);
if(!message.media || !message.media.document || message.media.document.type != 'voice') {
break;
}
/* case 'inputMessagesFilterVoice': {
//this.log('wrapping audio', message.media);
if(!message.media || !message.media.document || message.media.document.type != 'voice') {
break;
}
let doc = message.media.document;
let doc = message.media.document;
this.log('wrapping audio', doc);
this.log('wrapping audio', doc);
let audioDiv = wrapAudio(doc);
let audioDiv = wrapAudio(doc);
this.sharedMedia.contentAudio.append(audioDiv);
this.sharedMedia.contentAudio.append(audioDiv);
break;
} */
break;
} */
default:
//console.warn('death is my friend', message);
break;
}
default:
//console.warn('death is my friend', message);
break;
if(sharedMediaDiv) {
let parent = sharedMediaDiv.parentElement;
if(parent.lastElementChild.classList.contains('preloader')) {
parent.lastElementChild.remove();
}
});
}
this.onSidebarScroll();
}).then(() => {
@ -441,15 +455,6 @@ class AppSidebarRight { @@ -441,15 +455,6 @@ class AppSidebarRight {
//this.log('fillProfileElements');
this.savedVirtualStates = {};
this.prevTabID = -1;
this.sidebarScroll.setVirtualContainer(null);
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
if(this.sharedMediaSelected) {
//this.sidebarScroll.setVirtualContainer(this.sharedMediaSelected);
}
this.profileContentEl.parentElement.scrollTop = 0;
this.profileElements.bio.style.display = 'none';
this.profileElements.phone.style.display = 'none';
@ -464,6 +469,11 @@ class AppSidebarRight { @@ -464,6 +469,11 @@ class AppSidebarRight {
Object.keys(this.sharedMedia).forEach(key => {
this.sharedMedia[key].innerHTML = '';
let parent = this.sharedMedia[key].parentElement;
if(!parent.querySelector('.preloader')) {
putPreloader(parent, true);
}
});
this.sharedMediaTypes.forEach(type => {
@ -471,6 +481,11 @@ class AppSidebarRight { @@ -471,6 +481,11 @@ class AppSidebarRight {
this.cleared[type] = true;
});
this.savedVirtualStates = {};
this.prevTabID = -1;
this.sidebarScroll.setVirtualContainer(null);
(this.profileTabs.children[1] as HTMLLIElement).click(); // set media
let setText = (text: string, el: HTMLDivElement) => {
el.style.display = '';
if(el.childElementCount > 1) {
@ -488,9 +503,7 @@ class AppSidebarRight { @@ -488,9 +503,7 @@ class AppSidebarRight {
if(username) {
setText(appPeersManager.getPeerUsername(peerID), this.profileElements.username);
}
}
if(peerID != appImManager.myID) {
let dialog: any = appMessagesManager.getDialogByPeerID(peerID);
if(dialog.length) {
dialog = dialog[0];

12
src/lib/ckin.js

@ -3,18 +3,6 @@ @@ -3,18 +3,6 @@
NodeList.prototype.forEach = Array.prototype.forEach;
})();
String.prototype.toHHMMSS = function(leadZero) {
let sec_num = parseInt(this, 10);
let hours = Math.floor(sec_num / 3600);
let minutes = Math.floor((sec_num - (hours * 3600)) / 60);
let seconds = sec_num - (hours * 3600) - (minutes * 60);
if(hours < 10) hours = "0" + hours;
if(minutes < 10) minutes = leadZero ? "0" + minutes : minutes;
if(seconds < 10) seconds = "0" + seconds;
return minutes + ':' + seconds;
}
function stylePlayer(player, video) {
let skin = attachSkin(video.dataset.ckin);
player.classList.add(skin);

4
src/lib/lottieLoader.ts

@ -33,9 +33,9 @@ class LottieLoader { @@ -33,9 +33,9 @@ class LottieLoader {
for(let i = length - 1; i >= 0; --i) {
let {animation, container, paused, autoplay, canvas} = animations[i];
if(canvas && isElementInViewport(container)) {
if(canvas) {
let c = container.firstElementChild as HTMLCanvasElement;
if(!c.height && !c.width) {
if(!c.height && !c.width && isElementInViewport(container)) {
//console.log('lottie need resize');
animation.resize();
}

22
src/lib/polyfill.ts

@ -53,6 +53,23 @@ Array.prototype.forEachReverse = function<T>(callback: (value: T, index?: number @@ -53,6 +53,23 @@ Array.prototype.forEachReverse = function<T>(callback: (value: T, index?: number
}
};
Array.prototype.findAndSplice = function<T>(verify: (value: T, index?: number, array?: Array<T>) => boolean) {
let index = this.findIndex(verify);
return index !== -1 ? this.splice(index, 1)[0] : undefined;
};
String.prototype.toHHMMSS = function(leadZero = false) {
let sec_num = parseInt(this + '', 10);
let hours: any = Math.floor(sec_num / 3600);
let minutes: any = Math.floor((sec_num - (hours * 3600)) / 60);
let seconds: any = sec_num - (hours * 3600) - (minutes * 60);
if(hours < 10) hours = "0" + hours;
if(minutes < 10) minutes = leadZero ? "0" + minutes : minutes;
if(seconds < 10) seconds = "0" + seconds;
return minutes + ':' + seconds;
}
declare global {
interface Uint8Array {
hex: string;
@ -62,5 +79,10 @@ declare global { @@ -62,5 +79,10 @@ declare global {
interface Array<T> {
forEachReverse(callback: (value: T, index?: number, array?: Array<T>) => void): void;
findAndSplice(verify: (value: T, index?: number, array?: Array<T>) => boolean): T;
}
interface String {
toHHMMSS(leadZero?: boolean): string;
}
}

2
src/lib/utils.js

@ -297,7 +297,7 @@ export function getSelectedText() { @@ -297,7 +297,7 @@ export function getSelectedText() {
export const $rootScope = {
$broadcast: (name/* : string */, detail/*? : any */) => {
////console.log(dT(), 'Broadcasting ' + name + ' event, with args:', detail);
//console.log(dT(), 'Broadcasting ' + name + ' event, with args:', detail);
//console.trace();
let myCustomEvent = new CustomEvent(name, {detail});
document.dispatchEvent(myCustomEvent);

40
src/scss/partials/_chat.scss

@ -370,8 +370,8 @@ @@ -370,8 +370,8 @@
cursor: pointer;
background: none!important;
box-shadow: none;
max-width: 300px;
max-height: 300px;
/* max-width: 300px;
max-height: 300px; */
}
img {
@ -387,6 +387,13 @@ @@ -387,6 +387,13 @@
}
}
&.sticker {
.bubble__container {
max-width: 200px;
max-height: 200px;
}
}
&.round {
.attachment {
max-width: 200px;
@ -702,11 +709,11 @@ @@ -702,11 +709,11 @@
/* padding-bottom: 4px; */
color: $darkblue;
font-size: .9rem;
width: max-content;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
//width: max-content;
//white-space: nowrap;
}
&:not(.webpage) {
@ -732,17 +739,26 @@ @@ -732,17 +739,26 @@
margin-top: 6px;
}
&:not(.sticker):not(.emoji-big):not(.round):last-child .bubble__container:after {
&:not(.sticker):not(.emoji-big):not(.round).is-group-last .bubble__container:after {
position: absolute;
bottom: -1px;
bottom: 0;
width: 11px;
height: 20px;
background-repeat: no-repeat repeat;
content: '';
background-size: 11px 20px;
background-position-y: 1px;
}
}
.bubble-audio.is-in .time {
width: inherit;
}
.bubble-audio.is-out .time {
width: inherit;
}
/* .bubble + .bubble {
margin-top: 5px;
} */
@ -761,11 +777,11 @@ @@ -761,11 +777,11 @@
border-radius: 6px 12px 12px 6px;
}
&:first-child .bubble__container {
&.is-group-first .bubble__container {
border-radius: 12px 12px 12px 6px;
}
&:last-child .bubble__container {
&.is-group-last .bubble__container {
border-radius: 6px 12px 12px 0px;
//border-radius: 12px 12px 12px 0px;
@ -775,7 +791,7 @@ @@ -775,7 +791,7 @@
}
}
&:first-child:last-child .bubble__container {
&.is-group-first.is-group-last .bubble__container {
border-radius: 12px 12px 12px 0px;
}
@ -835,11 +851,11 @@ @@ -835,11 +851,11 @@
border-radius: 12px 6px 6px 12px;
}
&:first-child .bubble__container {
&.is-group-first .bubble__container {
border-radius: 12px 12px 6px 12px;
}
&:last-child .bubble__container {
&.is-group-last .bubble__container {
border-radius: 12px 6px 0px 12px;
&:after {
@ -848,7 +864,7 @@ @@ -848,7 +864,7 @@
}
}
&:first-child:last-child .bubble__container {
&.is-group-first.is-group-last .bubble__container {
border-radius: 12px 12px 0px 12px;
}

286
src/scss/partials/_rightSIdebar.scss

@ -0,0 +1,286 @@ @@ -0,0 +1,286 @@
.profile-container {
width: 0%;
/* grid-column: 3; */
position: relative;
transition: .2s ease-in-out;
> .scrollable {
min-width: 25vw;
display: flex;
flex-direction: column;
}
@media (min-width: $large-screen) {
> .scrollable {
min-width: calc(#{$large-screen} / 4 - 1px);
}
}
&:not(.active) {
border-left-width: 0;
}
&.active {
width: 25%;
}
.sidebar-header {
flex: 0 0 auto;
}
}
.profile-content {
flex: 1 1 auto;
display: flex;
flex-direction: column;
.profile-name {
text-align: center;
font-size: 23px;
font-weight: 500;
margin-bottom: 3px;
span.emoji {
vertical-align: inherit;
min-width: min-content;
}
}
.profile-subtitle {
text-align: center;
color: $darkgrey;
font-size: 14px;
&.online {
color: $darkblue;
}
}
.profile-row {
display: flex;
width: 100%;
flex-direction: column;
padding-left: 80px;
padding-top: 2px;
padding-right: 12px;
font-size: 15px;
position: relative;
margin-top: 1.75rem;
&:before {
position: absolute;
left: 24px;
/* top: 0; */
font-size: 24px;
color: $darkgrey;
}
p {
color: #000;
margin: 0;
}
&-bio {
.emoji {
width: 24px;
height: 24px;
}
}
}
p.profile-row-label {
color: $placeholder-color;
font-size: 14px;
margin-top: 1px;
}
.user-avatar {
width: 120px;
height: 120px;
margin: 0 auto 20px;
font-size: 4rem!important;
}
[type="checkbox"] + span {
padding-left: 54px;
margin-left: -54px;
}
.content-container {
width: 100%;
max-width: 100%;
overflow: hidden;
flex: 1;
}
.profile-tabs {
margin-top: 40px;
}
.profile-tabs-content {
height: 100%;
/* width: 500%;
margin-left: -100%;
*/
/* > div {
height: 0;
&.active {
height: auto;
}
} */
> div {
height: 100%;
position: relative;
}
/* > div > div:not(.scroll-padding) {
height: 100%;
} */
.preloader {
padding: 0;
position: absolute;
height: 100%;
> svg {
height: 50px;
width: 50px;
}
}
#content-media {
width: 100%;
display: flex;
flex-direction: column;
padding-top: 4px;
> div {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-rows: max-content;
grid-gap: 3.5px;
place-items: start;
padding-top: 3.5px;
> div {
width: 100%;
cursor: pointer;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
display: flex;
background-color: #cecece;
justify-content: center;
align-items: center;
&::before {
content: "";
display: inline-block;
width: 1px;
height: 0;
padding-bottom: 100%;
}
}
}
}
#content-docs {
padding: 7px 20px;
.document {
padding-left: 4rem;
padding-right: 1rem;
//height: 54px;
height: calc(50px + 1.5rem);
&-ico, &-download {
width: 48px;
height: 48px;
}
/* & + .document {
margin-top: 1.5rem;
} */
}
.document-name {
font-weight: normal;
width: 100%;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
}
#content-links {
padding: 0 30px 15px 15px;
> div {
display: flex;
flex-direction: column;
margin-top: 20px;
margin-left: 5px;
padding-bottom: 2px;
//padding-bottom: 10px;
position: relative;
padding-left: 60px;
overflow: hidden;
//min-height: 48px;
min-height: 58px;
.preview {
height: 48px;
width: 48px;
border-radius: 5px;
overflow: hidden;
position: absolute;
left: 0;
top: 0;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
&.empty {
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
color: #fff;
text-transform: uppercase;
background-color: $blue;
}
}
.url {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.title {
font-size: 16px;
margin-top: 3px;
}
.subtitle {
font-size: 14px;
}
}
#content-audio {
padding: 0 15px 15px 15px;
> div {
margin-top: 15px;
padding-bottom: 10px;
min-height: 60px;
}
}
}
}

231
src/scss/partials/_sidebar.scss

@ -29,234 +29,3 @@ @@ -29,234 +29,3 @@
margin-left: .5rem;
}
}
.profile-content {
.profile-name {
text-align: center;
font-size: 23px;
font-weight: 500;
margin: 3px 0;
span.emoji {
vertical-align: inherit;
min-width: min-content;
}
}
.profile-subtitle {
text-align: center;
color: $darkgrey;
font-size: 14px;
margin: 0 0 18px;
&.online {
color: $darkblue;
}
}
.profile-row {
display: flex;
width: 100%;
flex-direction: column;
padding-left: 80px;
padding-top: 2px;
padding-right: 12px;
font-size: 15px;
position: relative;
margin: 1.75rem 0;
&:before {
position: absolute;
left: 24px;
/* top: 0; */
font-size: 24px;
color: $darkgrey;
}
p {
color: #000;
margin: 0;
}
.profile-row-label {
color: $placeholder-color;
font-size: 14px;
margin-top: 1px;
}
}
.profile-row-bio {
.emoji {
width: 24px;
height: 24px;
}
}
.user-avatar {
width: 120px;
height: 120px;
margin: 0 auto 20px;
font-size: 4rem!important;
}
[type="checkbox"] + span {
padding-left: 54px;
margin-left: -54px;
}
.content-container {
width: 100%;
max-width: 100%;
overflow: hidden;
}
.profile-tabs {
margin-top: 40px;
}
.profile-tabs-content {
/* width: 500%;
margin-left: -100%;
*/
/* > div {
height: 0;
&.active {
height: auto;
}
} */
#content-media {
width: 100%;
display: flex;
flex-direction: column;
padding-top: 4px;
> div {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-rows: max-content;
grid-gap: 3.5px;
place-items: start;
padding-top: 3.5px;
> div {
width: 100%;
cursor: pointer;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
display: flex;
background-color: #cecece;
justify-content: center;
align-items: center;
&::before {
content: "";
display: inline-block;
width: 1px;
height: 0;
padding-bottom: 100%;
}
}
}
}
#content-docs {
padding: 7px 20px;
.document {
padding-left: 4rem;
padding-right: 1rem;
//height: 54px;
height: calc(50px + 1.5rem);
&-ico, &-download {
width: 48px;
height: 48px;
}
/* & + .document {
margin-top: 1.5rem;
} */
}
.document-name {
font-weight: normal;
width: 100%;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
}
#content-links {
padding: 0 30px 15px 15px;
> div {
display: flex;
flex-direction: column;
margin-top: 20px;
margin-left: 5px;
padding-bottom: 2px;
//padding-bottom: 10px;
position: relative;
padding-left: 60px;
overflow: hidden;
//min-height: 48px;
min-height: 58px;
.preview {
height: 48px;
width: 48px;
border-radius: 5px;
overflow: hidden;
position: absolute;
left: 0;
top: 0;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
&.empty {
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
color: #fff;
text-transform: uppercase;
background-color: $blue;
}
}
.url {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.title {
font-size: 16px;
margin-top: 3px;
}
.subtitle {
font-size: 14px;
}
}
#content-audio {
padding: 0 15px 15px 15px;
> div {
margin-top: 15px;
padding-bottom: 10px;
min-height: 60px;
}
}
}
}

34
src/scss/style.scss

@ -29,11 +29,15 @@ $bg: #ffffff; @@ -29,11 +29,15 @@ $bg: #ffffff;
$text-size: 16px;
$time-size: 12px;
//$large-screen: 1680px;
$large-screen: 16800px;
@import "partials/ico";
@import "partials/chatlist";
@import "partials/chat";
@import "partials/sidebar";
@import "partials/leftSidebar";
@import "partials/rightSidebar";
@import "partials/mediaViewer";
@import "partials/ckin";
@import "partials/emojiDropdown";
@ -59,7 +63,18 @@ button, input, optgroup, select, textarea, html { @@ -59,7 +63,18 @@ button, input, optgroup, select, textarea, html {
height: 100vh;
min-height: 100vh;
width: 100%;
min-width: 100%;
//min-width: 100%;
margin: 0 auto;
max-width: $large-screen;
@media (min-width: $large-screen) {
border-top-width: 0;
border-bottom-width: 0;
border-left-width: 1px;
border-right-width: 1px;
border-style: solid;
border-color: #DADCE0;
}
}
.container {
@ -481,13 +496,13 @@ input { @@ -481,13 +496,13 @@ input {
position: relative;
padding-left: 67px;
min-height: 58px;
max-width: 286px;
max-width: 244px;
overflow: visible!important;
&-toggle, &-download {
border-radius: 50%;
background-color: $blue;
font-size: 2.2rem;
font-size: 2.3rem;
align-items: center;
}
@ -1563,19 +1578,6 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -1563,19 +1578,6 @@ div.scrollable::-webkit-scrollbar-thumb {
flex: 1;
}
.profile-container {
//display: none;
width: 0%;
/* grid-column: 3; */
position: relative;
transition: .2s ease-in-out;
> .scrollable {
min-width: 25vw;
}
}
.preloader {
width: 50px;
height: 50px;

4
tsconfig.json

@ -65,9 +65,9 @@ @@ -65,9 +65,9 @@
"node_modules",
"public",
"coverage",
"./src/lib/StackBlur.js",
/* "./src/lib/StackBlur.js",
"./src/lib/*.js",
"./src/*.js",
"*.js",
"*.js", */
]
}

2
webpack.prod.js

@ -59,7 +59,7 @@ module.exports = merge(common, { @@ -59,7 +59,7 @@ module.exports = merge(common, {
files.forEach(file => {
//console.log('to unlink 1:', file);
if(file.includes('mitm.') || file.includes('sw.js')) return;
if(file.includes('mitm.') || file.includes('sw.js') || file.includes('.xml') || file.includes('.webmanifest')) return;
let p = path.resolve(buildDir + file);
if(!newlyCreatedAssets[file] && ['.gz', '.js'].find(ext => file.endsWith(ext)) !== undefined) {

Loading…
Cancel
Save