Browse Source

Refactored media viewer caption

master
morethanwords 3 years ago
parent
commit
cfd91e6a84
  1. 73
      src/components/appMediaViewer.ts
  2. 1
      src/components/chat/input.ts
  3. 2
      src/components/poll.ts
  4. 2
      src/components/privacySection.ts
  5. 4
      src/components/sidebarLeft/tabs/background.ts
  6. 2
      src/components/wrappers.ts
  7. 2
      src/helpers/dom/setInnerHTML.ts
  8. 2
      src/lib/appManagers/appAvatarsManager.ts
  9. 64
      src/lib/mediaPlayer.ts
  10. 4
      src/scss/partials/_chat.scss
  11. 24
      src/scss/partials/_ckin.scss
  12. 4
      src/scss/partials/_leftSidebar.scss
  13. 85
      src/scss/partials/_mediaViewer.scss
  14. 18
      src/scss/partials/_poll.scss

73
src/components/appMediaViewer.ts

@ -28,7 +28,7 @@ import ProgressivePreloader from "./preloader"; @@ -28,7 +28,7 @@ import ProgressivePreloader from "./preloader";
import Scrollable from "./scrollable";
import appSidebarRight from "./sidebarRight";
import SwipeHandler from "./swipeHandler";
import { months, ONE_DAY } from "../helpers/date";
import { ONE_DAY } from "../helpers/date";
import { SearchSuperContext } from "./appSearchSuper.";
import DEBUG from "../config/debug";
import appNavigationController from "./appNavigationController";
@ -45,6 +45,9 @@ import generatePathData from "../helpers/generatePathData"; @@ -45,6 +45,9 @@ import generatePathData from "../helpers/generatePathData";
import replaceContent from "../helpers/dom/replaceContent";
import PeerTitle from "./peerTitle";
import appMessagesIdsManager from "../lib/appManagers/appMessagesIdsManager";
import I18n, { i18n } from "../lib/langPack";
import { capitalizeFirstLetter } from "../helpers/string";
import setInnerHTML from "../helpers/dom/setInnerHTML";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
@ -353,6 +356,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType @@ -353,6 +356,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
protected async setMoverToTarget(target: HTMLElement, closing = false, fromRight = 0) {
if(this.videoPlayer) { // there could be a better place for it
this.wholeDiv.classList.remove('has-video-controls');
this.videoPlayer.removeListeners();
this.videoPlayer = undefined;
}
@ -859,19 +863,42 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType @@ -859,19 +863,42 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
const time = new Date(timestamp * 1000);
const now = date.getTime() / 1000;
const timeStr = time.getHours() + ':' + ('0' + time.getMinutes()).slice(-2);
let dateStr: string;
const timeEl = new I18n.IntlDateElement({
date: time,
options: {
hour: '2-digit',
minute: '2-digit'
}
}).element;
let dateEl: Node | string;
if((now - timestamp) < ONE_DAY && date.getDate() === time.getDate()) { // if the same day
dateStr = 'Today';
dateEl = i18n('Date.Today');
} else if((now - timestamp) < (ONE_DAY * 2) && (date.getDate() - 1) === time.getDate()) { // yesterday
dateStr = 'Yesterday';
dateEl = capitalizeFirstLetter(I18n.format('Yesterday', true));
} else if(date.getFullYear() !== time.getFullYear()) { // different year
dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate() + ', ' + time.getFullYear();
dateEl = new I18n.IntlDateElement({
date: time,
options: {
month: 'short',
day: 'numeric',
year: 'numeric'
}
}).element;
// dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate() + ', ' + time.getFullYear();
} else {
dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate();
dateEl = new I18n.IntlDateElement({
date: time,
options: {
month: 'short',
day: 'numeric'
}
}).element;
// dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate();
}
this.author.date.innerText = dateStr + ' at ' + timeStr;
this.author.date.innerHTML = '';
this.author.date.append(dateEl, ' ', i18n('ScheduleController.at'), ' ', timeEl);
replaceContent(this.author.nameEl, new PeerTitle({
peerId: fromId,
@ -882,7 +909,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType @@ -882,7 +909,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
let oldAvatar = this.author.avatarEl;
this.author.avatarEl = (this.author.avatarEl.cloneNode() as AvatarElement);
this.author.avatarEl.setAttribute('peer', '' + fromId);
this.author.avatarEl.setAttribute('peer', '' + (fromId || rootScope.myId));
oldAvatar.parentElement.replaceChild(this.author.avatarEl, oldAvatar);
}
@ -978,9 +1005,15 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType @@ -978,9 +1005,15 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
const mover = this.content.mover;
//const maxWidth = appPhotosManager.windowW - 16;
const maxWidth = mediaSizes.isMobile ? this.pageEl.scrollWidth : this.pageEl.scrollWidth - 16;
const maxWidth = this.pageEl.scrollWidth;
// TODO: const maxHeight = mediaSizes.isMobile ? appPhotosManager.windowH : appPhotosManager.windowH - 100;
const maxHeight = appPhotosManager.windowH - 120;
let padding = 0;
const windowH = appPhotosManager.windowH;
if(windowH < 1000000 && !mediaSizes.isMobile) {
padding = 32 + 120;
}
// const maxHeight = windowH - 120 - padding;
const maxHeight = windowH - 120;
let thumbPromise: Promise<any> = Promise.resolve();
const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight, mediaSizes.isMobile ? false : true).photoSize;
if(useContainerAsTarget) {
@ -1076,6 +1109,9 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType @@ -1076,6 +1109,9 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
}
const player = new VideoPlayer(video, true, media.supportsStreaming);
player.addEventListener('toggleControls', (show) => {
this.wholeDiv.classList.toggle('has-video-controls', show);
});
this.videoPlayer = player;
/* div.append(video);
mover.append(player.wrapper); */
@ -1459,16 +1495,19 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet @@ -1459,16 +1495,19 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|| (message.media.webpage && (message.media.webpage.document || message.media.webpage.photo));
}
private setCaption(message: any) {
private setCaption(message: Message.message) {
const caption = message.message;
this.content.caption.classList.toggle('hide', !caption);
let html = '';
if(caption) {
this.content.caption.firstElementChild.innerHTML = RichTextProcessor.wrapRichText(caption, {
html = RichTextProcessor.wrapRichText(caption, {
entities: message.totalEntities
});
} else {
this.content.caption.firstElementChild.innerHTML = '';
}
// html = 'Dandelion are a family of flowering plants that grow in many parts of the world.';
setInnerHTML(this.content.caption.firstElementChild, html);
this.content.caption.classList.toggle('hide', !caption);
// this.content.container.classList.toggle('with-caption', !!caption);
}
public setSearchContext(context: SearchSuperContext) {
@ -1497,8 +1536,8 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet @@ -1497,8 +1536,8 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
this.currentMessageId = mid;
this.currentPeerId = message.peerId;
const promise = super._openMedia(media, message.date, fromId, fromRight, target, reverse, prevTargets, nextTargets, needLoadMore);
this.setCaption(message);
const promise = super._openMedia(media, message.date, fromId, fromRight, target, reverse, prevTargets, nextTargets, needLoadMore);
return promise;
}

1
src/components/chat/input.ts

@ -810,6 +810,7 @@ export default class ChatInput { @@ -810,6 +810,7 @@ export default class ChatInput {
}
this.attachMenu.toggleAttribute('disabled', !visible.length);
this.attachMenu.classList.toggle('btn-disabled', !visible.length);
this.updateSendBtn();
}

2
src/components/poll.ts

@ -564,7 +564,7 @@ export default class PollElement extends HTMLElement { @@ -564,7 +564,7 @@ export default class PollElement extends HTMLElement {
*/
results.recent_voters/* .slice().reverse() */.forEach((userId, idx) => {
const style = idx === 0 ? '' : `style="transform: translateX(-${idx * 3}px);"`;
html += `<avatar-element class="avatar-16" dialog="0" peer="${userId}" ${style}></avatar-element>`;
html += `<avatar-element class="avatar-16 poll-avatar" dialog="0" peer="${userId}" ${style}></avatar-element>`;
});
this.avatarsDiv.innerHTML = html;
}

2
src/components/privacySection.ts

@ -245,7 +245,7 @@ export default class PrivacySection { @@ -245,7 +245,7 @@ export default class PrivacySection {
return peers;
}
private generateStr(peers: {users: number[], chats: number[]}): HTMLElement[] {
private generateStr(peers: {users: number[], chats: number[]}) {
if(!peers.users.length && !peers.chats.length) {
return [i18n('PrivacySettingsController.AddUsers')];
}

4
src/components/sidebarLeft/tabs/background.ts

@ -201,7 +201,9 @@ export default class AppBackgroundTab extends SliderSuperTab { @@ -201,7 +201,9 @@ export default class AppBackgroundTab extends SliderSuperTab {
};
private addWallPaper(wallpaper: WallPaper.wallPaper, append = true) {
if(wallpaper.pFlags.pattern || (wallpaper.document as MyDocument).mime_type.indexOf('application/') === 0) {
if(wallpaper.pFlags.pattern ||
!wallpaper.document ||
(wallpaper.document as MyDocument).mime_type.indexOf('application/') === 0) {
return;
}

2
src/components/wrappers.ts

@ -1287,7 +1287,7 @@ export function wrapLocalSticker({emoji, width, height}: { @@ -1287,7 +1287,7 @@ export function wrapLocalSticker({emoji, width, height}: {
return {container};
}
export function wrapReply(title: string | HTMLElement, subtitle: string | HTMLElement, message?: any) {
export function wrapReply(title: Parameters<ReplyContainer['fill']>[0], subtitle: Parameters<ReplyContainer['fill']>[1], message?: any) {
const replyContainer = new ReplyContainer('reply');
replyContainer.fill(title, subtitle, message);
/////////console.log('wrapReply', title, subtitle, media);

2
src/helpers/dom/setInnerHTML.ts

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
export default function setInnerHTML(elem: HTMLElement, html: string) {
export default function setInnerHTML(elem: Element, html: string) {
elem.setAttribute('dir', 'auto');
elem.innerHTML = html;
}

2
src/lib/appManagers/appAvatarsManager.ts

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import renderImageFromUrl, { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
import replaceContent from "../../helpers/dom/replaceContent";
import sequentialDom from "../../helpers/sequentialDom";
import { UserProfilePhoto, ChatPhoto, InputFileLocation } from "../../layer";

64
src/lib/mediaPlayer.ts

@ -13,6 +13,7 @@ import { cancelEvent } from "../helpers/dom/cancelEvent"; @@ -13,6 +13,7 @@ import { cancelEvent } from "../helpers/dom/cancelEvent";
import ListenerSetter from "../helpers/listenerSetter";
import ButtonMenu from "../components/buttonMenu";
import { ButtonMenuToggleHandler } from "../components/buttonMenuToggle";
import EventListenerBase from "../helpers/eventListenerBase";
export class MediaProgressLine extends RangeSelector {
private filledLoad: HTMLDivElement;
@ -167,7 +168,9 @@ export class MediaProgressLine extends RangeSelector { @@ -167,7 +168,9 @@ export class MediaProgressLine extends RangeSelector {
}
let lastVolume = 1, muted = !lastVolume;
export default class VideoPlayer {
export default class VideoPlayer extends EventListenerBase<{
toggleControls: (show: boolean) => void
}> {
private wrapper: HTMLDivElement;
private progress: MediaProgressLine;
private skin: 'default';
@ -178,6 +181,8 @@ export default class VideoPlayer { @@ -178,6 +181,8 @@ export default class VideoPlayer {
private videoWhichChild: number; */
constructor(private video: HTMLVideoElement, play = false, streamable = false, duration?: number) {
super(false);
this.wrapper = document.createElement('div');
this.wrapper.classList.add('ckin__player');
@ -308,39 +313,67 @@ export default class VideoPlayer { @@ -308,39 +313,67 @@ export default class VideoPlayer {
}
});
let showControlsTimeout = 0;
const hideControls = () => {
clearTimeout(showControlsTimeout);
showControlsTimeout = 0;
const showControls = () => {
if(showControlsTimeout) clearTimeout(showControlsTimeout);
else player.classList.add('show-controls');
if(this.video.paused || !player.classList.contains('show-controls')) {
return;
}
showControlsTimeout = window.setTimeout(() => {
this.dispatchEvent('toggleControls', false);
player.classList.remove('show-controls');
};
let showControlsTimeout = 0;
const showControls = (setHideTimeout = true) => {
if(showControlsTimeout) {
clearTimeout(showControlsTimeout);
showControlsTimeout = 0;
player.classList.remove('show-controls');
}, 3e3);
} else if(!player.classList.contains('show-controls')) {
this.dispatchEvent('toggleControls', true);
player.classList.add('show-controls');
}
if(!setHideTimeout) {
return;
}
showControlsTimeout = window.setTimeout(hideControls, 3e3);
};
const toggleControls = () => {
if(player.classList.contains('show-controls')) {
hideControls();
} else {
showControls();
}
};
if(isTouchSupported) {
this.listenerSetter.add(player)('click', () => {
showControls();
toggleControls();
});
this.listenerSetter.add(player)('touchstart', () => {
player.classList.add('show-controls');
clearTimeout(showControlsTimeout);
/* this.listenerSetter.add(player)('touchstart', () => {
showControls(false);
});
this.listenerSetter.add(player)('touchend', () => {
if(player.classList.contains('is-playing')) {
showControls();
}
});
}); */
} else {
this.listenerSetter.add(this.wrapper)('mousemove', () => {
showControls();
});
this.listenerSetter.add(document)('keydown', (e: KeyboardEvent) => {
if((e.target as HTMLElement) === document.activeElement) {
return;
}
if(e.code === 'KeyF') {
this.toggleFullScreen(fullScreenButton);
} else if(e.code === 'KeyM') {
@ -387,6 +420,10 @@ export default class VideoPlayer { @@ -387,6 +420,10 @@ export default class VideoPlayer {
this.listenerSetter.add(video)('play', () => {
this.wrapper.classList.add('played');
}, {once: true});
this.listenerSetter.add(video)('pause', () => {
showControls(false);
});
}
this.listenerSetter.add(video)('play', () => {
@ -546,6 +583,7 @@ export default class VideoPlayer { @@ -546,6 +583,7 @@ export default class VideoPlayer {
};
public removeListeners() {
super.cleanup();
this.listenerSetter.removeAll();
this.progress.removeListeners();
}

4
src/scss/partials/_chat.scss

@ -853,6 +853,10 @@ $chat-helper-size: 36px; @@ -853,6 +853,10 @@ $chat-helper-size: 36px;
padding: 0 38px 0 16px;
}
}
&.btn-disabled {
opacity: var(--disabled-opacity);
}
}
> div {

24
src/scss/partials/_ckin.scss

@ -38,7 +38,7 @@ @@ -38,7 +38,7 @@
color: #fff;
@include respond-to(handhelds) {
margin-left: 1rem;
margin-left: 1.125rem;
}
}
}
@ -95,9 +95,12 @@ @@ -95,9 +95,12 @@
left: 50%;
transform: translate3d(-50%, -50%, 0) scale(1);
font-size: 4rem;
transition: visibility .2s, opacity .2s;
touch-action: manipulation;
@include animation-level(2) {
transition: visibility var(--layer-transition), opacity var(--layer-transition);
}
@include respond-to(handhelds) {
font-size: 3rem;
}
@ -116,11 +119,14 @@ @@ -116,11 +119,14 @@
bottom: 0;
right: 0;
left: 0;
transition: transform .3s;
text-align: left;
direction: ltr;
z-index: 6;
@include animation-level(2) {
transition: transform var(--layer-transition);
}
.progress-line {
margin: 0 16px;
border-radius: var(--border-radius);
@ -132,6 +138,10 @@ @@ -132,6 +138,10 @@
&__loaded, & {
background: rgba(255, 255, 255, .38);
}
@include respond-to(handhelds) {
margin-bottom: -1px;
}
}
}
@ -146,8 +156,11 @@ @@ -146,8 +156,11 @@
position: absolute;
background-repeat: repeat-x;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
transition: transform .3s;
pointer-events: none;
@include animation-level(2) {
transition: transform var(--layer-transition);
}
}
&:not(.ckin__fullscreen) .default__gradient-bottom {
@ -403,7 +416,6 @@ input[type=range] { @@ -403,7 +416,6 @@ input[type=range] {
align-items: center;
@include respond-to(handhelds) {
height: 3.5rem;
padding: 0 .5rem;
height: 3.625rem;
}
}

4
src/scss/partials/_leftSidebar.scss

@ -205,6 +205,10 @@ @@ -205,6 +205,10 @@
display: none !important;
}
}
&:empty {
display: none;
}
}
.search-super-tabs-scrollable {

85
src/scss/partials/_mediaViewer.scss

@ -101,6 +101,10 @@ @@ -101,6 +101,10 @@
flex: 1 1 auto;
display: flex;
align-items: center;
/* &.with-caption {
padding: 4rem 0;
} */
}
&-media {
@ -116,38 +120,67 @@ @@ -116,38 +120,67 @@
overflow: hidden;
text-overflow: ellipsis;
z-index: 5;
bottom: 3rem;
left: 50%;
transform: translateX(-50%);
bottom: .75rem;
left: 0;
right: 0;
padding: 0 .5rem;
background-color: rgba(0, 0, 0, .6);
border-radius: 8px;
// background-color: rgba(0, 0, 0, .6);
// border-radius: 8px;
opacity: 0;
transition: opacity var(--open-duration);
line-height: var(--line-height);
white-space: pre-wrap;
@include respond-to(handhelds) {
border-radius: 0;
width: 100%;
@include animation-level(2) {
transition: opacity var(--open-duration);
}
> .scrollable {
padding: .5rem 0;
max-height: 10rem;
max-width: 1280px;
a {
color: #60a5e9 !important;
}
.scrollable {
padding: .5rem .875rem;
// max-height: 10rem;
max-height: 6rem;
max-width: 50rem;
height: 6rem;
position: relative;
margin: 0 auto;
}
.media-viewer-whole.active & {
opacity: 1;
@include hover() {
opacity: .2;
html.no-touch & {
opacity: .4;
&:hover {
opacity: 1;
//color: #fff;
}
}
html.is-touch & {
opacity: .6;
}
}
@include respond-to(handhelds) {
// border-radius: 0;
width: 100%;
transform: translateZ(0);
text-align: unset;
bottom: 1.0625rem;
.scrollable {
padding: 0 .5rem;
height: auto;
}
@include animation-level(2) {
transition: transform var(--layer-transition), opacity var(--open-duration) ease-in-out;
}
.media-viewer-whole.has-video-controls & {
transform: translate3d(0, -69px, 0);
}
}
}
@ -160,11 +193,11 @@ @@ -160,11 +193,11 @@
cursor: pointer;
z-index: 5;
@include hover() {
html.no-touch & {
height: calc(100% - 3.75rem);
&:hover {
> span {
.tgico-down {
opacity: 1;
}
}
@ -306,13 +339,15 @@ @@ -306,13 +339,15 @@
left: 50% !important;
top: 50% !important;
transform: translate3d(-50%, -50%, 0) !important;
max-width: calc(100vw - 16px);
max-height: calc((var(--vh, 1vh) * 100) - 120px);
max-width: 100vw;
// max-height: calc((var(--vh, 1vh) * 100) - 120px);
max-height: calc((var(--vh, 1vh) * 100) - 15rem);
@include respond-to(handhelds) {
width: 100% !important;
height: 100% !important;
max-width: 100vw !important;
max-height: calc((var(--vh, 1vh) * 100) - 120px);
// max-height: 100vh !important;
// TODO: max-height: calc((var(--vh, 1vh) * 100));
//height: calc(100% - 100px) !important;
@ -330,7 +365,7 @@ @@ -330,7 +365,7 @@
.ckin__player:not(.ckin__fullscreen) {
.default__controls/* ,
.default__gradient-bottom */ {
bottom: -61px;
bottom: -62px;
}
}
}
@ -487,13 +522,13 @@ @@ -487,13 +522,13 @@
}
}
&-switchers {
/* &-switchers {
position: relative;
width: $large-screen;
max-width: 100%;
height: 100%;
margin: 0 auto;
}
} */
}
.overlays {

18
src/scss/partials/_poll.scss

@ -8,12 +8,12 @@ poll-element { @@ -8,12 +8,12 @@ poll-element {
margin-top: -1px;
display: block;
//min-width: 280px;
min-width: 330px;
width: 330px;
user-select: none;
color: var(--primary-text-color);
@include respond-to(handhelds) {
min-width: 240px;
width: 240px;
}
&:not(.is-closed):not(.is-voted) .poll-answer {
@ -79,6 +79,13 @@ poll-element { @@ -79,6 +79,13 @@ poll-element {
margin-left: 18px;
}
&-avatar {
border: 1px solid var(--border-color);
cursor: pointer;
width: 18px;
height: 18px;
}
&-answer {
display: flex;
position: relative;
@ -258,13 +265,6 @@ poll-element { @@ -258,13 +265,6 @@ poll-element {
} */
}
avatar-element {
border: 1px solid var(--border-color);
cursor: pointer;
width: 18px;
height: 18px;
}
.circle-hover {
display: flex;
justify-content: center;

Loading…
Cancel
Save