Browse Source

Media viewer: fix mobile controls

master
Eduard Kuzmenko 3 years ago
parent
commit
730bc668e4
  1. 2
      .gitmodules
  2. 11
      src/components/appMediaViewer.ts
  3. 2
      src/lib/appManagers/apiUpdatesManager.ts
  4. 4
      src/lib/logger.ts
  5. 12
      src/lib/mediaPlayer.ts
  6. 35
      src/lib/mtproto/referenceDatabase.ts
  7. 74
      src/scss/partials/_ckin.scss
  8. 57
      src/scss/partials/_mediaViewer.scss

2
.gitmodules vendored

@ -4,6 +4,6 @@
[submodule "tweb-design"] [submodule "tweb-design"]
path = tweb-design path = tweb-design
url = https://github.com/morethanwords/tweb-design url = https://github.com/morethanwords/tweb-design
[submodule "rlottie-web"] [submodule "src/rlottie-web"]
path = src/rlottie-web path = src/rlottie-web
url = https://github.com/morethanwords/rlottie-web url = https://github.com/morethanwords/rlottie-web

11
src/components/appMediaViewer.ts

@ -117,16 +117,18 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
mainDiv.classList.add(MEDIA_VIEWER_CLASSNAME); mainDiv.classList.add(MEDIA_VIEWER_CLASSNAME);
const topbar = this.topbar = document.createElement('div'); const topbar = this.topbar = document.createElement('div');
topbar.classList.add(MEDIA_VIEWER_CLASSNAME + '-topbar'); topbar.classList.add(MEDIA_VIEWER_CLASSNAME + '-topbar', MEDIA_VIEWER_CLASSNAME + '-appear');
const topbarLeft = document.createElement('div'); const topbarLeft = document.createElement('div');
topbarLeft.classList.add(MEDIA_VIEWER_CLASSNAME + '-topbar-left'); topbarLeft.classList.add(MEDIA_VIEWER_CLASSNAME + '-topbar-left');
this.buttons['mobile-close'] = ButtonIcon('close', {onlyMobile: true}); this.buttons['mobile-close'] = ButtonIcon('close', {onlyMobile: true});
// * author // * author
this.author.container = document.createElement('div'); this.author.container = document.createElement('div');
this.author.container.classList.add(MEDIA_VIEWER_CLASSNAME + '-author', 'no-select'); this.author.container.classList.add(MEDIA_VIEWER_CLASSNAME + '-author', 'no-select');
const authorRight = document.createElement('div');
this.author.avatarEl = new AvatarElement(); this.author.avatarEl = new AvatarElement();
this.author.avatarEl.classList.add(MEDIA_VIEWER_CLASSNAME + '-userpic', 'avatar-44'); this.author.avatarEl.classList.add(MEDIA_VIEWER_CLASSNAME + '-userpic', 'avatar-44');
@ -137,7 +139,9 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
this.author.date = document.createElement('div'); this.author.date = document.createElement('div');
this.author.date.classList.add(MEDIA_VIEWER_CLASSNAME + '-date'); this.author.date.classList.add(MEDIA_VIEWER_CLASSNAME + '-date');
this.author.container.append(this.author.avatarEl, this.author.nameEl, this.author.date); authorRight.append(this.author.nameEl, this.author.date);
this.author.container.append(this.author.avatarEl, authorRight);
// * buttons // * buttons
const buttonsDiv = document.createElement('div'); const buttonsDiv = document.createElement('div');
@ -974,7 +978,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
//const maxWidth = appPhotosManager.windowW - 16; //const maxWidth = appPhotosManager.windowW - 16;
const maxWidth = mediaSizes.isMobile ? this.pageEl.scrollWidth : this.pageEl.scrollWidth - 16; const maxWidth = mediaSizes.isMobile ? this.pageEl.scrollWidth : this.pageEl.scrollWidth - 16;
const maxHeight = mediaSizes.isMobile ? appPhotosManager.windowH : appPhotosManager.windowH - 100; // TODO: const maxHeight = mediaSizes.isMobile ? appPhotosManager.windowH : appPhotosManager.windowH - 100;
const maxHeight = appPhotosManager.windowH - 120;
let thumbPromise: Promise<any> = Promise.resolve(); let thumbPromise: Promise<any> = Promise.resolve();
const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight, mediaSizes.isMobile ? false : true).photoSize; const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight, mediaSizes.isMobile ? false : true).photoSize;
if(useContainerAsTarget) { if(useContainerAsTarget) {

2
src/lib/appManagers/apiUpdatesManager.ts

@ -467,7 +467,7 @@ export class ApiUpdatesManager {
} }
break; break;
default: default:
if('channel_id' in update) { if('channel_id' in update && 'pts' in update) {
channelId = update.channel_id; channelId = update.channel_id;
} }
break; break;

4
src/lib/logger.ts

@ -21,8 +21,8 @@ function dT() {
return '[' + ((Date.now() - _logTimer) / 1000).toFixed(3) + ']'; return '[' + ((Date.now() - _logTimer) / 1000).toFixed(3) + ']';
} }
export function logger(prefix: string, type: LogTypes = LogTypes.Log | LogTypes.Warn | LogTypes.Error) { export function logger(prefix: string, type: LogTypes = LogTypes.Log | LogTypes.Warn | LogTypes.Error, ignoreDebugReset = false) {
if(!DEBUG/* || true */) { if(!DEBUG && !ignoreDebugReset/* || true */) {
type = LogTypes.Error; type = LogTypes.Error;
} }

12
src/lib/mediaPlayer.ts

@ -170,7 +170,7 @@ let lastVolume = 1, muted = !lastVolume;
export default class VideoPlayer { export default class VideoPlayer {
private wrapper: HTMLDivElement; private wrapper: HTMLDivElement;
private progress: MediaProgressLine; private progress: MediaProgressLine;
private skin: string; private skin: 'default';
private listenerSetter: ListenerSetter; private listenerSetter: ListenerSetter;
@ -186,10 +186,10 @@ export default class VideoPlayer {
video.parentNode.insertBefore(this.wrapper, video); video.parentNode.insertBefore(this.wrapper, video);
this.wrapper.appendChild(video); this.wrapper.appendChild(video);
this.skin = video.dataset.ckin ?? 'default'; this.skin = 'default';
this.stylePlayer(duration); this.stylePlayer(duration);
this.setBtnMenuToggle(); // this.setBtnMenuToggle();
if(this.skin === 'default') { if(this.skin === 'default') {
const controls = this.wrapper.querySelector('.default__controls.ckin__controls') as HTMLDivElement; const controls = this.wrapper.querySelector('.default__controls.ckin__controls') as HTMLDivElement;
@ -402,7 +402,7 @@ export default class VideoPlayer {
} }
} }
public togglePlay() { protected togglePlay() {
this.video[this.video.paused ? 'play' : 'pause'](); this.video[this.video.paused ? 'play' : 'pause']();
} }
@ -450,7 +450,7 @@ export default class VideoPlayer {
return !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement); return !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement);
} }
public toggleFullScreen(fullScreenButton: HTMLElement) { protected toggleFullScreen(fullScreenButton: HTMLElement) {
// alternative standard method // alternative standard method
const player = this.wrapper; const player = this.wrapper;
@ -533,7 +533,7 @@ export default class VideoPlayer {
} }
} }
onFullScreen = () => { protected onFullScreen = () => {
// @ts-ignore // @ts-ignore
const isFullscreenNow = document.webkitFullscreenElement !== null; const isFullscreenNow = document.webkitFullscreenElement !== null;
if(!isFullscreenNow) { if(!isFullscreenNow) {

35
src/lib/mtproto/referenceDatabase.ts

@ -12,6 +12,7 @@ import { deepEqual } from "../../helpers/object";
import { MOUNT_CLASS_TO } from "../../config/debug"; import { MOUNT_CLASS_TO } from "../../config/debug";
import apiManager from "./mtprotoworker"; import apiManager from "./mtprotoworker";
import assumeType from "../../helpers/assumeType"; import assumeType from "../../helpers/assumeType";
import { logger } from "../logger";
export type ReferenceContext = ReferenceContext.referenceContextProfilePhoto | ReferenceContext.referenceContextMessage; export type ReferenceContext = ReferenceContext.referenceContextProfilePhoto | ReferenceContext.referenceContextMessage;
export namespace ReferenceContext { export namespace ReferenceContext {
@ -36,21 +37,20 @@ class ReferenceDatabase {
private contexts: Map<ReferenceBytes, ReferenceContexts> = new Map(); private contexts: Map<ReferenceBytes, ReferenceContexts> = new Map();
//private references: Map<ReferenceBytes, number[]> = new Map(); //private references: Map<ReferenceBytes, number[]> = new Map();
private links: {[hex: string]: ReferenceBytes} = {}; private links: {[hex: string]: ReferenceBytes} = {};
private log = logger('RD', undefined, true);
constructor() { constructor() {
apiManager.addTaskListener('refreshReference', (task: RefreshReferenceTask) => { apiManager.addTaskListener('refreshReference', (task: RefreshReferenceTask) => {
const bytes = task.payload; const originalPayload = task.payload;
assumeType<RefreshReferenceTaskResponse>(task); assumeType<RefreshReferenceTaskResponse>(task);
task.originalPayload = bytes; task.originalPayload = originalPayload;
this.refreshReference(bytes).then(() => { this.refreshReference(originalPayload).then((bytes) => {
task.payload = this.getReferenceByLink(bytes); task.payload = bytes;
apiManager.postMessage(task);
}, (err) => { }, (err) => {
task.error = err; task.error = err;
apiManager.postMessage(task); }).then(() => apiManager.postMessage(task));
});
}); });
} }
@ -103,10 +103,12 @@ class ReferenceDatabase {
return false; return false;
} }
public refreshReference(reference: ReferenceBytes, context?: ReferenceContext): Promise<void> { public refreshReference(reference: ReferenceBytes, context?: ReferenceContext): Promise<Uint8Array | number[]> {
this.log('refreshReference: start', reference.slice(), context);
if(!context) { if(!context) {
const c = this.getContext(reference); const c = this.getContext(reference);
if(!c) { if(!c) {
this.log('refreshReference: got no context for reference:', reference.slice());
return Promise.reject('NO_CONTEXT'); return Promise.reject('NO_CONTEXT');
} }
@ -124,16 +126,18 @@ class ReferenceDatabase {
} }
default: { default: {
console.warn('FILE_REFERENCE_EXPIRED: not implemented context', context); this.log.warn('refreshReference: not implemented context', context);
return Promise.reject(); return Promise.reject();
} }
} }
const hex = bytesToHex(reference); const hex = bytesToHex(reference);
this.log('refreshReference: refreshing reference:', hex);
return promise.then(() => { return promise.then(() => {
const newHex = bytesToHex(reference); const newHex = bytesToHex(reference);
this.log('refreshReference: refreshed, reference before:', hex, 'after:', newHex);
if(hex !== newHex) { if(hex !== newHex) {
return; return reference;
} }
this.deleteContext(reference, context); this.deleteContext(reference, context);
@ -147,17 +151,6 @@ class ReferenceDatabase {
}); });
} }
/* handleReferenceError = (reference: ReferenceBytes, error: ApiError) => {
switch(error.type) {
case 'FILE_REFERENCE_EXPIRED': {
return this.refreshReference(reference);
}
default:
return Promise.reject(error);
}
}; */
/* public replaceReference(oldReference: ReferenceBytes, newReference: ReferenceBytes) { /* public replaceReference(oldReference: ReferenceBytes, newReference: ReferenceBytes) {
const contexts = this.contexts.get(oldReference); const contexts = this.contexts.get(oldReference);
if(contexts) { if(contexts) {

74
src/scss/partials/_ckin.scss

@ -42,18 +42,6 @@
} }
} }
} }
&__overlay {
position: relative;
&:before {
background: radial-gradient(ellipse at center, transparent 0%, rgba(0, 0, 0, .5) 100%);
}
&--2:before {
background: rgba(24, 24, 24, .8);
}
}
} }
.default { .default {
@ -71,7 +59,7 @@
cursor: pointer; cursor: pointer;
} }
&:before { /* &:before {
content: ''; content: '';
position: absolute; position: absolute;
top: 0; top: 0;
@ -81,27 +69,11 @@
transition: opacity .2s; transition: opacity .2s;
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
} } */
&__title {
position: absolute;
left: 20px;
top: 20px;
z-index: 1;
font-size: 24px;
color: rgba(255, 255, 255, .8);
font-style: italic;
}
&__button { &__button {
width: 2rem;
height: 2rem;
color: #fff; color: #fff;
padding: 0 .625rem; padding: .375rem;
@include respond-to(handhelds) {
font-size: 1.375rem;
}
i { i {
align-self: center; align-self: center;
@ -139,11 +111,6 @@
} }
} }
&__slider {
width: 10px;
height: 30px;
}
&__controls { &__controls {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@ -183,6 +150,12 @@
pointer-events: none; pointer-events: none;
} }
&:not(.ckin__fullscreen) .default__gradient-bottom {
@include respond-to(handhelds) {
display: none;
}
}
.toggle:before { .toggle:before {
content: $tgico-play; content: $tgico-play;
} }
@ -194,6 +167,10 @@
.default__controls { .default__controls {
transform: translate3d(0, 52px, 0); transform: translate3d(0, 52px, 0);
@include respond-to(handhelds) {
transform: translate3d(0, 65px, 0);
}
} }
/* html.no-touch &:hover,*/ /* html.no-touch &:hover,*/
@ -240,15 +217,11 @@
&__icon { &__icon {
fill: #fff; fill: #fff;
width: 1.5rem; width: 2.25rem;
height: 1.5rem; height: 2.25rem;
margin-right: .5rem; margin-right: .5rem;
cursor: pointer; cursor: pointer;
padding: .375rem;
@include respond-to(handhelds) {
width: 1.25rem;
height: 1.25rem;
}
} }
.progress-line { .progress-line {
@ -416,26 +389,21 @@ input[type=range] {
} }
} }
.left-controls { .left-controls,
.right-controls {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.right-controls {
display: flex;
align-items: center;
float: right;
}
.bottom-controls { .bottom-controls {
padding: 0 .25rem; padding: 0 .625rem;
height: 2rem; height: 2.25rem;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
@include respond-to(handhelds) { @include respond-to(handhelds) {
height: 3.5rem; height: 3.5rem;
padding: 0 .375rem; padding: 0 .5rem;
} }
} }

57
src/scss/partials/_mediaViewer.scss

@ -23,6 +23,8 @@
&-author { &-author {
height: 100%; height: 100%;
cursor: pointer; cursor: pointer;
display: flex;
align-items: center;
@include respond-to(handhelds) { @include respond-to(handhelds) {
margin-left: 1.5rem; margin-left: 1.5rem;
@ -30,9 +32,7 @@
} }
&-userpic { &-userpic {
position: absolute; margin-right: 1rem;
top: .5rem;
left: 1.25rem;
@include respond-to(handhelds) { @include respond-to(handhelds) {
display: none; display: none;
@ -43,6 +43,10 @@
line-height: var(--line-height); line-height: var(--line-height);
font-weight: 500; font-weight: 500;
margin: .0625rem 0; margin: .0625rem 0;
@include respond-to(not-handhelds) {
margin: .0625rem 0 .125rem;
}
} }
&-date { &-date {
@ -56,7 +60,7 @@
right: 0; right: 0;
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
padding: .5rem; padding: .5rem .75rem;
.btn-icon { .btn-icon {
margin: 0 .25rem; margin: 0 .25rem;
@ -220,7 +224,10 @@
overflow: hidden; overflow: hidden;
transform: translateZ(0) scale3d(1, 1, 1); transform: translateZ(0) scale3d(1, 1, 1);
border-radius: 0; border-radius: 0;
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix border-radius overflow
&:not(.center) {
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix border-radius overflow
}
// эти значения должны быть такими же, как при установке maxWidth и maxHeight в openMedia! // эти значения должны быть такими же, как при установке maxWidth и maxHeight в openMedia!
//max-width: 100%; //max-width: 100%;
@ -300,13 +307,13 @@
top: 50% !important; top: 50% !important;
transform: translate3d(-50%, -50%, 0) !important; transform: translate3d(-50%, -50%, 0) !important;
max-width: calc(100vw - 16px); max-width: calc(100vw - 16px);
max-height: calc((var(--vh, 1vh) * 100) - 100px); max-height: calc((var(--vh, 1vh) * 100) - 120px);
@include respond-to(handhelds) { @include respond-to(handhelds) {
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;
max-width: 100vw !important; max-width: 100vw !important;
max-height: 100vh !important; // max-height: 100vh !important;
// TODO: max-height: calc((var(--vh, 1vh) * 100)); // TODO: max-height: calc((var(--vh, 1vh) * 100));
//height: calc(100% - 100px) !important; //height: calc(100% - 100px) !important;
/* height: calc(100% - 50px) !important; /* height: calc(100% - 50px) !important;
@ -320,15 +327,12 @@
max-height: calc(100% - 100px); max-height: calc(100% - 100px);
} */ } */
/* .ckin__player:not(.ckin__fullscreen) { .ckin__player:not(.ckin__fullscreen) {
.default__controls { .default__controls/* ,
bottom: -50px; .default__gradient-bottom */ {
bottom: -61px;
} }
}
.default__gradient-bottom {
bottom: -50px;
}
} */
} }
/* @include respond-to(handhelds) { /* @include respond-to(handhelds) {
@ -341,9 +345,10 @@
} }
} */ } */
img:not(.thumbnail), video { img:not(.thumbnail),
height: auto; video {
width: auto; /* height: auto;
width: auto; */
object-fit: contain; object-fit: contain;
//max-height: calc(100% - 100px); //max-height: calc(100% - 100px);
} }
@ -362,6 +367,12 @@
} }
} }
&-appear {
opacity: 0;
visibility: hidden;
transition: opacity var(--open-duration) 0s, visibility 0s var(--open-duration);
}
&-topbar { &-topbar {
position: absolute; position: absolute;
top: 0; top: 0;
@ -372,7 +383,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
z-index: 5; z-index: 5;
padding: 0 .5rem; padding: 0 1.25rem;
.btn-icon, .media-viewer-author { .btn-icon, .media-viewer-author {
color: #8b8b8b; color: #8b8b8b;
@ -392,12 +403,13 @@
} }
@include respond-to(handhelds) { @include respond-to(handhelds) {
padding: 0 .5rem;
// transform: translateY(-3.5rem); // transform: translateY(-3.5rem);
// background: rgba(0, 0, 0, .2); // background: rgba(0, 0, 0, .2);
@include animation-level(2) { /* @include animation-level(2) {
transition: transform var(--open-duration) ease-in-out; transition: transform var(--open-duration) ease-in-out;
} } */
} }
} }
@ -442,7 +454,8 @@
visibility: visible; visibility: visible;
transition-delay: 0s; transition-delay: 0s;
.overlays/* , .overlays,
.media-viewer-appear/* ,
> .btn-icon */ { > .btn-icon */ {
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;

Loading…
Cancel
Save