Browse Source

scroll try #10

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

4
package.json

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

30
src/components/misc.ts

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

349
src/components/scrollable.ts

@ -32,12 +32,13 @@ export default class Scrollable {
public type: string; public type: string;
public side: string; public side: string;
public translate: string;
public scrollType: string; public scrollType: string;
public scrollSide: string; public scrollSide: string;
public clientAxis: string; public clientAxis: string;
public scrollSize = -1; public scrollSize = -1; // it will be scrollHeight
public size = 0; public size = 0; // it will be outerHeight of container (not scrollHeight)
public thumbSize = 0; public thumbSize = 0;
public hiddenElements: { public hiddenElements: {
@ -57,11 +58,12 @@ export default class Scrollable {
public onAddedBottom: () => void = null; public onAddedBottom: () => void = null;
public onScrolledTop: () => void = null; public onScrolledTop: () => void = null;
public onScrolledBottom: () => void = null; public onScrolledBottom: () => void = null;
public onScrolledTopFired = false;
public onScrolledBottomFired = false;
public topObserver: IntersectionObserver; public topObserver: IntersectionObserver;
public bottomObserver: IntersectionObserver; public bottomObserver: IntersectionObserver;
public splitObserver: IntersectionObserver;
public splitMeasureTop: Promise<{element: Element, height: number}[]> = null; public splitMeasureTop: Promise<{element: Element, height: number}[]> = null;
public splitMeasureBottom: Scrollable['splitMeasureTop'] = null; public splitMeasureBottom: Scrollable['splitMeasureTop'] = null;
public splitMeasureAdd: Promise<number> = null; public splitMeasureAdd: Promise<number> = null;
@ -86,37 +88,17 @@ export default class Scrollable {
private log: ReturnType<typeof logger>; private log: ReturnType<typeof logger>;
private debug = false; 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 = document.createElement('div');
this.container.classList.add('scrollable'); this.container.classList.add('scrollable');
this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : '')); 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) { if(x) {
this.container.classList.add('scrollable-x'); this.container.classList.add('scrollable-x');
this.type = 'width'; this.type = 'width';
this.side = 'left'; this.side = 'left';
this.translate = 'translateX';
this.scrollType = 'scrollWidth'; this.scrollType = 'scrollWidth';
this.scrollSide = 'scrollLeft'; this.scrollSide = 'scrollLeft';
this.clientAxis = 'clientX'; this.clientAxis = 'clientX';
@ -141,6 +123,7 @@ export default class Scrollable {
this.container.classList.add('scrollable-y'); this.container.classList.add('scrollable-y');
this.type = 'height'; this.type = 'height';
this.side = 'top'; this.side = 'top';
this.translate = 'translateY';
this.scrollType = 'scrollHeight'; this.scrollType = 'scrollHeight';
this.scrollSide = 'scrollTop'; this.scrollSide = 'scrollTop';
this.clientAxis = 'clientY'; this.clientAxis = 'clientY';
@ -181,6 +164,7 @@ export default class Scrollable {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
//this.resize.bind(this); //this.resize.bind(this);
this.onScroll(); this.onScroll();
this.resize();
}); });
this.paddingTopDiv = document.createElement('div'); this.paddingTopDiv = document.createElement('div');
@ -188,9 +172,6 @@ export default class Scrollable {
this.paddingBottomDiv = document.createElement('div'); this.paddingBottomDiv = document.createElement('div');
this.paddingBottomDiv.classList.add('scroll-padding'); this.paddingBottomDiv.classList.add('scroll-padding');
this.topObserver.observe(this.paddingTopDiv);
this.bottomObserver.observe(this.paddingBottomDiv);
this.container.addEventListener('scroll', this.onScroll.bind(this)); this.container.addEventListener('scroll', this.onScroll.bind(this));
Array.from(el.children).forEach(c => this.container.append(c)); Array.from(el.children).forEach(c => this.container.append(c));
@ -280,181 +261,9 @@ export default class Scrollable {
}); });
} }
public detachByPrevScroll(child: Element, prevScrollTop: number, needHeight = 0) { public resize() {
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() {
//console.time('scroll resize'); //console.time('scroll resize');
fastdom.mutate(() => {
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(() => {
if(!this.size || this.size == this.scrollSize) { if(!this.size || this.size == this.scrollSize) {
this.thumbSize = 0; this.thumbSize = 0;
@ -480,7 +289,7 @@ export default class Scrollable {
//console.log('onresize', thumb.style[type], thumbHeight, height); //console.log('onresize', thumb.style[type], thumbHeight, height);
} }
public async setVirtualContainer(el?: HTMLElement) { public setVirtualContainer(el?: HTMLElement) {
this.splitUp = el; this.splitUp = el;
this.hiddenElements.up.length = this.hiddenElements.down.length = 0; this.hiddenElements.up.length = this.hiddenElements.down.length = 0;
@ -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.log('setVirtualContainer:', el, this);
this.getScrollTopOffset(); this.getScrollTopOffset();
@ -509,6 +312,9 @@ export default class Scrollable {
el.parentElement.insertBefore(this.paddingTopDiv, el); el.parentElement.insertBefore(this.paddingTopDiv, el);
el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling); el.parentNode.insertBefore(this.paddingBottomDiv, el.nextSibling);
}); });
} else {
this.paddingTopDiv.remove();
this.paddingBottomDiv.remove();
} }
} }
@ -529,33 +335,66 @@ export default class Scrollable {
public onScroll() { public onScroll() {
if(this.onScrollMeasure) fastdom.clear(this.onScrollMeasure); if(this.onScrollMeasure) fastdom.clear(this.onScrollMeasure);
this.onScrollMeasure = fastdom.measure(() => { this.onScrollMeasure = fastdom.measure(() => {
// @ts-ignore quick brown fix
this.size = this.parentElement[this.scrollType];
// @ts-ignore // @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.resize();
} }
this.scrollSize = scrollSize;
// @ts-ignore // @ts-ignore
let value = this.container[this.scrollSide] / (this.scrollSize - this.size) * 100; let scrollPos = this.container[this.scrollSide];
let maxValue = 100 - (this.thumbSize / this.size * 100);
// 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 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) { if(!this.splitUp) {
return ret; return ret;
} }
let perf = performance.now(); 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; if(scrollTop < 0) scrollTop = 0;
else if(scrollTop > maxScrollTop) scrollTop = maxScrollTop; else if(scrollTop > maxScrollTop) scrollTop = maxScrollTop;
let toBottom = scrollTop > this.lastScrollTop; let toBottom = scrollTop > this.lastScrollTop;
let visibleFrom = /* scrollTop < this.paddings.up ? scrollTop : */scrollTop - this.paddings.up; let visibleFrom = /* scrollTop < this.paddings.up ? scrollTop : */scrollTop - this.paddings.up;
let visibleUntil = visibleFrom + outerHeight; let visibleUntil = visibleFrom + this.size;
let sum = 0; let sum = 0;
let firstVisibleElement: Element; let firstVisibleElement: Element;
@ -619,10 +458,6 @@ export default class Scrollable {
this.onBottomIntersection(needHeight); this.onBottomIntersection(needHeight);
return needHeight; return needHeight;
}); });
/* this.splitMeasureAdd.then(needHeight => {
this.onBottomIntersection(needHeight);
}); */
} else if(length) { // scrolled manually or safari } else if(length) { // scrolled manually or safari
if(this.debug) { if(this.debug) {
this.log.warn('will detach all of top', length, this.splitUp.childElementCount, maxScrollTop, this.paddings, this.lastScrollTop); 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 {
this.onTopIntersection(needHeight); this.onTopIntersection(needHeight);
return needHeight; return needHeight;
}); });
/* this.splitMeasureAdd.then(needHeight => {
this.onTopIntersection(needHeight);
}); */
} else if(length) { // scrolled manually or safari } else if(length) { // scrolled manually or safari
if(this.debug) { if(this.debug) {
this.log.warn('will detach all of bottom', length, this.splitUp.childElementCount, maxScrollTop, this.paddings, this.lastScrollTop); 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 {
this.onScrollMeasure.then(({value, maxValue}) => { this.onScrollMeasure.then(({value, maxValue}) => {
fastdom.mutate(() => { fastdom.mutate(() => {
// @ts-ignore // @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 {
public onManualScrollTop(scrollTop: number, needHeight: number, maxScrollTop: number) { public onManualScrollTop(scrollTop: number, needHeight: number, maxScrollTop: number) {
//if(this.splitMutateRemoveBad) fastdom.clear(this.splitMutateRemoveBad); //if(this.splitMutateRemoveBad) fastdom.clear(this.splitMutateRemoveBad);
this.splitMutateRemoveBad = fastdom.mutate(() => { this.splitMutateRemoveBad = fastdom.mutate(() => {
let h = maxScrollTop - (scrollTop + outerHeight); let h = maxScrollTop - (scrollTop + this.size);
while(this.paddings.down < h && this.paddings.up) { while(this.paddings.down < h && this.paddings.up) {
let child = this.hiddenElements.up.pop(); let child = this.hiddenElements.up.pop();
@ -705,11 +537,8 @@ export default class Scrollable {
} }
this.paddingBottomDiv.style.height = this.paddings.down + 'px'; 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) { public onManualScrollBottom(scrollTop: number, needHeight: number) {
@ -729,11 +558,8 @@ export default class Scrollable {
} }
this.paddingTopDiv.style.height = this.paddings.up + 'px'; 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) { public onTopIntersection(needHeight: number) {
@ -813,7 +639,7 @@ export default class Scrollable {
}); });
if(this.hiddenElements.up.length) { if(this.hiddenElements.up.length) {
fastdom.mutate(() => { /* fastdom.mutate(() => {
this.splitUp.append(...smth); this.splitUp.append(...smth);
}).then(() => { }).then(() => {
return fastdom.measure(() => { return fastdom.measure(() => {
@ -839,13 +665,26 @@ export default class Scrollable {
this.onScroll(); 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 { } else {
this.splitUp.prepend(...smth); this.splitUp.prepend(...smth);
this.onScroll(); this.onScroll();
} }
} else { } else {
this.container.prepend(...smth); this.appendTo.prepend(...smth);
this.onScroll(); this.onScroll();
} }
@ -891,7 +730,7 @@ export default class Scrollable {
this.onScroll(); this.onScroll();
} }
} else { } else {
this.container.append(...smth); this.appendTo.append(...smth);
this.onScroll(); this.onScroll();
} }
@ -914,17 +753,14 @@ export default class Scrollable {
} }
} }
let index = this.hiddenElements.up.findIndex(c => c.element == element); let child = this.hiddenElements.up.findAndSplice(c => c.element == element);
let child: {element: Element, height: number};
let foundUp = false; let foundUp = false;
if(index !== -1) { if(child) {
child = this.hiddenElements.up.splice(index, 1)[0];
this.paddings.up -= child.height; this.paddings.up -= child.height;
foundUp = true; foundUp = true;
} else { } else {
index = this.hiddenElements.down.findIndex(c => c.element == element); child = this.hiddenElements.down.findAndSplice(c => c.element == element);
if(index !== -1) { if(child) {
child = this.hiddenElements.down.splice(index, 1)[0];
this.paddings.down -= child.height; this.paddings.down -= child.height;
} }
} }
@ -943,7 +779,7 @@ export default class Scrollable {
} }
public insertBefore(newChild: Element, refChild: Element, height?: number) { public insertBefore(newChild: Element, refChild: Element, height?: number) {
this.log('insertBefore', newChild, refChild); //this.log('insertBefore', newChild, refChild);
return; return;
if(this.splitUp) { if(this.splitUp) {
@ -1023,6 +859,29 @@ export default class Scrollable {
return ret; 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) { set scrollTop(y: number) {
fastdom.mutate(() => { fastdom.mutate(() => {
this.container.scrollTop = y; this.container.scrollTop = y;

61
src/components/wrappers.ts

@ -259,15 +259,21 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.classList.add('audio-waveform'); svg.classList.add('audio-waveform');
svg.setAttributeNS(null, 'width', '250'); svg.setAttributeNS(null, 'width', '190');
svg.setAttributeNS(null, 'height', '23'); 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); div.insertBefore(svg, div.lastElementChild);
let wave = doc.attributes[0].waveform as Uint8Array; let wave = doc.attributes[0].waveform as Uint8Array;
let index = 0; let index = 0;
let skipped = 0;
for(let uint8 of wave) { for(let uint8 of wave) {
if (index > 0 && index % 4 == 0) {
++index;
++skipped;
continue;
}
let percents = uint8 / 255; let percents = uint8 / 255;
let height = 23 * percents; let height = 23 * percents;
@ -276,12 +282,14 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
} }
svg.insertAdjacentHTML('beforeend', ` 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; ++index;
} }
let progress = div.querySelector('.audio-waveform') as HTMLDivElement;
let onClick = () => { let onClick = () => {
if(!promise) { if(!promise) {
if(downloadDiv.classList.contains('downloading')) { if(downloadDiv.classList.contains('downloading')) {
@ -310,6 +318,7 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
let toggle = div.querySelector('.audio-toggle') as HTMLDivElement; let toggle = div.querySelector('.audio-toggle') as HTMLDivElement;
let interval = 0; let interval = 0;
let lastIndex = 0;
toggle.addEventListener('click', () => { toggle.addEventListener('click', () => {
if(audio.paused) { if(audio.paused) {
@ -327,7 +336,6 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
(Array.from(svg.children) as HTMLElement[]).forEach(node => node.classList.remove('active')); (Array.from(svg.children) as HTMLElement[]).forEach(node => node.classList.remove('active'));
let lastIndex = 0;
interval = setInterval(() => { interval = setInterval(() => {
if(lastIndex > svg.childElementCount || isNaN(audio.duration)) { if(lastIndex > svg.childElementCount || isNaN(audio.duration)) {
clearInterval(interval); clearInterval(interval);
@ -337,10 +345,11 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
// @ts-ignore // @ts-ignore
timeDiv.innerText = String(audio.currentTime | 0).toHHMMSS(true); 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].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; //++lastIndex;
//console.log('lastIndex:', lastIndex, audio.currentTime); //console.log('lastIndex:', lastIndex, audio.currentTime);
//}, duration * 1000 / svg.childElementCount | 0/* 63 * duration / 10 */); //}, duration * 1000 / svg.childElementCount | 0/* 63 * duration / 10 */);
@ -358,11 +367,51 @@ export function wrapAudio(doc: MTDocument, withTime = false): HTMLDivElement {
toggle.classList.add('tgico-largeplay'); toggle.classList.add('tgico-largeplay');
toggle.classList.remove('tgico-largepause'); toggle.classList.remove('tgico-largepause');
clearInterval(interval); clearInterval(interval);
(Array.from(svg.children) as HTMLElement[]).forEach(node => node.classList.remove('active'));
// @ts-ignore // @ts-ignore
timeDiv.innerText = String(audio.currentTime | 0).toHHMMSS(true); 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.append(source);
audio.style.display = 'none'; audio.style.display = 'none';
div.append(audio); div.append(audio);

451
src/lib/appManagers/appImManager.ts

@ -27,21 +27,35 @@ console.log('appImManager included!');
let testScroll = false; let testScroll = false;
class ScrollPosition { class ScrollPosition {
public previousScrollHeightMinusTop = 0; previousScrollHeightMinusTop = 0;
public readyFor = 'up'; readyFor = 'up';
public container: HTMLElement; container: HTMLElement;
rAF: number;
debug = true;
constructor(public node: HTMLElement) { constructor(node: HTMLElement) {
this.container = node.parentElement; this.container = node.parentElement;
} }
public restore() { restore() {
//console.log('scrollPosition restore 2', this.node.scrollHeight, (this.node.scrollHeight let setScrollTop = this.container.scrollHeight - this.previousScrollHeightMinusTop;
//- this.previousScrollHeightMinusTop) + 'px', this.container); 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')) { if(this.readyFor === 'up'/* || this.container.parentElement.classList.contains('scrolled-down') */) {
this.container.scrollTop = this.node.scrollHeight if(this.debug) appImManager.log('scrollPosition restore 2', this.readyFor, this.container.scrollHeight,
- this.previousScrollHeightMinusTop; 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 // 'down' doesn't need to be special cased unless the
@ -50,12 +64,130 @@ class ScrollPosition {
// a Facebook messages effect // a Facebook messages effect
} }
public prepareFor(direction: string) { prepareFor(direction = 'up') {
this.readyFor = direction || 'up'; if(this.rAF) {
this.previousScrollHeightMinusTop = this.node.scrollHeight window.cancelAnimationFrame(this.rAF);
- this.container.scrollTop; 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);
//console.log('scrollPosition prepareFor', direction, this.node.scrollHeight, this.previousScrollHeightMinusTop + 'px') 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');
}));
}
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 {
private setPeerPromise: Promise<boolean> = null; private setPeerPromise: Promise<boolean> = null;
public bubbleGroups = new BubbleGroups();
constructor() { constructor() {
this.log = logger('IM'); this.log = logger('IM');
@ -152,12 +286,14 @@ export class AppImManager {
this.myID = userAuth ? userAuth.id : 0; this.myID = userAuth ? userAuth.id : 0;
}); });
// will call when message is sent (only 1)
$rootScope.$on('history_append', (e: CustomEvent) => { $rootScope.$on('history_append', (e: CustomEvent) => {
let details = e.detail; let details = e.detail;
this.renderMessagesByIDs([details.messageID]); this.renderMessagesByIDs([details.messageID]);
}); });
// will call when sent for update pos
$rootScope.$on('history_update', (e: CustomEvent) => { $rootScope.$on('history_update', (e: CustomEvent) => {
let details = e.detail; let details = e.detail;
@ -169,6 +305,7 @@ export class AppImManager {
let message = appMessagesManager.getMessage(mid); let message = appMessagesManager.getMessage(mid);
//this.log('history_update', this.bubbles[mid], mid, message); //this.log('history_update', this.bubbles[mid], mid, message);
this.renderMessage(message, false, false, bubble); this.renderMessage(message, false, false, bubble);
this.deleteEmptySideDivs(); this.deleteEmptySideDivs();
@ -219,6 +356,8 @@ export class AppImManager {
bubble.classList.remove('is-sending'); bubble.classList.remove('is-sending');
bubble.classList.add('is-sent'); bubble.classList.add('is-sent');
this.bubbleGroups.removeBubble(bubble, tempID);
delete this.bubbles[tempID]; delete this.bubbles[tempID];
} else { } else {
this.log.warn('message_sent there is no bubble', e.detail); this.log.warn('message_sent there is no bubble', e.detail);
@ -363,9 +502,7 @@ export class AppImManager {
let ids = Object.keys(this.bubbles).map(k => +k).filter(id => { let ids = Object.keys(this.bubbles).map(k => +k).filter(id => {
let message = appMessagesManager.getMessage(id); let message = appMessagesManager.getMessage(id);
return message.media && (message.media.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)));
|| (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(); }).sort();
let idx = ids.findIndex(i => i == messageID); let idx = ids.findIndex(i => i == messageID);
@ -458,8 +595,7 @@ export class AppImManager {
if(!msgID) return; if(!msgID) return;
if(this.myID == this.peerID || if(this.myID == this.peerID || (this.peerID < 0 && !appPeersManager.isChannel(this.peerID) && !appPeersManager.isMegagroup(this.peerID))) {
(this.peerID < 0 && !appPeersManager.isChannel(this.peerID) && !appPeersManager.isMegagroup(this.peerID))) {
this.contextMenuPin.style.display = ''; this.contextMenuPin.style.display = '';
} else this.contextMenuPin.style.display = 'none'; } else this.contextMenuPin.style.display = 'none';
@ -646,40 +782,8 @@ export class AppImManager {
this.loadMediaQueueProcess(); 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[]> { public async loadMediaQueueProcess(): Promise<void[]> {
if(this.loadingMedia >= 5) return; if(this.loadingMedia >= 5/* || 1 == 1 */) return;
let item = this.loadMediaQueue.pop(); let item = this.loadMediaQueue.pop();
if(item) { if(item) {
@ -709,44 +813,10 @@ export class AppImManager {
return apiManager.invokeApi('account.updateStatus', {offline: this.offline}); return apiManager.invokeApi('account.updateStatus', {offline: this.offline});
} }
public onScroll() { public loadMoreHistory(top: boolean) {
let readed: number[] = [];
this.unreaded.forEachReverse((msgID, idx) => {
let bubble = this.bubbles[msgID];
if(isElementInViewport(bubble)) {
readed.push(msgID);
this.unreaded.splice(idx, 1);
}
});
lottieLoader.checkAnimations();
if(readed.length) {
let max = Math.max(...readed);
let min = Math.min(...readed);
if(this.peerID < 0) {
max = appMessagesIDsManager.getMessageIDInfo(max)[0];
min = appMessagesIDsManager.getMessageIDInfo(min)[0];
}
//appMessagesManager.readMessages(readed);
appMessagesManager.readHistory(this.peerID, max, min).catch((err: any) => {
this.log.error('readHistory err:', err);
appMessagesManager.readHistory(this.peerID, max, min);
});
}
if(this.scroll.scrollHeight - (this.scroll.scrollTop + this.scroll.offsetHeight) == 0/* <= 5 */) {
this.scroll.parentElement.classList.add('scrolled-down');
} else if(this.scroll.parentElement.classList.contains('scrolled-down')) {
this.scroll.parentElement.classList.remove('scrolled-down');
}
// load more history // load more history
if(!this.getHistoryPromise && !this.getHistoryTimeout && !testScroll) { // возможно нужно добавить разные таймауты для верха и низа
if(!this.getHistoryPromise && !this.getHistoryTimeout && this.peerID && !testScroll) {
this.getHistoryTimeout = setTimeout(() => { // must be this.getHistoryTimeout = setTimeout(() => { // must be
let history = Object.keys(this.bubbles).map(id => +id).sort(); let history = Object.keys(this.bubbles).map(id => +id).sort();
@ -765,32 +835,13 @@ export class AppImManager {
this.getHistoryTimeout = 0; this.getHistoryTimeout = 0;
let willLoad = false; if(!this.scrolledAll && top) {
if(!this.scrolledAll) { this.log('Will load more (up) history by id:', history[0], 'maxID:', history[history.length - 1], history);
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 /* false && */!testScroll && this.getHistory(history[0], true).then(() => { // uncomment
this.onScroll(); this.onScroll();
}).catch(err => { }).catch(err => {
this.log.warn('Could not load more history, err:', err); this.log.warn('Could not load more history, err:', err);
}); });
break;
}
}
} }
if(this.scrolledAllDown) return; if(this.scrolledAllDown) return;
@ -802,34 +853,62 @@ export class AppImManager {
} */ } */
// if scroll down after search // if scroll down after search
if(!willLoad && (!dialog || history.indexOf(dialog.top_message) === -1)) { if(!top && (!dialog || history.indexOf(dialog.top_message) === -1)) {
let lastMsgIDs = history.slice(-10); this.log('Will load more (down) history by maxID:', history[history.length - 1], history);
for(let msgID of lastMsgIDs) { /* false && */!testScroll && this.getHistory(history[history.length - 1], false, true).then(() => { // uncomment
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(); this.onScroll();
}).catch(err => { }).catch(err => {
this.log.warn('Could not load more history, err:', err); this.log.warn('Could not load more history, err:', err);
}); });
}
}, 0);
}
}
break; public onScroll() {
let readed: number[] = [];
this.unreaded.forEachReverse((msgID, idx) => {
let bubble = this.bubbles[msgID];
if(isElementInViewport(bubble)) {
readed.push(msgID);
this.unreaded.splice(idx, 1);
} }
});
lottieLoader.checkAnimations();
if(readed.length) {
let max = Math.max(...readed);
let min = Math.min(...readed);
if(this.peerID < 0) {
max = appMessagesIDsManager.getMessageIDInfo(max)[0];
min = appMessagesIDsManager.getMessageIDInfo(min)[0];
} }
//appMessagesManager.readMessages(readed);
appMessagesManager.readHistory(this.peerID, max, min).catch((err: any) => {
this.log.error('readHistory err:', err);
appMessagesManager.readHistory(this.peerID, max, min);
});
} }
}, 0);
if(this.scroll.scrollHeight - (this.scroll.scrollTop + this.scroll.offsetHeight) == 0/* <= 5 */) {
this.scroll.parentElement.classList.add('scrolled-down');
} else if(this.scroll.parentElement.classList.contains('scrolled-down')) {
this.scroll.parentElement.classList.remove('scrolled-down');
} }
} }
public setScroll() { 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.scroll = this.scrollable.container;
this.scrollable.setVirtualContainer(this.chatInner); this.scrollable.setVirtualContainer(this.chatInner);
this.scrollable.onScrolledTop = () => this.loadMoreHistory(true);
this.scrollable.onScrolledBottom = () => this.loadMoreHistory(false);
this.scrollPosition = new ScrollPosition(this.chatInner); this.scrollPosition = new ScrollPosition(this.chatInner);
this.scroll.addEventListener('scroll', this.onScroll.bind(this)); this.scroll.addEventListener('scroll', this.onScroll.bind(this));
@ -937,6 +1016,7 @@ export class AppImManager {
} }
this.bubbles = {}; this.bubbles = {};
this.dateMessages = {}; this.dateMessages = {};
this.bubbleGroups.cleanup();
this.unreaded = []; this.unreaded = [];
this.unreadOut = []; this.unreadOut = [];
this.loadMediaQueue = []; this.loadMediaQueue = [];
@ -984,7 +1064,8 @@ export class AppImManager {
if(dialog && lastMsgID == dialog.top_message) { if(dialog && lastMsgID == dialog.top_message) {
this.scroll.scrollTop = this.scroll.scrollHeight; this.scroll.scrollTop = this.scroll.scrollHeight;
} else { } else {
this.bubbles[lastMsgID].scrollIntoView(); //this.bubbles[lastMsgID].scrollIntoView();
this.scrollable.scrollIntoView(this.bubbles[lastMsgID]);
} }
return Promise.resolve(true); return Promise.resolve(true);
@ -1038,7 +1119,7 @@ export class AppImManager {
this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = title; this.titleEl.innerHTML = appSidebarRight.profileElements.name.innerHTML = title;
this.topbar.style.display = this.goDownBtn.style.display = ''; 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' : ''; this.chatInput.style.display = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID) ? 'none' : '';
@ -1048,6 +1129,8 @@ export class AppImManager {
this.chatInner.classList.remove('is-chat'); this.chatInner.classList.remove('is-chat');
} }
//this.scroll.scrollTop = this.scroll.scrollHeight;
return this.setPeerPromise = Promise.all([ return this.setPeerPromise = Promise.all([
this.getHistory(forwarding ? lastMsgID + 1 : lastMsgID).then(() => { this.getHistory(forwarding ? lastMsgID + 1 : lastMsgID).then(() => {
////this.log('setPeer removing preloader'); ////this.log('setPeer removing preloader');
@ -1068,18 +1151,18 @@ export class AppImManager {
this.scroll.scrollTop = this.scroll.scrollHeight; this.scroll.scrollTop = this.scroll.scrollHeight;
} }
} else if(dialog && dialog.top_message) { // add last message, bc in getHistory will load < max_id } 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(); this.preloader.detach();
//setTimeout(() => { //setTimeout(() => {
//appSidebarRight.fillProfileElements(); //appSidebarRight.fillProfileElements();
appSidebarRight.loadSidebarMedia(); appSidebarRight.loadSidebarMedia(true);
//}, 500); //}, 500);
return true; return true;
@ -1123,7 +1206,6 @@ export class AppImManager {
///////this.log('updateUnreadByDialog', maxID, dialog, this.unreadOut); ///////this.log('updateUnreadByDialog', maxID, dialog, this.unreadOut);
let length = this.unreadOut.length;
this.unreadOut.forEachReverse((msgID, idx) => { this.unreadOut.forEachReverse((msgID, idx) => {
if(msgID > 0 && msgID <= maxID) { if(msgID > 0 && msgID <= maxID) {
let bubble = this.bubbles[msgID]; let bubble = this.bubbles[msgID];
@ -1148,13 +1230,9 @@ export class AppImManager {
if(!(id in this.bubbles)) return; if(!(id in this.bubbles)) return;
let bubble = this.bubbles[id]; let bubble = this.bubbles[id];
let parent = bubble.parentNode as HTMLDivElement;
delete this.bubbles[id]; delete this.bubbles[id];
bubble.remove(); this.scrollable.removeElement(bubble);
//bubble.remove();
if(!parent.childNodes.length) {
parent.remove();
}
}); });
lottieLoader.checkAnimations(); lottieLoader.checkAnimations();
@ -1210,11 +1288,36 @@ export class AppImManager {
//bubble.innerHTML = ''; //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 // time section
let date = new Date(message.date * 1000); let date = new Date(message.date * 1000);
let time = ('0' + date.getHours()).slice(-2) + let time = ('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2);
':' + ('0' + date.getMinutes()).slice(-2);
if(message.views) { if(message.views) {
bubble.classList.add('channel-post'); bubble.classList.add('channel-post');
@ -1461,7 +1564,7 @@ export class AppImManager {
break; break;
} else if(doc.mime_type == 'video/mp4' && doc.size <= 20e6) { } 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') { if(doc.type == 'round') {
bubble.classList.add('round'); bubble.classList.add('round');
@ -1470,6 +1573,16 @@ export class AppImManager {
bubble.classList.add('video'); bubble.classList.add('video');
wrapVideo.call(this, doc, attachmentDiv, message, true, null, false, doc.type == 'round'); 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; break;
} else { } else {
let docDiv = wrapDocument(doc); let docDiv = wrapDocument(doc);
@ -1513,13 +1626,8 @@ export class AppImManager {
if(message.savedFrom) { if(message.savedFrom) {
let fwd = document.createElement('div'); let fwd = document.createElement('div');
fwd.classList.add('forward'/* , 'tgico-forward' */); fwd.classList.add('forward'/* , 'tgico-forward' */);
fwd.innerHTML = ` 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">
<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>`;
<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); bubbleContainer.append(fwd);
bubble.dataset.savedFrom = message.savedFrom; bubble.dataset.savedFrom = message.savedFrom;
} }
@ -1568,17 +1676,9 @@ export class AppImManager {
} else /* if(!message.reply_to_mid) */ { } else /* if(!message.reply_to_mid) */ {
bubble.classList.add('hide-name'); bubble.classList.add('hide-name');
} }
//bubble.prepend(avatarDiv);
/* if(messageDiv.nextElementSibling) {
bubble.insertBefore(avatarDiv, messageDiv.nextElementSibling);
} else { */
//}
} }
if(!our && this.peerID < 0 && if(!our && this.peerID < 0 && (!appPeersManager.isChannel(this.peerID) || appPeersManager.isMegagroup(this.peerID))) {
(!appPeersManager.isChannel(this.peerID) || appPeersManager.isMegagroup(this.peerID))) {
let avatarDiv = document.createElement('div'); let avatarDiv = document.createElement('div');
avatarDiv.classList.add('user-avatar'); avatarDiv.classList.add('user-avatar');
@ -1599,26 +1699,6 @@ export class AppImManager {
bubble.classList.add('hide-name'); 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'); bubble.classList.add(our ? 'is-out' : 'is-in');
if(updatePosition) { if(updatePosition) {
if(reverse) { if(reverse) {
@ -1627,6 +1707,8 @@ export class AppImManager {
this.scrollable.append(bubble); this.scrollable.append(bubble);
} }
this.bubbleGroups.addBubble(bubble, message, reverse);
let justDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); let justDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
let dateTimestamp = justDate.getTime(); let dateTimestamp = justDate.getTime();
if(!(dateTimestamp in this.dateMessages)) { if(!(dateTimestamp in this.dateMessages)) {
@ -1640,8 +1722,7 @@ export class AppImManager {
if(today < date) { if(today < date) {
str = 'Today'; str = 'Today';
} else { } else {
const months = ['January', 'February', 'March', 'April', 'May', 'June', const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
'July', 'August', 'September', 'October', 'November', 'December'];
str = justDate.getFullYear() == new Date().getFullYear() ? str = justDate.getFullYear() == new Date().getFullYear() ?
months[justDate.getMonth()] + ' ' + justDate.getDate() : months[justDate.getMonth()] + ' ' + justDate.getDate() :
justDate.toISOString().split('T')[0].split('-').reverse().join('.'); justDate.toISOString().split('T')[0].split('-').reverse().join('.');
@ -1664,6 +1745,8 @@ export class AppImManager {
this.scrollable.insertBefore(dateMessage.div, bubble); this.scrollable.insertBefore(dateMessage.div, bubble);
} }
} }
} else {
this.bubbleGroups.updateGroupByMessageID(message.mid);
} }
/* if(bubble.classList.contains('webpage')) { /* if(bubble.classList.contains('webpage')) {
@ -1675,8 +1758,6 @@ export class AppImManager {
if(!multipleRender) { if(!multipleRender) {
this.scrollPosition.restore(); // лагает из-за этого this.scrollPosition.restore(); // лагает из-за этого
} }
//this.log('history msg', message);
} }
// reverse means scroll up // reverse means scroll up
@ -1688,9 +1769,7 @@ export class AppImManager {
maxID = dialog.top_message/* + 1 */; maxID = dialog.top_message/* + 1 */;
} }
let loadCount = Object.keys(this.bubbles).length > 0 ? let loadCount = Object.keys(this.bubbles).length > 0 ? 20 : this.scrollable.container.parentElement.scrollHeight / 30 * 1.25 | 0;
20 :
this.scrollable.container.parentElement.scrollHeight / 30 * 1.25 | 0;
/* if(testScroll) { /* if(testScroll) {
loadCount = 1; loadCount = 1;
@ -1800,7 +1879,7 @@ export class AppImManager {
peer: inputPeer peer: inputPeer
}; };
let settings: any = { let settings = {
_: 'inputPeerNotifySettings', _: 'inputPeerNotifySettings',
flags: 0, flags: 0,
mute_until: 0 mute_until: 0
@ -1850,9 +1929,7 @@ export class AppImManager {
this.typingUsers[update.user_id] = peerID; this.typingUsers[update.user_id] = peerID;
if(!appUsersManager.hasUser(update.user_id)) { if(!appUsersManager.hasUser(update.user_id)) {
if(update.chat_id && if(update.chat_id && appChatsManager.hasChat(update.chat_id) && !appChatsManager.isChannel(update.chat_id)) {
appChatsManager.hasChat(update.chat_id) &&
!appChatsManager.isChannel(update.chat_id)) {
appProfileManager.getChatFull(update.chat_id); appProfileManager.getChatFull(update.chat_id);
} }

7
src/lib/appManagers/appSidebarLeft.ts

@ -87,9 +87,7 @@ class AppSidebarLeft {
}; };
constructor() { constructor() {
this.chatsPreloader = document.createElement('div'); this.chatsPreloader = putPreloader(null, true);
this.chatsPreloader.classList.add('preloader');
putPreloader(this.chatsPreloader);
//this.chatsContainer.append(this.chatsPreloader); //this.chatsContainer.append(this.chatsPreloader);
//this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5); //this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
@ -98,7 +96,6 @@ class AppSidebarLeft {
this.scroll.setVirtualContainer(appDialogsManager.chatList); this.scroll.setVirtualContainer(appDialogsManager.chatList);
this.scroll.onScrolledBottom = this.onChatsScroll.bind(this); this.scroll.onScrolledBottom = this.onChatsScroll.bind(this);
appDialogsManager.chatsHidden = this.scroll.hiddenElements; 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 = new Scrollable(this.chatsArchivedContainer as HTMLDivElement, false, true, 300, 'CLA');
this.scrollArchived.setVirtualContainer(appDialogsManager.chatListArchived); this.scrollArchived.setVirtualContainer(appDialogsManager.chatListArchived);
@ -134,7 +131,7 @@ class AppSidebarLeft {
for(let i = 0; i < 1000; ++i) { for(let i = 0; i < 1000; ++i) {
let li = document.createElement('li'); let li = document.createElement('li');
li.dataset.id = '' + i; 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); this.scroll.append(li);
} }
} }

131
src/lib/appManagers/appSidebarRight.ts

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

12
src/lib/ckin.js

@ -3,18 +3,6 @@
NodeList.prototype.forEach = Array.prototype.forEach; 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) { function stylePlayer(player, video) {
let skin = attachSkin(video.dataset.ckin); let skin = attachSkin(video.dataset.ckin);
player.classList.add(skin); player.classList.add(skin);

4
src/lib/lottieLoader.ts

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

22
src/lib/polyfill.ts

@ -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 { declare global {
interface Uint8Array { interface Uint8Array {
hex: string; hex: string;
@ -62,5 +79,10 @@ declare global {
interface Array<T> { interface Array<T> {
forEachReverse(callback: (value: T, index?: number, array?: Array<T>) => void): void; 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() {
export const $rootScope = { export const $rootScope = {
$broadcast: (name/* : string */, detail/*? : any */) => { $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(); //console.trace();
let myCustomEvent = new CustomEvent(name, {detail}); let myCustomEvent = new CustomEvent(name, {detail});
document.dispatchEvent(myCustomEvent); document.dispatchEvent(myCustomEvent);

40
src/scss/partials/_chat.scss

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

286
src/scss/partials/_rightSIdebar.scss

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

4
tsconfig.json

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

2
webpack.prod.js

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

Loading…
Cancel
Save