Merge branch 'liteMode'
This commit is contained in:
commit
9e0f20c766
|
@ -4,6 +4,7 @@
|
|||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import type {LiteModeKey} from '../helpers/liteMode';
|
||||
import {CustomEmojiElement, CustomEmojiRendererElement} from '../lib/richTextProcessor/wrapRichText';
|
||||
import rootScope from '../lib/rootScope';
|
||||
import {IS_SAFARI} from '../environment/userAgent';
|
||||
|
@ -25,6 +26,7 @@ export interface AnimationItem {
|
|||
el: HTMLElement,
|
||||
group: AnimationItemGroup,
|
||||
animation: AnimationItemWrapper,
|
||||
liteModeKey?: LiteModeKey,
|
||||
controlled?: boolean | Middleware
|
||||
};
|
||||
|
||||
|
@ -34,6 +36,7 @@ export interface AnimationItemWrapper {
|
|||
pause: () => any;
|
||||
play: () => any;
|
||||
autoplay: boolean;
|
||||
loop: boolean | number;
|
||||
// onVisibilityChange?: (visible: boolean) => boolean;
|
||||
};
|
||||
|
||||
|
@ -176,12 +179,14 @@ export class AnimationIntersector {
|
|||
}
|
||||
}
|
||||
|
||||
public addAnimation(
|
||||
public addAnimation(options: {
|
||||
animation: AnimationItem['animation'],
|
||||
group: AnimationItemGroup = '',
|
||||
group?: AnimationItemGroup,
|
||||
observeElement?: HTMLElement,
|
||||
controlled?: AnimationItem['controlled']
|
||||
) {
|
||||
controlled?: AnimationItem['controlled'],
|
||||
liteModeKey?: LiteModeKey
|
||||
}) {
|
||||
let {animation, group = '', observeElement, controlled, liteModeKey} = options;
|
||||
if(group === 'none' || this.byPlayer.has(animation)) {
|
||||
return;
|
||||
}
|
||||
|
@ -202,7 +207,8 @@ export class AnimationIntersector {
|
|||
el: observeElement,
|
||||
animation: animation,
|
||||
group,
|
||||
controlled
|
||||
controlled,
|
||||
liteModeKey
|
||||
};
|
||||
|
||||
if(controlled && typeof(controlled) !== 'boolean') {
|
||||
|
|
|
@ -58,6 +58,7 @@ import clamp from '../helpers/number/clamp';
|
|||
import debounce from '../helpers/schedulers/debounce';
|
||||
import isBetween from '../helpers/number/isBetween';
|
||||
import findUpAsChild from '../helpers/dom/findUpAsChild';
|
||||
import liteMode from '../helpers/liteMode';
|
||||
|
||||
const ZOOM_STEP = 0.5;
|
||||
const ZOOM_INITIAL_VALUE = 1;
|
||||
|
@ -866,7 +867,7 @@ export default class AppMediaViewerBase<
|
|||
|
||||
const wasActive = fromRight !== 0;
|
||||
|
||||
const delay = rootScope.settings.animationsEnabled ? (wasActive ? 350 : 200) : 0;
|
||||
const delay = liteMode.isAvailable('animations') ? (wasActive ? 350 : 200) : 0;
|
||||
// let delay = wasActive ? 350 : 10000;
|
||||
|
||||
/* if(wasActive) {
|
||||
|
|
|
@ -37,6 +37,7 @@ import hasRights from '../lib/appManagers/utils/chats/hasRights';
|
|||
import getDialogIndex from '../lib/appManagers/utils/dialogs/getDialogIndex';
|
||||
import {generateDelimiter} from './generateDelimiter';
|
||||
import SettingSection from './settingSection';
|
||||
import liteMode from '../helpers/liteMode';
|
||||
|
||||
type SelectSearchPeerType = 'contacts' | 'dialogs' | 'channelParticipants';
|
||||
|
||||
|
@ -678,7 +679,7 @@ export default class AppSelectPeers {
|
|||
this.onChange && this.onChange(this.selected.size);
|
||||
};
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
if(liteMode.isAvailable('animations')) {
|
||||
div.addEventListener('animationend', onAnimationEnd, {once: true});
|
||||
} else {
|
||||
onAnimationEnd();
|
||||
|
|
|
@ -12,6 +12,7 @@ import appNavigationController, {NavigationItem} from '../appNavigationControlle
|
|||
import SetTransition from '../singleTransition';
|
||||
import AutocompleteHelperController from './autocompleteHelperController';
|
||||
import safeAssign from '../../helpers/object/safeAssign';
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
|
||||
export default class AutocompleteHelper extends EventListenerBase<{
|
||||
hidden: () => void,
|
||||
|
@ -51,21 +52,19 @@ export default class AutocompleteHelper extends EventListenerBase<{
|
|||
|
||||
this.attachNavigation();
|
||||
|
||||
this.controller && this.controller.addHelper(this);
|
||||
this.controller?.addHelper(this);
|
||||
}
|
||||
|
||||
public toggleListNavigation(enabled: boolean) {
|
||||
if(enabled) {
|
||||
this.attach && this.attach();
|
||||
this.attach?.();
|
||||
} else {
|
||||
this.detach && this.detach();
|
||||
this.detach?.();
|
||||
}
|
||||
}
|
||||
|
||||
protected onVisible = () => {
|
||||
if(this.detach) { // it can be so because 'visible' calls before animation's end
|
||||
this.detach();
|
||||
}
|
||||
this.detach?.(); // it can be so because 'visible' calls before animation's end
|
||||
|
||||
const list = this.list;
|
||||
const {attach, detach, resetTarget} = attachListNavigation({
|
||||
|
@ -143,9 +142,7 @@ export default class AutocompleteHelper extends EventListenerBase<{
|
|||
this.controller.hideOtherHelpers();
|
||||
}
|
||||
|
||||
if(this.detach) { // force detach here
|
||||
this.detach();
|
||||
}
|
||||
this.detach?.(); // force detach here
|
||||
}
|
||||
|
||||
const useRafs = this.controller || hide ? 0 : 2;
|
||||
|
@ -158,7 +155,7 @@ export default class AutocompleteHelper extends EventListenerBase<{
|
|||
element: this.container,
|
||||
className: 'is-visible',
|
||||
forwards: !hide,
|
||||
duration: rootScope.settings.animationsEnabled && !skipAnimation ? 300 : 0,
|
||||
duration: liteMode.isAvailable('animations') && !skipAnimation ? 300 : 0,
|
||||
onTransitionEnd: () => {
|
||||
this.hidden && this.dispatchEvent('hidden');
|
||||
},
|
||||
|
|
|
@ -44,7 +44,7 @@ import findUpTag from '../../helpers/dom/findUpTag';
|
|||
import {toast, toastNew} from '../toast';
|
||||
import {getMiddleware, Middleware} from '../../helpers/middleware';
|
||||
import cancelEvent from '../../helpers/dom/cancelEvent';
|
||||
import {attachClickEvent, detachClickEvent, simulateClickEvent} from '../../helpers/dom/clickEvent';
|
||||
import {attachClickEvent, simulateClickEvent} from '../../helpers/dom/clickEvent';
|
||||
import htmlToDocumentFragment from '../../helpers/dom/htmlToDocumentFragment';
|
||||
import reflowScrollableElement from '../../helpers/dom/reflowScrollableElement';
|
||||
import replaceContent from '../../helpers/dom/replaceContent';
|
||||
|
@ -132,6 +132,7 @@ import wrapPeerTitle from '../wrappers/peerTitle';
|
|||
import {PopupPeerCheckboxOptions} from '../popups/peer';
|
||||
import toggleDisability from '../../helpers/dom/toggleDisability';
|
||||
import {copyTextToClipboard} from '../../helpers/clipboard';
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
|
||||
export const USER_REACTIONS_INLINE = false;
|
||||
const USE_MEDIA_TAILS = false;
|
||||
|
@ -1048,7 +1049,7 @@ export default class ChatBubbles {
|
|||
this.listenerSetter.add(rootScope)('history_append', async({storageKey, message}) => {
|
||||
if(storageKey !== this.chat.messagesStorageKey || this.chat.type === 'scheduled') return;
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
if(liteMode.isAvailable('chat_background')) {
|
||||
this.updateGradient = true;
|
||||
}
|
||||
|
||||
|
@ -4463,6 +4464,7 @@ export default class ChatBubbles {
|
|||
group: this.chat.animationGroup,
|
||||
// play: !!message.pending || !multipleRender,
|
||||
play: true,
|
||||
liteModeKey: 'stickers_chat',
|
||||
loop: true,
|
||||
emoji: isEmoji ? messageMessage : undefined,
|
||||
withThumb: true,
|
||||
|
@ -5534,7 +5536,8 @@ export default class ChatBubbles {
|
|||
play: true,
|
||||
loop: true,
|
||||
withThumb: true,
|
||||
loadPromises
|
||||
loadPromises,
|
||||
liteModeKey: 'stickers_chat'
|
||||
});
|
||||
|
||||
attachClickEvent(stickerDiv, (e) => {
|
||||
|
@ -6232,7 +6235,7 @@ export default class ChatBubbles {
|
|||
|
||||
const waitPromise = isAdditionRender ? processPromise(resultPromise) : promise;
|
||||
|
||||
if(isFirstMessageRender && rootScope.settings.animationsEnabled/* && false */) {
|
||||
if(isFirstMessageRender && liteMode.isAvailable('animations')/* && false */) {
|
||||
let times = isAdditionRender ? 2 : 1;
|
||||
this.messagesQueueOnRenderAdditional = () => {
|
||||
this.log('messagesQueueOnRenderAdditional');
|
||||
|
|
|
@ -37,6 +37,7 @@ import indexOfAndSplice from '../../helpers/array/indexOfAndSplice';
|
|||
import {Message, WallPaper} from '../../layer';
|
||||
import animationIntersector, {AnimationItemGroup} from '../animationIntersector';
|
||||
import {getColorsFromWallPaper} from '../../helpers/color';
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
|
||||
export type ChatType = 'chat' | 'pinned' | 'discussion' | 'scheduled';
|
||||
|
||||
|
@ -211,7 +212,7 @@ export default class Chat extends EventListenerBase<{
|
|||
gradientCanvas = this.gradientCanvas = canvas;
|
||||
gradientCanvas.classList.add('chat-background-item-canvas', 'chat-background-item-color-canvas');
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
if(liteMode.isAvailable('animations')) {
|
||||
gradientRenderer.scrollAnimate(true);
|
||||
}
|
||||
// } else {
|
||||
|
|
|
@ -307,7 +307,7 @@ export default class ChatBackgroundGradientRenderer {
|
|||
this.update();
|
||||
}
|
||||
|
||||
public update() {
|
||||
private update() {
|
||||
if(this._colors.length < 2) {
|
||||
const color = this._colors[0];
|
||||
this._ctx.fillStyle = `rgb(${color.r}, ${color.g}, ${color.b})`;
|
||||
|
|
|
@ -21,6 +21,7 @@ import RLottiePlayer from '../../lib/rlottie/rlottiePlayer';
|
|||
import {fastRaf} from '../../helpers/schedulers';
|
||||
import noop from '../../helpers/noop';
|
||||
import {Middleware} from '../../helpers/middleware';
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
|
||||
const CLASS_NAME = 'reaction';
|
||||
const TAG_NAME = CLASS_NAME + '-element';
|
||||
|
@ -186,6 +187,10 @@ export default class ReactionElement extends HTMLElement {
|
|||
}
|
||||
|
||||
public fireAroundAnimation() {
|
||||
if(!liteMode.isAvailable('effects_reactions')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reaction = this.reactionCount.reaction;
|
||||
if(reaction._ !== 'reactionEmoji') return;
|
||||
callbackify(this.managers.appReactionsManager.getReaction(reaction.emoticon), (availableReaction) => {
|
||||
|
|
|
@ -145,7 +145,7 @@ export default class ReactionsElement extends HTMLElement {
|
|||
const totalReactions = counts.reduce((acc, c) => acc + c.count, 0);
|
||||
const canRenderAvatars = reactions && (!!reactions.pFlags.can_see_list || this.message.peerId.isUser()) && totalReactions < REACTION_DISPLAY_BLOCK_COUNTER_AT;
|
||||
this.sorted = counts.map((reactionCount, idx) => {
|
||||
let reactionElement = this.sorted.find((reactionElement) => reactionsEqual(reactionElement.reactionCount.reaction, reactionCount.reaction));
|
||||
let reactionElement: ReactionElement = this.sorted.find((reactionElement) => reactionsEqual(reactionElement.reactionCount.reaction, reactionCount.reaction));
|
||||
if(!reactionElement) {
|
||||
const middlewareHelper = this.middleware.create();
|
||||
reactionElement = new ReactionElement();
|
||||
|
|
|
@ -11,6 +11,7 @@ import callbackify from '../../helpers/callbackify';
|
|||
import {attachClickEvent} from '../../helpers/dom/clickEvent';
|
||||
import findUpClassName from '../../helpers/dom/findUpClassName';
|
||||
import getVisibleRect from '../../helpers/dom/getVisibleRect';
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
import {getMiddleware} from '../../helpers/middleware';
|
||||
import noop from '../../helpers/noop';
|
||||
import {fastRaf} from '../../helpers/schedulers';
|
||||
|
@ -136,7 +137,7 @@ export class ChatReactionsMenu {
|
|||
};
|
||||
|
||||
private canUseAnimations() {
|
||||
return rootScope.settings.animationsEnabled && !IS_MOBILE;
|
||||
return liteMode.isAvailable('animations') && liteMode.isAvailable('stickers_chat') && !IS_MOBILE;
|
||||
}
|
||||
|
||||
private renderReaction(reaction: AvailableReaction) {
|
||||
|
@ -184,6 +185,7 @@ export class ChatReactionsMenu {
|
|||
wrapSticker({
|
||||
doc: reaction.static_icon,
|
||||
div: appearWrapper,
|
||||
liteModeKey: false,
|
||||
...options
|
||||
});
|
||||
} else {
|
||||
|
@ -192,6 +194,7 @@ export class ChatReactionsMenu {
|
|||
doc: reaction.appear_animation,
|
||||
div: appearWrapper,
|
||||
play: true,
|
||||
liteModeKey: false,
|
||||
...options
|
||||
}).then(({render}) => render).then((player) => {
|
||||
assumeType<RLottiePlayer>(player);
|
||||
|
@ -217,6 +220,7 @@ export class ChatReactionsMenu {
|
|||
const selectLoadPromise = wrapSticker({
|
||||
doc: reaction.select_animation,
|
||||
div: selectWrapper,
|
||||
liteModeKey: false,
|
||||
...options
|
||||
}).then(({render}) => render).then((player) => {
|
||||
assumeType<RLottiePlayer>(player);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import indexOfAndSplice from '../../helpers/array/indexOfAndSplice';
|
||||
import callbackify from '../../helpers/callbackify';
|
||||
import ListenerSetter from '../../helpers/listenerSetter';
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
import {getMiddleware} from '../../helpers/middleware';
|
||||
import {modifyAckedPromise} from '../../helpers/modifyAckedResult';
|
||||
import {ChatFull} from '../../layer';
|
||||
|
@ -148,7 +149,7 @@ export default class ChatSendAs {
|
|||
this.updateButtons(peerIds);
|
||||
};
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
if(liteMode.isAvailable('animations')) {
|
||||
setTimeout(executeButtonsUpdate, 250);
|
||||
} else {
|
||||
executeButtonsUpdate();
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import cancelEvent from '../helpers/dom/cancelEvent';
|
||||
import {attachClickEvent} from '../helpers/dom/clickEvent';
|
||||
import findUpAsChild from '../helpers/dom/findUpAsChild';
|
||||
import ListenerSetter from '../helpers/listenerSetter';
|
||||
import safeAssign from '../helpers/object/safeAssign';
|
||||
import I18n, {LangPackKey} from '../lib/langPack';
|
||||
import CheckboxField from './checkboxField';
|
||||
import Row from './row';
|
||||
import {toast} from './toast';
|
||||
|
||||
export type CheckboxFieldsField = {
|
||||
text: LangPackKey,
|
||||
description?: LangPackKey,
|
||||
restrictionText?: LangPackKey,
|
||||
checkboxField?: CheckboxField,
|
||||
checked?: boolean,
|
||||
nested?: CheckboxFieldsField[],
|
||||
nestedTo?: CheckboxFieldsField,
|
||||
nestedCounter?: HTMLElement,
|
||||
setNestedCounter?: (count: number) => void,
|
||||
toggleWith?: {checked?: CheckboxFieldsField[], unchecked?: CheckboxFieldsField[]},
|
||||
name?: string,
|
||||
row?: Row
|
||||
};
|
||||
|
||||
export default class CheckboxFields<K extends CheckboxFieldsField = CheckboxFieldsField> {
|
||||
public fields: Array<K>;
|
||||
protected listenerSetter: ListenerSetter;
|
||||
protected asRestrictions: boolean;
|
||||
|
||||
constructor(options: {
|
||||
fields: Array<K>,
|
||||
listenerSetter: ListenerSetter,
|
||||
asRestrictions?: boolean
|
||||
}) {
|
||||
safeAssign(this, options);
|
||||
}
|
||||
|
||||
public createField(info: CheckboxFieldsField, isNested?: boolean) {
|
||||
if(info.nestedTo && !isNested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const row = info.row = new Row({
|
||||
titleLangKey: isNested ? undefined : info.text,
|
||||
checkboxField: info.checkboxField = new CheckboxField({
|
||||
text: isNested ? info.text : undefined,
|
||||
checked: info.nested ? false : info.checked,
|
||||
toggle: !isNested,
|
||||
listenerSetter: this.listenerSetter,
|
||||
restriction: this.asRestrictions && !isNested,
|
||||
name: info.name
|
||||
}),
|
||||
listenerSetter: this.listenerSetter,
|
||||
subtitleLangKey: info.description,
|
||||
clickable: info.nested ? (e) => {
|
||||
if(findUpAsChild(e.target as HTMLElement, row.checkboxField.label)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cancelEvent(e);
|
||||
row.container.classList.toggle('accordion-toggler-expanded');
|
||||
accordion.classList.toggle('is-expanded');
|
||||
} : undefined
|
||||
});
|
||||
|
||||
if(info.restrictionText) {
|
||||
info.checkboxField.input.disabled = true;
|
||||
|
||||
attachClickEvent(info.checkboxField.label, (e) => {
|
||||
toast(I18n.format(info.restrictionText, true));
|
||||
}, {listenerSetter: this.listenerSetter});
|
||||
}
|
||||
|
||||
const nodes: HTMLElement[] = [row.container];
|
||||
let accordion: HTMLElement, nestedCounter: HTMLElement;
|
||||
if(info.nested) {
|
||||
const container = accordion = document.createElement('div');
|
||||
container.classList.add('accordion');
|
||||
container.style.setProperty('--max-height', info.nested.length * 48 + 'px');
|
||||
const _info = info;
|
||||
info.nested.forEach((info) => {
|
||||
info.nestedTo ??= _info;
|
||||
container.append(...this.createField(info, true).nodes);
|
||||
});
|
||||
nodes.push(container);
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.classList.add('tgico-down', 'accordion-icon');
|
||||
|
||||
nestedCounter = info.nestedCounter = document.createElement('b');
|
||||
this.setNestedCounter(info);
|
||||
row.title.append(' ', nestedCounter, ' ', span);
|
||||
|
||||
row.container.classList.add('accordion-toggler');
|
||||
row.titleRow.classList.add('with-delimiter');
|
||||
|
||||
row.checkboxField.setValueSilently(this.getNestedCheckedLength(info) === info.nested.length);
|
||||
|
||||
info.toggleWith ??= {checked: info.nested, unchecked: info.nested};
|
||||
}
|
||||
|
||||
if(info.toggleWith || info.nestedTo) {
|
||||
const processToggleWith = info.toggleWith ? (info: CheckboxFieldsField) => {
|
||||
const {toggleWith, nested} = info;
|
||||
const value = info.checkboxField.checked;
|
||||
const arr = value ? toggleWith.checked : toggleWith.unchecked;
|
||||
if(!arr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const other = this.fields.filter((i) => arr.includes(i));
|
||||
other.forEach((info) => {
|
||||
info.checkboxField.setValueSilently(value);
|
||||
if(info.nestedTo && !nested) {
|
||||
this.setNestedCounter(info.nestedTo);
|
||||
}
|
||||
|
||||
if(info.toggleWith) {
|
||||
processToggleWith(info);
|
||||
}
|
||||
});
|
||||
|
||||
if(info.nested) {
|
||||
this.setNestedCounter(info);
|
||||
}
|
||||
} : undefined;
|
||||
|
||||
const processNestedTo = info.nestedTo ? () => {
|
||||
const length = this.getNestedCheckedLength(info.nestedTo);
|
||||
info.nestedTo.checkboxField.setValueSilently(length === info.nestedTo.nested.length);
|
||||
this.setNestedCounter(info.nestedTo, length);
|
||||
} : undefined;
|
||||
|
||||
this.listenerSetter.add(info.checkboxField.input)('change', () => {
|
||||
processToggleWith?.(info);
|
||||
processNestedTo?.();
|
||||
});
|
||||
}
|
||||
|
||||
return {row, nodes};
|
||||
}
|
||||
|
||||
protected getNestedCheckedLength(info: CheckboxFieldsField) {
|
||||
return info.nested.reduce((acc, v) => acc + +v.checkboxField.checked, 0);
|
||||
}
|
||||
|
||||
public setNestedCounter(info: CheckboxFieldsField, count = this.getNestedCheckedLength(info)) {
|
||||
info.nestedCounter.textContent = `${count}/${info.nested.length}`;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import {IS_MOBILE} from '../environment/userAgent';
|
||||
import {animate} from '../helpers/animation';
|
||||
import liteMode from '../helpers/liteMode';
|
||||
import {Middleware} from '../helpers/middleware';
|
||||
import clamp from '../helpers/number/clamp';
|
||||
import animationIntersector, {AnimationItemGroup, AnimationItemWrapper} from './animationIntersector';
|
||||
|
@ -31,6 +32,8 @@ export default class DotRenderer implements AnimationItemWrapper {
|
|||
|
||||
private dpr: number;
|
||||
|
||||
public loop: boolean = true;
|
||||
|
||||
constructor(
|
||||
private width: number,
|
||||
private height: number,
|
||||
|
@ -51,7 +54,7 @@ export default class DotRenderer implements AnimationItemWrapper {
|
|||
private prepare() {
|
||||
let count = Math.round(this.width * this.height / (35 * (IS_MOBILE ? 2 : 1)));
|
||||
count *= this.multiply || 1;
|
||||
count = Math.min(IS_MOBILE ? 1000 : 2200, count);
|
||||
count = Math.min(!liteMode.isAvailable('chat_spoilers') ? 400 : IS_MOBILE ? 1000 : 2200, count);
|
||||
count = Math.round(count);
|
||||
const dots: DotRendererDot[] = this.dots = new Array(count);
|
||||
|
||||
|
@ -168,7 +171,12 @@ export default class DotRenderer implements AnimationItemWrapper {
|
|||
const dotRenderer = new DotRenderer(width, height, multiply);
|
||||
dotRenderer.renderFirstFrame();
|
||||
|
||||
animationIntersector.addAnimation(dotRenderer, animationGroup, dotRenderer.canvas, middleware);
|
||||
animationIntersector.addAnimation({
|
||||
animation: dotRenderer,
|
||||
group: animationGroup,
|
||||
observeElement: dotRenderer.canvas,
|
||||
controlled: middleware
|
||||
});
|
||||
|
||||
return dotRenderer;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import PopupStickers from '../../popups/stickers';
|
|||
import {hideToast, toastNew} from '../../toast';
|
||||
import safeAssign from '../../../helpers/object/safeAssign';
|
||||
import type {AppStickersManager} from '../../../lib/appManagers/appStickersManager';
|
||||
import liteMode from '../../../helpers/liteMode';
|
||||
|
||||
const loadedURLs: Set<string> = new Set();
|
||||
export function appendEmoji(emoji: string, container?: HTMLElement, prepend = false, unify = false) {
|
||||
|
@ -81,14 +82,14 @@ export function appendEmoji(emoji: string, container?: HTMLElement, prepend = fa
|
|||
const placeholder = document.createElement('span');
|
||||
placeholder.classList.add('emoji-placeholder');
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
if(liteMode.isAvailable('animations')) {
|
||||
image.style.opacity = '0';
|
||||
placeholder.style.opacity = '1';
|
||||
}
|
||||
|
||||
image.addEventListener('load', () => {
|
||||
fastRaf(() => {
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
if(liteMode.isAvailable('animations')) {
|
||||
image.style.opacity = '';
|
||||
placeholder.style.opacity = '';
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import findUpAsChild from '../helpers/dom/findUpAsChild';
|
|||
import whichChild from '../helpers/dom/whichChild';
|
||||
import ListenerSetter from '../helpers/listenerSetter';
|
||||
import {attachClickEvent} from '../helpers/dom/clickEvent';
|
||||
import liteMode from '../helpers/liteMode';
|
||||
|
||||
export function horizontalMenu(
|
||||
tabs: HTMLElement,
|
||||
|
@ -66,7 +67,7 @@ export function horizontalMenu(
|
|||
});
|
||||
}
|
||||
|
||||
if(!rootScope.settings.animationsEnabled) {
|
||||
if(!liteMode.isAvailable('animations')) {
|
||||
animate = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import {fastRaf} from '../helpers/schedulers';
|
|||
import SetTransition from './singleTransition';
|
||||
import findUpClassName from '../helpers/dom/findUpClassName';
|
||||
import cancelEvent from '../helpers/dom/cancelEvent';
|
||||
import {attachClickEvent, detachClickEvent, simulateClickEvent} from '../helpers/dom/clickEvent';
|
||||
import {attachClickEvent, simulateClickEvent} from '../helpers/dom/clickEvent';
|
||||
import replaceContent from '../helpers/dom/replaceContent';
|
||||
import windowSize from '../helpers/windowSize';
|
||||
import {Message, MessageMedia, Poll, PollResults} from '../layer';
|
||||
|
@ -26,6 +26,7 @@ import setInnerHTML from '../helpers/dom/setInnerHTML';
|
|||
import {AppManagers} from '../lib/appManagers/managers';
|
||||
import wrapEmojiText from '../lib/richTextProcessor/wrapEmojiText';
|
||||
import wrapRichText from '../lib/richTextProcessor/wrapRichText';
|
||||
import liteMode from '../helpers/liteMode';
|
||||
|
||||
let lineTotalLength = 0;
|
||||
const tailLength = 9;
|
||||
|
@ -206,6 +207,8 @@ export default class PollElement extends HTMLElement {
|
|||
private sendVotePromise: Promise<void>;
|
||||
private sentVote = false;
|
||||
|
||||
private detachClickEvent: () => void;
|
||||
|
||||
public static setMaxLength() {
|
||||
const width = windowSize.width <= 360 ? windowSize.width - 120 : mediaSizes.active.poll.width;
|
||||
this.MAX_LENGTH = width + tailLength + this.MAX_OFFSET + -13.7; // 13 - position left
|
||||
|
@ -445,7 +448,7 @@ export default class PollElement extends HTMLElement {
|
|||
|
||||
if(canVote) {
|
||||
this.setVotersCount(results);
|
||||
attachClickEvent(this, this.clickHandler);
|
||||
this.detachClickEvent = attachClickEvent(this, this.clickHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -528,7 +531,7 @@ export default class PollElement extends HTMLElement {
|
|||
}
|
||||
|
||||
performResults(results: PollResults, chosenIndexes: number[], animate = true) {
|
||||
if(!rootScope.settings.animationsEnabled) {
|
||||
if(!liteMode.isAvailable('animations')) {
|
||||
animate = false;
|
||||
}
|
||||
|
||||
|
@ -568,9 +571,10 @@ export default class PollElement extends HTMLElement {
|
|||
this.chosenIndexes = chosenIndexes.slice();
|
||||
|
||||
if(this.isRetracted) {
|
||||
attachClickEvent(this, this.clickHandler);
|
||||
this.detachClickEvent = attachClickEvent(this, this.clickHandler);
|
||||
} else {
|
||||
detachClickEvent(this, this.clickHandler);
|
||||
this.detachClickEvent?.();
|
||||
this.detachClickEvent = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
import {doubleRaf} from '../../helpers/schedulers';
|
||||
import appImManager from '../../lib/appManagers/appImManager';
|
||||
import {LangPackKey, _i18n, i18n} from '../../lib/langPack';
|
||||
|
@ -114,7 +115,7 @@ class P extends PopupPeer {
|
|||
hint.classList.add('active');
|
||||
};
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
if(liteMode.isAvailable('animations')) {
|
||||
doubleRaf().then(setHintActive);
|
||||
} else {
|
||||
setHintActive();
|
||||
|
|
|
@ -47,6 +47,7 @@ import VIDEO_MIME_TYPES_SUPPORTED from '../../environment/videoMimeTypesSupport'
|
|||
import rootScope from '../../lib/rootScope';
|
||||
import shake from '../../helpers/dom/shake';
|
||||
import AUDIO_MIME_TYPES_SUPPORTED from '../../environment/audioMimeTypeSupport';
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
|
||||
type SendFileParams = SendFileDetails & {
|
||||
file?: File,
|
||||
|
@ -559,7 +560,7 @@ export default class PopupNewMedia extends PopupElement {
|
|||
langPackKey: key
|
||||
});
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
if(liteMode.isAvailable('animations')) {
|
||||
shake(this.body);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import getPeerInitials from './wrappers/getPeerInitials';
|
|||
import {wrapTopicIcon} from './wrappers/messageActionTextNewUnsafe';
|
||||
import makeError from '../helpers/makeError';
|
||||
import noop from '../helpers/noop';
|
||||
import liteMode from '../helpers/liteMode';
|
||||
|
||||
export async function putAvatar(
|
||||
div: HTMLElement,
|
||||
|
@ -46,7 +47,7 @@ export async function putAvatar(
|
|||
div.dataset.color = '';
|
||||
};
|
||||
} else {
|
||||
const animate = rootScope.settings.animationsEnabled;
|
||||
const animate = liteMode.isAvailable('animations');
|
||||
if(animate) {
|
||||
img.classList.add('fade-in');
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import IS_TOUCH_SUPPORTED from '../environment/touchSupport';
|
|||
import rootScope from '../lib/rootScope';
|
||||
import findUpAsChild from '../helpers/dom/findUpAsChild';
|
||||
import {fastRaf} from '../helpers/schedulers';
|
||||
import liteMode from '../helpers/liteMode';
|
||||
|
||||
let rippleClickId = 0;
|
||||
export default function ripple(
|
||||
|
@ -167,7 +168,7 @@ export default function ripple(
|
|||
};
|
||||
|
||||
attachListenerTo.addEventListener('touchstart', (e) => {
|
||||
if(!rootScope.settings.animationsEnabled) {
|
||||
if(!liteMode.isAvailable('animations')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -196,7 +197,7 @@ export default function ripple(
|
|||
return;
|
||||
}
|
||||
|
||||
if(!rootScope.settings.animationsEnabled) {
|
||||
if(!liteMode.isAvailable('animations')) {
|
||||
return;
|
||||
}
|
||||
// console.log('ripple mousedown', e, e.target, findUpClassName(e.target as HTMLElement, 'c-ripple') === r);
|
||||
|
|
|
@ -285,10 +285,15 @@ export default class Row {
|
|||
return media;
|
||||
}
|
||||
|
||||
public toggleDisability(disable = !this.container.classList.contains('is-disabled')) {
|
||||
this.container.classList.toggle('is-disabled', disable);
|
||||
return () => this.toggleDisability(!disable);
|
||||
}
|
||||
|
||||
public disableWithPromise(promise: Promise<any>) {
|
||||
this.container.classList.add('is-disabled');
|
||||
const toggle = this.toggleDisability(true);
|
||||
promise.finally(() => {
|
||||
this.container.classList.remove('is-disabled');
|
||||
toggle();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -154,30 +154,6 @@ export default class AppDataAndStorageTab extends SliderSuperTabEventable {
|
|||
|
||||
this.scrollable.append(section.container);
|
||||
}
|
||||
|
||||
{
|
||||
const section = new SettingSection({name: 'AutoplayMedia'});
|
||||
|
||||
section.content.append(new Row({
|
||||
checkboxField: new CheckboxField({
|
||||
text: 'AutoplayGIF',
|
||||
name: 'gifs',
|
||||
stateKey: 'settings.autoPlay.gifs',
|
||||
listenerSetter: this.listenerSetter
|
||||
}),
|
||||
listenerSetter: this.listenerSetter
|
||||
}).container, new Row({
|
||||
checkboxField: new CheckboxField({
|
||||
text: 'AutoplayVideo',
|
||||
name: 'videos',
|
||||
stateKey: 'settings.autoPlay.videos',
|
||||
listenerSetter: this.listenerSetter
|
||||
}),
|
||||
listenerSetter: this.listenerSetter
|
||||
}).container);
|
||||
|
||||
this.scrollable.append(section.container);
|
||||
}
|
||||
}
|
||||
|
||||
private setAutoDownloadSubtitle(row: Row, settings: AutoDownloadPeerTypeSettings, sizeMax?: number) {
|
||||
|
|
|
@ -12,7 +12,7 @@ import rootScope from '../../../lib/rootScope';
|
|||
import {IS_APPLE, IS_SAFARI} from '../../../environment/userAgent';
|
||||
import Row, {CreateRowFromCheckboxField} from '../../row';
|
||||
import AppBackgroundTab from './background';
|
||||
import {LangPackKey, _i18n} from '../../../lib/langPack';
|
||||
import I18n, {i18n, LangPackKey, _i18n} from '../../../lib/langPack';
|
||||
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
|
||||
import assumeType from '../../../helpers/assumeType';
|
||||
import {BaseTheme, MessagesAllStickers, StickerSet} from '../../../layer';
|
||||
|
@ -33,6 +33,8 @@ import {Theme} from '../../../layer';
|
|||
import findUpClassName from '../../../helpers/dom/findUpClassName';
|
||||
import RLottiePlayer from '../../../lib/rlottie/rlottiePlayer';
|
||||
import themeController from '../../../helpers/themeController';
|
||||
import liteMode from '../../../helpers/liteMode';
|
||||
import AppPowerSavingTab from './powerSaving';
|
||||
|
||||
export class RangeSettingSelector {
|
||||
public container: HTMLDivElement;
|
||||
|
@ -123,17 +125,30 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
|
|||
this.slider.createTab(AppBackgroundTab).open(initArgs);
|
||||
});
|
||||
|
||||
const animationsCheckboxField = new CheckboxField({
|
||||
text: 'EnableAnimations',
|
||||
name: 'animations',
|
||||
stateKey: 'settings.animationsEnabled',
|
||||
const getLiteModeStatus = (): LangPackKey => rootScope.settings.liteMode.all ? 'Checkbox.Enabled' : 'Checkbox.Disabled';
|
||||
const i = new I18n.IntlElement();
|
||||
|
||||
const onUpdate = () => {
|
||||
i.compareAndUpdate({key: getLiteModeStatus()});
|
||||
};
|
||||
onUpdate();
|
||||
|
||||
const liteModeRow = new Row({
|
||||
icon: 'animations',
|
||||
titleLangKey: 'LiteMode.EnableText',
|
||||
titleRightSecondary: i.element,
|
||||
clickable: () => {
|
||||
this.slider.createTab(AppPowerSavingTab).open();
|
||||
},
|
||||
listenerSetter: this.listenerSetter
|
||||
});
|
||||
|
||||
this.listenerSetter.add(rootScope)('settings_updated', onUpdate);
|
||||
|
||||
container.append(
|
||||
range.container,
|
||||
chatBackgroundButton,
|
||||
CreateRowFromCheckboxField(animationsCheckboxField).container
|
||||
liteModeRow.container
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -186,7 +201,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
|
|||
|
||||
lastOnFrameNo?.(-1);
|
||||
|
||||
if(item.player && rootScope.settings.animationsEnabled) {
|
||||
if(item.player && liteMode.isAvailable('animations')) {
|
||||
if(IS_SAFARI) {
|
||||
if(item.player.paused) {
|
||||
item.player.restart();
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import {State} from '../../../config/state';
|
||||
import flatten from '../../../helpers/array/flatten';
|
||||
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
|
||||
import {LiteModeKey} from '../../../helpers/liteMode';
|
||||
import pause from '../../../helpers/schedulers/pause';
|
||||
import rootScope from '../../../lib/rootScope';
|
||||
import CheckboxFields, {CheckboxFieldsField} from '../../checkboxFields';
|
||||
import SettingSection from '../../settingSection';
|
||||
import SliderSuperTab from '../../sliderTab';
|
||||
import {toastNew} from '../../toast';
|
||||
|
||||
type PowerSavingCheckboxFieldsField = CheckboxFieldsField & {
|
||||
key: LiteModeKey
|
||||
};
|
||||
|
||||
export default class AppPowerSavingTab extends SliderSuperTab {
|
||||
public init() {
|
||||
this.container.classList.add('power-saving-container');
|
||||
this.setTitle('LiteMode.Title');
|
||||
|
||||
const form = document.createElement('form');
|
||||
|
||||
let infoSection: SettingSection;
|
||||
{
|
||||
const section = infoSection = new SettingSection({
|
||||
caption: 'LiteMode.Info'
|
||||
});
|
||||
|
||||
form.append(section.container);
|
||||
}
|
||||
|
||||
const keys: Array<LiteModeKey | [LiteModeKey, LiteModeKey[]]> = [
|
||||
'all',
|
||||
'video',
|
||||
'gif',
|
||||
['stickers', ['stickers_panel', 'stickers_chat']],
|
||||
['emoji', ['emoji_panel', 'emoji_messages']],
|
||||
['effects', ['effects_reactions', 'effects_premiumstickers', 'effects_emoji']],
|
||||
['chat', ['chat_background', 'chat_spoilers']],
|
||||
'animations'
|
||||
];
|
||||
|
||||
let fields: PowerSavingCheckboxFieldsField[], checkboxFields: CheckboxFields<PowerSavingCheckboxFieldsField>;
|
||||
{
|
||||
const section = new SettingSection({});
|
||||
|
||||
const wrap = (key: typeof keys[0]): PowerSavingCheckboxFieldsField[] => {
|
||||
const isArray = Array.isArray(key);
|
||||
const mainKey = isArray ? key[0] : key;
|
||||
const nested = isArray ? flatten(key[1].map(wrap)) : undefined;
|
||||
const value = rootScope.settings.liteMode[mainKey];
|
||||
return [{
|
||||
key: mainKey,
|
||||
text: mainKey === 'all' ? 'LiteMode.EnableText' : `LiteMode.Key.${mainKey}.Title`,
|
||||
checked: mainKey === 'all' ? value : !value,
|
||||
nested: nested,
|
||||
name: 'power-saving-' + mainKey
|
||||
}, ...(nested || [])];
|
||||
};
|
||||
|
||||
fields = flatten(keys.map(wrap));
|
||||
|
||||
checkboxFields = new CheckboxFields({
|
||||
fields: fields,
|
||||
listenerSetter: this.listenerSetter
|
||||
});
|
||||
|
||||
fields.forEach((field, idx) => {
|
||||
const created = checkboxFields.createField(field);
|
||||
if(!created) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {row, nodes} = created;
|
||||
(idx === 0 ? infoSection : section).content.append(...nodes);
|
||||
});
|
||||
|
||||
attachClickEvent(section.content, () => {
|
||||
if(rootScope.settings.liteMode.all) {
|
||||
toastNew({langPackKey: 'LiteMode.DisableAlert'});
|
||||
}
|
||||
}, {listenerSetter: this.listenerSetter});
|
||||
|
||||
form.append(section.container);
|
||||
}
|
||||
|
||||
const onAllChange = (disable: boolean) => {
|
||||
fields.forEach((field) => {
|
||||
if(field.key === 'all') {
|
||||
return;
|
||||
}
|
||||
|
||||
if(field.nested) {
|
||||
checkboxFields.setNestedCounter(field, disable ? 0 : undefined);
|
||||
}
|
||||
|
||||
field.checkboxField.input.classList.toggle('is-fake-disabled', disable);
|
||||
field.row.toggleDisability(disable);
|
||||
});
|
||||
};
|
||||
|
||||
this.listenerSetter.add(form)('change', async() => {
|
||||
const liteMode: State['settings']['liteMode'] = {} as any;
|
||||
fields.forEach((field) => {
|
||||
const checked = field.checkboxField.checked;
|
||||
liteMode[field.key] = field.key === 'all' ? checked : !checked;
|
||||
});
|
||||
|
||||
const wasAll = rootScope.settings.liteMode.all;
|
||||
if(wasAll !== liteMode.all) {
|
||||
onAllChange(!wasAll);
|
||||
|
||||
if(liteMode.all) {
|
||||
await pause(200);
|
||||
}
|
||||
}
|
||||
|
||||
await this.managers.appStateManager.setByKey('settings.liteMode', rootScope.settings.liteMode = liteMode);
|
||||
});
|
||||
|
||||
onAllChange(rootScope.settings.liteMode.all);
|
||||
|
||||
this.scrollable.append(form);
|
||||
}
|
||||
}
|
|
@ -28,7 +28,6 @@ import {AccountAuthorizations, Authorization} from '../../../layer';
|
|||
import PopupElement from '../../popups';
|
||||
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
|
||||
import SettingSection from '../../settingSection';
|
||||
// import AppMediaViewer from "../../appMediaViewerNew";
|
||||
|
||||
export default class AppSettingsTab extends SliderSuperTab {
|
||||
private buttons: {
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
*/
|
||||
|
||||
import type {ChatRights} from '../../../lib/appManagers/appChatsManager';
|
||||
import flatten from '../../../helpers/array/flatten';
|
||||
import cancelEvent from '../../../helpers/dom/cancelEvent';
|
||||
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
|
||||
import findUpTag from '../../../helpers/dom/findUpTag';
|
||||
import replaceContent from '../../../helpers/dom/replaceContent';
|
||||
|
@ -19,32 +17,22 @@ import combineParticipantBannedRights from '../../../lib/appManagers/utils/chats
|
|||
import hasRights from '../../../lib/appManagers/utils/chats/hasRights';
|
||||
import getPeerActiveUsernames from '../../../lib/appManagers/utils/peers/getPeerActiveUsernames';
|
||||
import getPeerId from '../../../lib/appManagers/utils/peers/getPeerId';
|
||||
import I18n, {i18n, join, LangPackKey} from '../../../lib/langPack';
|
||||
import {i18n, join, LangPackKey} from '../../../lib/langPack';
|
||||
import rootScope from '../../../lib/rootScope';
|
||||
import CheckboxField from '../../checkboxField';
|
||||
import PopupPickUser from '../../popups/pickUser';
|
||||
import Row from '../../row';
|
||||
import SettingSection from '../../settingSection';
|
||||
import {SliderSuperTabEventable} from '../../sliderTab';
|
||||
import {toast} from '../../toast';
|
||||
import AppUserPermissionsTab from './userPermissions';
|
||||
import findUpAsChild from '../../../helpers/dom/findUpAsChild';
|
||||
import CheckboxFields, {CheckboxFieldsField} from '../../checkboxFields';
|
||||
|
||||
type T = {
|
||||
type PermissionsCheckboxFieldsField = CheckboxFieldsField & {
|
||||
flags: ChatRights[],
|
||||
text: LangPackKey,
|
||||
exceptionText: LangPackKey,
|
||||
checkboxField?: CheckboxField,
|
||||
nested?: T[],
|
||||
nestedTo?: T,
|
||||
nestedCounter?: HTMLElement,
|
||||
setNestedCounter?: (count: number) => void,
|
||||
toggleWith?: {checked?: ChatRights[], unchecked?: ChatRights[]}
|
||||
exceptionText: LangPackKey
|
||||
};
|
||||
|
||||
export class ChatPermissions {
|
||||
public v: Array<T>;
|
||||
|
||||
export class ChatPermissions extends CheckboxFields<PermissionsCheckboxFieldsField> {
|
||||
protected chat: Chat.chat | Chat.channel;
|
||||
protected rights: ChatBannedRights.chatBannedRights;
|
||||
protected defaultBannedRights: ChatBannedRights.chatBannedRights;
|
||||
|
@ -56,11 +44,22 @@ export class ChatPermissions {
|
|||
appendTo: HTMLElement,
|
||||
participant?: ChannelParticipant.channelParticipantBanned
|
||||
}, private managers: AppManagers) {
|
||||
super({
|
||||
listenerSetter: options.listenerSetter,
|
||||
fields: [],
|
||||
asRestrictions: true
|
||||
});
|
||||
|
||||
this.construct();
|
||||
}
|
||||
|
||||
public async construct() {
|
||||
const mediaNested: T[] = [
|
||||
const options = this.options;
|
||||
const chat = this.chat = await this.managers.appChatsManager.getChat(options.chatId) as Chat.chat | Chat.channel;
|
||||
const defaultBannedRights = this.defaultBannedRights = chat.default_banned_rights;
|
||||
const rights = this.rights = options.participant ? combineParticipantBannedRights(chat as Chat.channel, options.participant.banned_rights) : defaultBannedRights;
|
||||
|
||||
const mediaNested: PermissionsCheckboxFieldsField[] = [
|
||||
{flags: ['send_photos'], text: 'UserRestrictionsSendPhotos', exceptionText: 'UserRestrictionsNoSendPhotos'},
|
||||
{flags: ['send_videos'], text: 'UserRestrictionsSendVideos', exceptionText: 'UserRestrictionsNoSendVideos'},
|
||||
{flags: ['send_stickers', 'send_gifs'], text: 'UserRestrictionsSendStickers', exceptionText: 'UserRestrictionsNoSendStickers'},
|
||||
|
@ -68,150 +67,57 @@ export class ChatPermissions {
|
|||
{flags: ['send_docs'], text: 'UserRestrictionsSendFiles', exceptionText: 'UserRestrictionsNoSendDocs'},
|
||||
{flags: ['send_voices'], text: 'UserRestrictionsSendVoices', exceptionText: 'UserRestrictionsNoSendVoice'},
|
||||
{flags: ['send_roundvideos'], text: 'UserRestrictionsSendRound', exceptionText: 'UserRestrictionsNoSendRound'},
|
||||
{flags: ['embed_links'], text: 'UserRestrictionsEmbedLinks', exceptionText: 'UserRestrictionsNoEmbedLinks', toggleWith: {checked: ['send_plain']}},
|
||||
{flags: ['embed_links'], text: 'UserRestrictionsEmbedLinks', exceptionText: 'UserRestrictionsNoEmbedLinks'},
|
||||
{flags: ['send_polls'], text: 'UserRestrictionsSendPolls', exceptionText: 'UserRestrictionsNoSendPolls'}
|
||||
];
|
||||
|
||||
const mediaToggleWith = flatten(mediaNested.map(({flags}) => flags));
|
||||
const media: T = {flags: ['send_media'], text: 'UserRestrictionsSendMedia', exceptionText: 'UserRestrictionsNoSendMedia', nested: mediaNested, toggleWith: {unchecked: mediaToggleWith, checked: mediaToggleWith}};
|
||||
|
||||
this.v = [
|
||||
{flags: ['send_plain'], text: 'UserRestrictionsSend', exceptionText: 'UserRestrictionsNoSend', toggleWith: {unchecked: ['embed_links']}},
|
||||
media,
|
||||
const mediaToggleWith = mediaNested;
|
||||
const v: PermissionsCheckboxFieldsField[] = [
|
||||
{flags: ['send_plain'], text: 'UserRestrictionsSend', exceptionText: 'UserRestrictionsNoSend'},
|
||||
{flags: ['send_media'], text: 'UserRestrictionsSendMedia', exceptionText: 'UserRestrictionsNoSendMedia', nested: mediaNested},
|
||||
{flags: ['invite_users'], text: 'UserRestrictionsInviteUsers', exceptionText: 'UserRestrictionsNoInviteUsers'},
|
||||
{flags: ['pin_messages'], text: 'UserRestrictionsPinMessages', exceptionText: 'UserRestrictionsNoPinMessages'},
|
||||
{flags: ['change_info'], text: 'UserRestrictionsChangeInfo', exceptionText: 'UserRestrictionsNoChangeInfo'}
|
||||
];
|
||||
|
||||
mediaNested.forEach((info) => info.nestedTo = media);
|
||||
|
||||
const options = this.options;
|
||||
const chat = this.chat = await this.managers.appChatsManager.getChat(options.chatId) as Chat.chat | Chat.channel;
|
||||
const defaultBannedRights = this.defaultBannedRights = chat.default_banned_rights;
|
||||
const rights = this.rights = options.participant ? combineParticipantBannedRights(chat as Chat.channel, options.participant.banned_rights) : defaultBannedRights;
|
||||
|
||||
for(const info of this.v) {
|
||||
const {nodes} = this.createRow(info);
|
||||
options.appendTo.append(...nodes);
|
||||
}
|
||||
|
||||
this.v.push(...mediaNested);
|
||||
}
|
||||
|
||||
protected createRow(info: T, isNested?: boolean) {
|
||||
const {defaultBannedRights, chat, rights, restrictionText} = this;
|
||||
|
||||
const mainFlag = info.flags[0];
|
||||
const row = new Row({
|
||||
titleLangKey: isNested ? undefined : info.text,
|
||||
checkboxField: info.checkboxField = new CheckboxField({
|
||||
text: isNested ? info.text : undefined,
|
||||
checked: info.nested ? false : hasRights(chat, mainFlag, rights),
|
||||
toggle: !isNested,
|
||||
listenerSetter: this.options.listenerSetter,
|
||||
restriction: !isNested
|
||||
}),
|
||||
listenerSetter: this.options.listenerSetter,
|
||||
clickable: info.nested ? (e) => {
|
||||
if(findUpAsChild(e.target as HTMLElement, row.checkboxField.label)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cancelEvent(e);
|
||||
row.container.classList.toggle('accordion-toggler-expanded');
|
||||
accordion.classList.toggle('is-expanded');
|
||||
} : undefined
|
||||
const map: {[action in ChatRights]?: PermissionsCheckboxFieldsField} = {};
|
||||
v.push(...mediaNested);
|
||||
v.forEach((info) => {
|
||||
const mainFlag = info.flags[0];
|
||||
map[mainFlag] = info;
|
||||
info.checked = hasRights(chat, mainFlag, rights)
|
||||
});
|
||||
|
||||
if((
|
||||
this.options.participant &&
|
||||
defaultBannedRights.pFlags[mainFlag as keyof typeof defaultBannedRights['pFlags']]
|
||||
) || (
|
||||
getPeerActiveUsernames(chat as Chat.channel)[0] &&
|
||||
(
|
||||
info.flags.includes('pin_messages') ||
|
||||
info.flags.includes('change_info')
|
||||
)
|
||||
)
|
||||
) {
|
||||
info.checkboxField.input.disabled = true;
|
||||
mediaNested.forEach((info) => info.nestedTo = map.send_media);
|
||||
map.send_media.toggleWith = {unchecked: mediaToggleWith, checked: mediaToggleWith};
|
||||
map.embed_links.toggleWith = {checked: [map.send_plain]};
|
||||
map.send_plain.toggleWith = {unchecked: [map.embed_links]};
|
||||
|
||||
attachClickEvent(info.checkboxField.label, (e) => {
|
||||
toast(I18n.format(restrictionText, true));
|
||||
}, {listenerSetter: this.options.listenerSetter});
|
||||
this.fields = v;
|
||||
|
||||
for(const info of this.fields) {
|
||||
if((
|
||||
this.options.participant &&
|
||||
defaultBannedRights.pFlags[info.flags[0] as keyof typeof defaultBannedRights['pFlags']]
|
||||
) || (
|
||||
getPeerActiveUsernames(chat as Chat.channel)[0] &&
|
||||
(
|
||||
info.flags.includes('pin_messages') ||
|
||||
info.flags.includes('change_info')
|
||||
)
|
||||
)
|
||||
) {
|
||||
info.restrictionText = this.restrictionText;
|
||||
}
|
||||
|
||||
if(info.nestedTo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const {nodes} = this.createField(info);
|
||||
options.appendTo.append(...nodes);
|
||||
}
|
||||
|
||||
if(info.toggleWith || info.nestedTo) {
|
||||
const processToggleWith = info.toggleWith ? (info: T) => {
|
||||
const {toggleWith, nested} = info;
|
||||
const value = info.checkboxField.checked;
|
||||
const arr = value ? toggleWith.checked : toggleWith.unchecked;
|
||||
if(!arr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const other = this.v.filter((i) => arr.includes(i.flags[0]));
|
||||
other.forEach((info) => {
|
||||
info.checkboxField.setValueSilently(value);
|
||||
if(info.nestedTo && !nested) {
|
||||
this.setNestedCounter(info.nestedTo);
|
||||
}
|
||||
|
||||
if(info.toggleWith) {
|
||||
processToggleWith(info);
|
||||
}
|
||||
});
|
||||
|
||||
if(info.nested) {
|
||||
this.setNestedCounter(info);
|
||||
}
|
||||
} : undefined;
|
||||
|
||||
const processNestedTo = info.nestedTo ? () => {
|
||||
const length = this.getNestedCheckedLength(info.nestedTo);
|
||||
info.nestedTo.checkboxField.setValueSilently(length === info.nestedTo.nested.length);
|
||||
this.setNestedCounter(info.nestedTo, length);
|
||||
} : undefined;
|
||||
|
||||
this.options.listenerSetter.add(info.checkboxField.input)('change', () => {
|
||||
processToggleWith?.(info);
|
||||
processNestedTo?.();
|
||||
});
|
||||
}
|
||||
|
||||
const nodes: HTMLElement[] = [row.container];
|
||||
let accordion: HTMLElement, nestedCounter: HTMLElement;
|
||||
if(info.nested) {
|
||||
const container = accordion = document.createElement('div');
|
||||
container.classList.add('accordion');
|
||||
container.style.setProperty('--max-height', info.nested.length * 48 + 'px');
|
||||
info.nested.forEach((info) => {
|
||||
container.append(...this.createRow(info, true).nodes);
|
||||
});
|
||||
nodes.push(container);
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.classList.add('tgico-down', 'accordion-icon');
|
||||
|
||||
nestedCounter = info.nestedCounter = document.createElement('b');
|
||||
this.setNestedCounter(info);
|
||||
row.title.append(' ', nestedCounter, ' ', span);
|
||||
|
||||
row.container.classList.add('accordion-toggler');
|
||||
row.titleRow.classList.add('with-delimiter');
|
||||
|
||||
row.checkboxField.setValueSilently(this.getNestedCheckedLength(info) === info.nested.length);
|
||||
}
|
||||
|
||||
return {row, nodes};
|
||||
}
|
||||
|
||||
protected getNestedCheckedLength(info: T) {
|
||||
return info.nested.reduce((acc, v) => acc + +v.checkboxField.checked, 0);
|
||||
}
|
||||
|
||||
protected setNestedCounter(info: T, count = this.getNestedCheckedLength(info)) {
|
||||
info.nestedCounter.textContent = `${count}/${info.nested.length}`;
|
||||
}
|
||||
|
||||
public takeOut() {
|
||||
|
@ -224,7 +130,7 @@ export class ChatPermissions {
|
|||
const IGNORE_FLAGS: Set<ChatRights> = new Set([
|
||||
'send_media'
|
||||
]);
|
||||
for(const info of this.v) {
|
||||
for(const info of this.fields) {
|
||||
const banned = !info.checkboxField.checked;
|
||||
if(!banned) {
|
||||
continue;
|
||||
|
@ -341,7 +247,7 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
|
|||
// const combinedRights = appChatsManager.combineParticipantBannedRights(this.chatId, bannedRights);
|
||||
|
||||
const cantWhat: LangPackKey[] = []/* , canWhat: LangPackKey[] = [] */;
|
||||
chatPermissions.v.forEach((info) => {
|
||||
chatPermissions.fields.forEach((info) => {
|
||||
const mainFlag = info.flags[0];
|
||||
// @ts-ignore
|
||||
if(bannedRights.pFlags[mainFlag] && !defaultBannedRights.pFlags[mainFlag]) {
|
||||
|
|
|
@ -24,6 +24,7 @@ import PeerProfile from '../../peerProfile';
|
|||
import {Message} from '../../../layer';
|
||||
import getMessageThreadId from '../../../lib/appManagers/utils/messages/getMessageThreadId';
|
||||
import AppEditTopicTab from './editTopic';
|
||||
import liteMode from '../../../helpers/liteMode';
|
||||
|
||||
type SharedMediaHistoryStorage = Partial<{
|
||||
[type in SearchSuperType]: {mid: number, peerId: PeerId}[]
|
||||
|
@ -226,7 +227,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
|
|||
}],
|
||||
scrollable: this.scrollable,
|
||||
onChangeTab: (mediaTab) => {
|
||||
const timeout = mediaTab.type === 'members' && rootScope.settings.animationsEnabled ? 250 : 0;
|
||||
const timeout = mediaTab.type === 'members' && liteMode.isAvailable('animations') ? 250 : 0;
|
||||
setTimeout(() => {
|
||||
btnAddMembers.classList.toggle('is-hidden', mediaTab.type !== 'members');
|
||||
}, timeout);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import liteMode from '../helpers/liteMode';
|
||||
import rootScope from '../lib/rootScope';
|
||||
|
||||
type SetTransitionOptions = {
|
||||
|
@ -36,7 +37,7 @@ const SetTransition = (options: SetTransitionOptions) => {
|
|||
// return;
|
||||
// }
|
||||
|
||||
if(useRafs && rootScope.settings.animationsEnabled && duration) {
|
||||
if(useRafs && liteMode.isAvailable('animations') && duration) {
|
||||
element.dataset.raf = '' + window.requestAnimationFrame(() => {
|
||||
delete element.dataset.raf;
|
||||
SetTransition({
|
||||
|
@ -64,7 +65,7 @@ const SetTransition = (options: SetTransitionOptions) => {
|
|||
};
|
||||
|
||||
onTransitionStart?.();
|
||||
if(!rootScope.settings.animationsEnabled || !duration) {
|
||||
if(!liteMode.isAvailable('animations') || !duration) {
|
||||
element.classList.remove('animating', 'backwards');
|
||||
afterTimeout();
|
||||
return;
|
||||
|
|
|
@ -10,6 +10,7 @@ import {dispatchHeavyAnimationEvent} from '../hooks/useHeavyAnimationCheck';
|
|||
import whichChild from '../helpers/dom/whichChild';
|
||||
import cancelEvent from '../helpers/dom/cancelEvent';
|
||||
import ListenerSetter from '../helpers/listenerSetter';
|
||||
import liteMode from '../helpers/liteMode';
|
||||
|
||||
function makeTransitionFunction(options: TransitionFunction) {
|
||||
return options;
|
||||
|
@ -216,7 +217,7 @@ const TransitionSlider = (options: TransitionSliderOptions) => {
|
|||
|
||||
const to = content.children[id] as HTMLElement;
|
||||
|
||||
if(!rootScope.settings.animationsEnabled || (prevId === -1 && !animateFirst)) {
|
||||
if(!liteMode.isAvailable('animations') || (prevId === -1 && !animateFirst)) {
|
||||
animate = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import {formatFullSentTime} from '../../helpers/date';
|
|||
import {simulateClickEvent, attachClickEvent} from '../../helpers/dom/clickEvent';
|
||||
import findUpClassName from '../../helpers/dom/findUpClassName';
|
||||
import formatBytes from '../../helpers/formatBytes';
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
import {MediaSizeType} from '../../helpers/mediaSizes';
|
||||
import noop from '../../helpers/noop';
|
||||
import {Message, MessageMedia, WebPage} from '../../layer';
|
||||
|
@ -289,7 +290,7 @@ export default async function wrapDocument({message, withTime, fontWeight, voice
|
|||
setTimeout(async() => { // wait for preloader animation end
|
||||
const url = (await getCacheContext()).url;
|
||||
window.open(url);
|
||||
}, rootScope.settings.animationsEnabled ? 250 : 0);
|
||||
}, liteMode.isAvailable('animations') ? 250 : 0);
|
||||
});
|
||||
}
|
||||
} else if(MEDIA_MIME_TYPES_SUPPORTED.has(doc.mime_type) && doc.thumbs?.length) {
|
||||
|
|
|
@ -24,6 +24,7 @@ import createVideo from '../../helpers/dom/createVideo';
|
|||
import noop from '../../helpers/noop';
|
||||
import {THUMB_TYPE_FULL} from '../../lib/mtproto/mtproto_config';
|
||||
import {Middleware} from '../../helpers/middleware';
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
|
||||
export default async function wrapPhoto({photo, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware, size, withoutPreloader, loadPromises, autoDownloadSize, noBlur, noThumb, noFadeIn, blurAfter, managers = rootScope.managers, processUrl}: {
|
||||
photo: MyPhoto | MyDocument | WebDocument,
|
||||
|
@ -191,7 +192,7 @@ export default async function wrapPhoto({photo, message, container, boxWidth, bo
|
|||
|
||||
// console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
||||
|
||||
const needFadeIn = (thumbImage || !cacheContext.downloaded) && rootScope.settings.animationsEnabled && !noFadeIn;
|
||||
const needFadeIn = (thumbImage || !cacheContext.downloaded) && liteMode.isAvailable('animations') && !noFadeIn;
|
||||
|
||||
let preloader: ProgressivePreloader;
|
||||
const uploadingFileName = (message as Message.message)?.uploadingFileName;
|
||||
|
|
|
@ -46,6 +46,7 @@ import {hideToast, toastNew} from '../toast';
|
|||
import wrapStickerAnimation from './stickerAnimation';
|
||||
import framesCache from '../../helpers/framesCache';
|
||||
import {IS_MOBILE} from '../../environment/userAgent';
|
||||
import liteMode, {LiteModeKey} from '../../helpers/liteMode';
|
||||
|
||||
// https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp#L40
|
||||
export const STICKER_EFFECT_MULTIPLIER = 1 + 0.245 * 2;
|
||||
|
@ -65,7 +66,7 @@ const onAnimationEnd = (element: HTMLElement, onAnimationEnd: () => void, timeou
|
|||
const _timeout = setTimeout(onEnd, timeout);
|
||||
};
|
||||
|
||||
export default async function wrapSticker({doc, div, middleware, loadStickerMiddleware, lazyLoadQueue, exportLoad, group, play, onlyThumb, emoji, width, height, withThumb, loop, loadPromises, needFadeIn, needUpscale, skipRatio, static: asStatic, managers = rootScope.managers, fullThumb, isOut, noPremium, withLock, relativeEffect, loopEffect, isCustomEmoji, syncedVideo}: {
|
||||
export default async function wrapSticker({doc, div, middleware, loadStickerMiddleware, lazyLoadQueue, exportLoad, group, play, onlyThumb, emoji, width, height, withThumb, loop, loadPromises, needFadeIn, needUpscale, skipRatio, static: asStatic, managers = rootScope.managers, fullThumb, isOut, noPremium, withLock, relativeEffect, loopEffect, isCustomEmoji, syncedVideo, liteModeKey, isEffect}: {
|
||||
doc: MyDocument,
|
||||
div: HTMLElement | HTMLElement[],
|
||||
middleware?: Middleware,
|
||||
|
@ -93,10 +94,14 @@ export default async function wrapSticker({doc, div, middleware, loadStickerMidd
|
|||
relativeEffect?: boolean,
|
||||
loopEffect?: boolean,
|
||||
isCustomEmoji?: boolean,
|
||||
syncedVideo?: boolean
|
||||
syncedVideo?: boolean,
|
||||
liteModeKey?: LiteModeKey | false,
|
||||
isEffect?: boolean
|
||||
}) {
|
||||
div = Array.isArray(div) ? div : [div];
|
||||
|
||||
liteModeKey ??= 'stickers_panel';
|
||||
|
||||
if(isCustomEmoji) {
|
||||
emoji = doc.stickerEmojiRaw;
|
||||
}
|
||||
|
@ -119,15 +124,25 @@ export default async function wrapSticker({doc, div, middleware, loadStickerMidd
|
|||
lottieLoader.loadLottieWorkers();
|
||||
}
|
||||
|
||||
loop = !!(!emoji || isCustomEmoji) && loop;
|
||||
|
||||
div.forEach((div) => {
|
||||
div.dataset.docId = '' + doc.id;
|
||||
if(emoji) {
|
||||
div.dataset.stickerEmoji = emoji;
|
||||
}
|
||||
|
||||
div.dataset.stickerPlay = '' + +play;
|
||||
div.dataset.stickerLoop = '' + +loop;
|
||||
|
||||
div.classList.add('media-sticker-wrapper');
|
||||
});
|
||||
|
||||
if(play && !liteMode.isAvailable(liteModeKey) && !isCustomEmoji && !isEffect) {
|
||||
play = false;
|
||||
loop = false;
|
||||
}
|
||||
|
||||
/* if(stickerType === 3) {
|
||||
const videoRes = wrapVideo({
|
||||
doc,
|
||||
|
@ -278,7 +293,7 @@ export default async function wrapSticker({doc, div, middleware, loadStickerMidd
|
|||
|
||||
const path = document.createElementNS(ns, 'path');
|
||||
path.setAttributeNS(null, 'd', d);
|
||||
if(rootScope.settings.animationsEnabled && !isCustomEmoji) path.setAttributeNS(null, 'fill', 'url(#g)');
|
||||
if(liteMode.isAvailable('animations') && !isCustomEmoji) path.setAttributeNS(null, 'fill', 'url(#g)');
|
||||
svg.append(path);
|
||||
div.forEach((div, idx) => div.append(idx > 0 ? svg.cloneNode(true) : svg));
|
||||
haveThumbCached = true;
|
||||
|
@ -384,7 +399,7 @@ export default async function wrapSticker({doc, div, middleware, loadStickerMidd
|
|||
|
||||
const animation = await lottieLoader.loadAnimationWorker({
|
||||
container: (div as HTMLElement[])[0],
|
||||
loop: !!(!emoji || isCustomEmoji) && loop,
|
||||
loop,
|
||||
autoplay: play,
|
||||
animationData: blob,
|
||||
width,
|
||||
|
@ -395,7 +410,8 @@ export default async function wrapSticker({doc, div, middleware, loadStickerMidd
|
|||
toneIndex,
|
||||
sync: isCustomEmoji,
|
||||
middleware: loadStickerMiddleware ?? middleware,
|
||||
group
|
||||
group,
|
||||
liteModeKey: liteModeKey || undefined
|
||||
});
|
||||
|
||||
// const deferred = deferredPromise<void>();
|
||||
|
@ -408,7 +424,7 @@ export default async function wrapSticker({doc, div, middleware, loadStickerMidd
|
|||
const onFirstFrame = (container: HTMLElement, canvas: HTMLCanvasElement) => {
|
||||
const element = container.firstElementChild !== canvas && container.firstElementChild as HTMLElement;
|
||||
if(needFadeIn !== false) {
|
||||
needFadeIn = (needFadeIn || !element || element.tagName === 'svg') && rootScope.settings.animationsEnabled;
|
||||
needFadeIn = (needFadeIn || !element || element.tagName === 'svg') && liteMode.isAvailable('animations');
|
||||
}
|
||||
|
||||
const cb = () => {
|
||||
|
@ -506,7 +522,7 @@ export default async function wrapSticker({doc, div, middleware, loadStickerMidd
|
|||
|
||||
const thumbImage = (div as HTMLElement[]).map((div, idx) => (div.firstElementChild as HTMLElement) !== media[idx] && div.firstElementChild) as HTMLElement[];
|
||||
if(needFadeIn !== false) {
|
||||
needFadeIn = (needFadeIn || !downloaded || (asStatic ? thumbImage[0] : (!thumbImage[0] || thumbImage[0].tagName === 'svg'))) && rootScope.settings.animationsEnabled;
|
||||
needFadeIn = (needFadeIn || !downloaded || (asStatic ? thumbImage[0] : (!thumbImage[0] || thumbImage[0].tagName === 'svg'))) && liteMode.isAvailable('animations');
|
||||
}
|
||||
|
||||
if(needFadeIn) {
|
||||
|
@ -570,7 +586,13 @@ export default async function wrapSticker({doc, div, middleware, loadStickerMidd
|
|||
}
|
||||
|
||||
if(isAnimated) {
|
||||
animationIntersector.addAnimation(media as HTMLVideoElement, group, undefined, middleware);
|
||||
animationIntersector.addAnimation({
|
||||
animation: media as HTMLVideoElement,
|
||||
observeElement: div,
|
||||
group,
|
||||
controlled: middleware,
|
||||
liteModeKey: liteModeKey || undefined
|
||||
});
|
||||
}
|
||||
|
||||
if(loaded.push(media) === mediaLength) {
|
||||
|
@ -667,8 +689,13 @@ function attachStickerEffectHandler({container, doc, managers, middleware, isOut
|
|||
|
||||
let playing = false;
|
||||
attachClickEvent(container, async(e) => {
|
||||
const isAvailable = liteMode.isAvailable('effects_premiumstickers') || relativeEffect;
|
||||
cancelEvent(e);
|
||||
if(playing) {
|
||||
if(!e.isTrusted && !isAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(playing || !isAvailable) {
|
||||
const a = document.createElement('a');
|
||||
a.onclick = () => {
|
||||
hideToast();
|
||||
|
@ -750,7 +777,7 @@ export async function onEmojiStickerClick({event, container, managers, peerId, m
|
|||
animation.restart();
|
||||
}
|
||||
|
||||
if(!peerId.isUser()) {
|
||||
if(!peerId.isUser() || !liteMode.isAvailable('effects_emoji')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,8 @@ export default function wrapStickerAnimation({
|
|||
group: 'none',
|
||||
skipRatio,
|
||||
managers,
|
||||
fullThumb
|
||||
fullThumb,
|
||||
isEffect: true
|
||||
}).then(({render}) => render).then((_animation) => {
|
||||
assumeType<RLottiePlayer>(_animation);
|
||||
if(!middleware()) {
|
||||
|
|
|
@ -69,7 +69,10 @@ export default async function wrapStickerSetThumb({set, lazyLoadQueue, container
|
|||
container.append(media);
|
||||
|
||||
if(set.pFlags.videos) {
|
||||
animationIntersector.addAnimation(media as HTMLVideoElement, group);
|
||||
animationIntersector.addAnimation({
|
||||
animation: media as HTMLVideoElement,
|
||||
group
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ import createVideo from '../../helpers/dom/createVideo';
|
|||
import isInDOM from '../../helpers/dom/isInDOM';
|
||||
import renderImageFromUrl from '../../helpers/dom/renderImageFromUrl';
|
||||
import getStrippedThumbIfNeeded from '../../helpers/getStrippedThumbIfNeeded';
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
import makeError from '../../helpers/makeError';
|
||||
import mediaSizes, {ScreenSize} from '../../helpers/mediaSizes';
|
||||
import {Middleware} from '../../helpers/middleware';
|
||||
|
@ -96,7 +97,7 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
|
|||
doc.size <= MAX_VIDEO_AUTOPLAY_SIZE &&
|
||||
!isAlbumItem
|
||||
)
|
||||
) && (doc.type === 'gif' ? rootScope.settings.autoPlay.gifs : rootScope.settings.autoPlay.videos)
|
||||
) && (doc.type === 'gif' ? liteMode.isAvailable('gif') : liteMode.isAvailable('video'))
|
||||
);
|
||||
let spanTime: HTMLElement, spanPlay: HTMLElement;
|
||||
|
||||
|
@ -537,7 +538,10 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
|
|||
|
||||
onMediaLoad(video).then(() => {
|
||||
if(group) {
|
||||
animationIntersector.addAnimation(video, group);
|
||||
animationIntersector.addAnimation({
|
||||
animation: video,
|
||||
group
|
||||
});
|
||||
}
|
||||
|
||||
if(preloader && !uploadFileName) {
|
||||
|
|
|
@ -21,7 +21,7 @@ const App = {
|
|||
version: process.env.VERSION,
|
||||
versionFull: process.env.VERSION_FULL,
|
||||
build: +process.env.BUILD,
|
||||
langPackVersion: '0.9.3',
|
||||
langPackVersion: '0.9.7',
|
||||
langPack: 'webk',
|
||||
langPackCode: 'en',
|
||||
domains: MAIN_DOMAINS,
|
||||
|
|
|
@ -4,15 +4,16 @@
|
|||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import {AppMediaPlaybackController} from '../components/appMediaPlaybackController';
|
||||
import type {LiteModeKey} from '../helpers/liteMode';
|
||||
import type {AppMediaPlaybackController} from '../components/appMediaPlaybackController';
|
||||
import type {TopPeerType, MyTopPeer} from '../lib/appManagers/appUsersManager';
|
||||
import type {AutoDownloadSettings, BaseTheme, NotifyPeer, PeerNotifySettings, Theme, ThemeSettings, WallPaper} from '../layer';
|
||||
import type DialogsStorage from '../lib/storages/dialogs';
|
||||
import type FiltersStorage from '../lib/storages/filters';
|
||||
import type {AuthState, Modify} from '../types';
|
||||
import {IS_MOBILE} from '../environment/userAgent';
|
||||
import getTimeFormat from '../helpers/getTimeFormat';
|
||||
import {nextRandomUint} from '../helpers/random';
|
||||
import {AutoDownloadSettings, BaseTheme, NotifyPeer, PeerNotifySettings, Theme, ThemeSettings, WallPaper} from '../layer';
|
||||
import {TopPeerType, MyTopPeer} from '../lib/appManagers/appUsersManager';
|
||||
import DialogsStorage from '../lib/storages/dialogs';
|
||||
import FiltersStorage from '../lib/storages/filters';
|
||||
import {AuthState, Modify} from '../types';
|
||||
import App from './app';
|
||||
|
||||
const STATE_VERSION = App.version;
|
||||
|
@ -74,7 +75,7 @@ export type State = {
|
|||
messagesTextSize: number,
|
||||
distanceUnit: 'kilometers' | 'miles',
|
||||
sendShortcut: 'enter' | 'ctrlEnter',
|
||||
animationsEnabled: boolean,
|
||||
animationsEnabled?: boolean, // ! DEPRECATED
|
||||
autoDownload: {
|
||||
contacts?: boolean, // ! DEPRECATED
|
||||
private?: boolean, // ! DEPRECATED
|
||||
|
@ -85,7 +86,7 @@ export type State = {
|
|||
file: AutoDownloadPeerTypeSettings
|
||||
},
|
||||
autoDownloadNew: AutoDownloadSettings,
|
||||
autoPlay: {
|
||||
autoPlay?: { // ! DEPRECATED
|
||||
gifs: boolean,
|
||||
videos: boolean
|
||||
},
|
||||
|
@ -104,7 +105,8 @@ export type State = {
|
|||
sound: boolean
|
||||
},
|
||||
nightTheme?: boolean, // ! DEPRECATED
|
||||
timeFormat: 'h12' | 'h23'
|
||||
timeFormat: 'h12' | 'h23',
|
||||
liteMode: {[key in LiteModeKey]: boolean}
|
||||
},
|
||||
playbackParams: ReturnType<AppMediaPlaybackController['getPlaybackParams']>,
|
||||
keepSigned: boolean,
|
||||
|
@ -233,7 +235,6 @@ export const STATE_INIT: State = {
|
|||
messagesTextSize: 16,
|
||||
distanceUnit: 'kilometers',
|
||||
sendShortcut: 'enter',
|
||||
animationsEnabled: true,
|
||||
autoDownload: {
|
||||
photo: {
|
||||
contacts: true,
|
||||
|
@ -265,10 +266,6 @@ export const STATE_INIT: State = {
|
|||
video_size_max: 15728640,
|
||||
video_upload_maxbitrate: 100
|
||||
},
|
||||
autoPlay: {
|
||||
gifs: true,
|
||||
videos: true
|
||||
},
|
||||
stickers: {
|
||||
suggest: true,
|
||||
loop: true
|
||||
|
@ -285,7 +282,26 @@ export const STATE_INIT: State = {
|
|||
notifications: {
|
||||
sound: false
|
||||
},
|
||||
timeFormat: getTimeFormat()
|
||||
timeFormat: getTimeFormat(),
|
||||
liteMode: {
|
||||
all: false,
|
||||
animations: false,
|
||||
chat: false,
|
||||
chat_background: false,
|
||||
chat_spoilers: false,
|
||||
effects: false,
|
||||
effects_premiumstickers: false,
|
||||
effects_reactions: false,
|
||||
effects_emoji: false,
|
||||
emoji: false,
|
||||
emoji_messages: false,
|
||||
emoji_panel: false,
|
||||
gif: false,
|
||||
stickers: false,
|
||||
stickers_chat: false,
|
||||
stickers_panel: false,
|
||||
video: false
|
||||
}
|
||||
},
|
||||
playbackParams: {
|
||||
volume: 1,
|
||||
|
|
|
@ -12,6 +12,7 @@ import roundRect from './canvas/roundRect';
|
|||
import Shimmer from './canvas/shimmer';
|
||||
import customProperties from './dom/customProperties';
|
||||
import easeInOutSine from './easing/easeInOutSine';
|
||||
import liteMode from './liteMode';
|
||||
import mediaSizes from './mediaSizes';
|
||||
|
||||
export default class DialogsPlaceholder {
|
||||
|
@ -91,7 +92,7 @@ export default class DialogsPlaceholder {
|
|||
this.availableLength = availableLength;
|
||||
this.detachTime = Date.now();
|
||||
|
||||
if(!rootScope.settings.animationsEnabled) {
|
||||
if(!liteMode.isAvailable('animations')) {
|
||||
this.remove();
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +133,7 @@ export default class DialogsPlaceholder {
|
|||
|
||||
if(!detachTime) {
|
||||
return;
|
||||
} else if(!rootScope.settings.animationsEnabled) {
|
||||
} else if(!liteMode.isAvailable('animations')) {
|
||||
this.remove();
|
||||
return;
|
||||
}
|
||||
|
@ -219,7 +220,7 @@ export default class DialogsPlaceholder {
|
|||
}
|
||||
|
||||
// ! should've removed the loop if animations are disabled
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
if(liteMode.isAvailable('animations')) {
|
||||
this.renderFrame();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import fastSmoothScroll from '../fastSmoothScroll';
|
||||
import cancelEvent from './cancelEvent';
|
||||
import {attachClickEvent, detachClickEvent} from './clickEvent';
|
||||
import {attachClickEvent} from './clickEvent';
|
||||
import findUpAsChild from './findUpAsChild';
|
||||
import findUpClassName from './findUpClassName';
|
||||
|
||||
|
@ -147,7 +147,7 @@ export default function attachListNavigation({list, type, onSelect, once, waitFo
|
|||
}
|
||||
};
|
||||
|
||||
let attached = false;
|
||||
let attached = false, detachClickEvent: () => void;
|
||||
const attach = () => {
|
||||
if(attached) return;
|
||||
attached = true;
|
||||
|
@ -155,7 +155,7 @@ export default function attachListNavigation({list, type, onSelect, once, waitFo
|
|||
// input.addEventListener(HANDLE_EVENT, onKeyDown, {capture: true, passive: false});
|
||||
document.addEventListener(HANDLE_EVENT, onKeyDown, {capture: true, passive: false});
|
||||
list.addEventListener('mousemove', onMouseMove, {passive: true});
|
||||
attachClickEvent(list, onClick);
|
||||
detachClickEvent = attachClickEvent(list, onClick);
|
||||
};
|
||||
|
||||
const detach = () => {
|
||||
|
@ -164,7 +164,8 @@ export default function attachListNavigation({list, type, onSelect, once, waitFo
|
|||
// input.removeEventListener(HANDLE_EVENT, onKeyDown, {capture: true});
|
||||
document.removeEventListener(HANDLE_EVENT, onKeyDown, {capture: true});
|
||||
list.removeEventListener('mousemove', onMouseMove);
|
||||
detachClickEvent(list, onClick);
|
||||
detachClickEvent();
|
||||
detachClickEvent = undefined;
|
||||
};
|
||||
|
||||
const resetTarget = () => {
|
||||
|
|
|
@ -71,13 +71,13 @@ export function attachClickEvent(elem: HTMLElement | Window, callback: (e: /* To
|
|||
return () => remove(CLICK_EVENT_NAME, callback, options);
|
||||
}
|
||||
|
||||
export function detachClickEvent(elem: HTMLElement | Window, callback: (e: /* TouchEvent | */MouseEvent) => void, options?: AddEventListenerOptions) {
|
||||
// if(CLICK_EVENT_NAME === 'touchend') {
|
||||
// elem.removeEventListener('touchstart', callback, options);
|
||||
// } else {
|
||||
elem.removeEventListener(CLICK_EVENT_NAME, callback as any, options);
|
||||
// }
|
||||
}
|
||||
// export function detachClickEvent(elem: HTMLElement | Window, callback: (e: /* TouchEvent | */MouseEvent) => void, options?: AddEventListenerOptions) {
|
||||
// // if(CLICK_EVENT_NAME === 'touchend') {
|
||||
// // elem.removeEventListener('touchstart', callback, options);
|
||||
// // } else {
|
||||
// elem.removeEventListener(CLICK_EVENT_NAME, callback as any, options);
|
||||
// // }
|
||||
// }
|
||||
|
||||
export function simulateClickEvent(elem: HTMLElement) {
|
||||
simulateEvent(elem, CLICK_EVENT_NAME);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import rootScope from '../../lib/rootScope';
|
||||
import liteMode from '../liteMode';
|
||||
|
||||
export default function shake(element: HTMLElement) {
|
||||
if(!rootScope.settings.animationsEnabled) {
|
||||
if(!liteMode.isAvailable('animations')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import {ScrollableBase} from '../../components/scrollable';
|
|||
import SwipeHandler from '../../components/swipeHandler';
|
||||
import IS_TOUCH_SUPPORTED from '../../environment/touchSupport';
|
||||
import rootScope from '../../lib/rootScope';
|
||||
import liteMode from '../liteMode';
|
||||
import {Middleware} from '../middleware';
|
||||
import clamp from '../number/clamp';
|
||||
import safeAssign from '../object/safeAssign';
|
||||
|
@ -156,7 +157,7 @@ export default class Sortable {
|
|||
attachClickEvent(document.body, cancelEvent, {capture: true, once: true});
|
||||
}
|
||||
|
||||
if(rootScope.settings.animationsEnabled) {
|
||||
if(liteMode.isAvailable('animations')) {
|
||||
await pause(250);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import {attachClickEvent, detachClickEvent} from './dom/clickEvent';
|
||||
import {attachClickEvent} from './dom/clickEvent';
|
||||
import findUpAsChild from './dom/findUpAsChild';
|
||||
import EventListenerBase from './eventListenerBase';
|
||||
import ListenerSetter from './listenerSetter';
|
||||
|
@ -13,6 +13,7 @@ import safeAssign from './object/safeAssign';
|
|||
import appNavigationController, {NavigationItem} from '../components/appNavigationController';
|
||||
import findUpClassName from './dom/findUpClassName';
|
||||
import rootScope from '../lib/rootScope';
|
||||
import liteMode from './liteMode';
|
||||
|
||||
const KEEP_OPEN = false;
|
||||
const TOGGLE_TIMEOUT = 200;
|
||||
|
@ -36,6 +37,7 @@ export default class DropdownHover extends EventListenerBase<{
|
|||
protected navigationItem: NavigationItem;
|
||||
protected ignoreOutClickClassName: string;
|
||||
protected timeouts: {[type in DropdownHoverTimeoutType]?: number};
|
||||
protected detachClickEvent: () => void;
|
||||
|
||||
constructor(options: {
|
||||
element: DropdownHover['element'],
|
||||
|
@ -86,7 +88,7 @@ export default class DropdownHover extends EventListenerBase<{
|
|||
if(ignore && !this.ignoreMouseOut.size) {
|
||||
this.ignoreButtons.add(button);
|
||||
setTimeout(() => {
|
||||
attachClickEvent(window, this.onClickOut, {capture: true});
|
||||
this.detachClickEvent = attachClickEvent(window, this.onClickOut, {capture: true});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
@ -171,7 +173,7 @@ export default class DropdownHover extends EventListenerBase<{
|
|||
return;
|
||||
}
|
||||
|
||||
const delay = IS_TOUCH_SUPPORTED || !rootScope.settings.animationsEnabled ? 0 : ANIMATION_DURATION;
|
||||
const delay = IS_TOUCH_SUPPORTED || !liteMode.isAvailable('animations') ? 0 : ANIMATION_DURATION;
|
||||
if((this.element.style.display && enable === undefined) || enable) {
|
||||
const res = this.dispatchResultableEvent('open');
|
||||
await Promise.all(res);
|
||||
|
@ -213,7 +215,8 @@ export default class DropdownHover extends EventListenerBase<{
|
|||
this.element.classList.remove('active');
|
||||
|
||||
appNavigationController.removeItem(this.navigationItem);
|
||||
detachClickEvent(window, this.onClickOut, {capture: true});
|
||||
this.detachClickEvent?.();
|
||||
this.detachClickEvent = undefined;
|
||||
|
||||
this.clearTimeout('toggle');
|
||||
this.setTimeout('done', () => {
|
||||
|
|
|
@ -11,6 +11,7 @@ import {fastRafPromise} from './schedulers';
|
|||
import {animateSingle, cancelAnimationByKey} from './animation';
|
||||
import rootScope from '../lib/rootScope';
|
||||
import isInDOM from './dom/isInDOM';
|
||||
import liteMode from './liteMode';
|
||||
|
||||
const MIN_JS_DURATION = 250;
|
||||
const MAX_JS_DURATION = 600;
|
||||
|
@ -58,7 +59,7 @@ export default function fastSmoothScroll(options: ScrollOptions) {
|
|||
options.axis ??= 'y';
|
||||
// return;
|
||||
|
||||
if(!rootScope.settings.animationsEnabled || options.forceDuration === 0) {
|
||||
if(!liteMode.isAvailable('animations') || options.forceDuration === 0) {
|
||||
options.forceDirection = FocusDirection.Static;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import {MOUNT_CLASS_TO} from '../config/debug';
|
||||
import rootScope from '../lib/rootScope';
|
||||
|
||||
export type LiteModeKey = 'all' | 'gif' | 'video' |
|
||||
'emoji' | 'emoji_panel' | 'emoji_messages' |
|
||||
'effects' | 'effects_reactions' | 'effects_premiumstickers' | 'effects_emoji' |
|
||||
'stickers' | 'stickers_panel' | 'stickers_chat' |
|
||||
'chat' | 'chat_background' | 'chat_spoilers' | 'animations';
|
||||
|
||||
export class LiteMode {
|
||||
public isAvailable(key: LiteModeKey) {
|
||||
return !rootScope.settings.liteMode.all && !rootScope.settings.liteMode[key];
|
||||
}
|
||||
}
|
||||
|
||||
const liteMode = new LiteMode();
|
||||
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.liteMode = liteMode);
|
||||
export default liteMode;
|
25
src/lang.ts
25
src/lang.ts
|
@ -115,6 +115,17 @@ const lang = {
|
|||
'Link.Available': 'Link is available',
|
||||
'Link.Taken': 'Link is already taken',
|
||||
'Link.Invalid': 'Link is invalid',
|
||||
'LiteMode.Key.chat.Title': 'Chat Animations',
|
||||
'LiteMode.Key.chat_background.Title': 'Background rotation',
|
||||
'LiteMode.Key.chat_spoilers.Title': 'Animated spoiler effect',
|
||||
'LiteMode.Key.stickers_panel.Title': 'Autoplay in panel',
|
||||
'LiteMode.Key.stickers_chat.Title': 'Autoplay in chat',
|
||||
'LiteMode.Key.emoji_panel.Title': 'Autoplay in panel',
|
||||
'LiteMode.Key.emoji_messages.Title': 'Autoplay in messages',
|
||||
'LiteMode.Key.effects.Title': 'Interactive Effects',
|
||||
'LiteMode.Key.effects_reactions.Title': 'Reaction effect',
|
||||
'LiteMode.Key.effects_premiumstickers.Title': 'Premium stickers effect',
|
||||
'LiteMode.Key.effects_emoji.Title': 'Emoji effect',
|
||||
'StickersTab.SearchPlaceholder': 'Search Stickers',
|
||||
'ForwardedFrom': 'Forwarded from %s',
|
||||
'Popup.Avatar.Title': 'Drag to Reposition',
|
||||
|
@ -1178,6 +1189,20 @@ const lang = {
|
|||
'other_value': 'last seen %d hours ago'
|
||||
},
|
||||
'Login.Register.LastName.Placeholder': 'Last Name',
|
||||
'LiteMode.Title': 'Power Saving',
|
||||
'LiteMode.Key.emoji.Title': 'Emoji Animations',
|
||||
'LiteMode.Key.gif.Title': 'Autoplay GIFs',
|
||||
'LiteMode.Key.video.Title': 'Autoplay Videos',
|
||||
'LiteMode.Key.stickers.Title': 'Sticker Animations',
|
||||
'LiteMode.Key.animations.Title': 'Interface Animations',
|
||||
// 'LiteMode.Key.emoji.Info': 'Loop animated emoji in messages, reactions and statuses.',
|
||||
// 'LiteMode.Key.gif.Info': 'Autoplay and loop GIFs in chats and in the keyboard.',
|
||||
// 'LiteMode.Key.video.Info': 'Autoplay and loop videos and video messages in chats.',
|
||||
// 'LiteMode.Key.stickers.Info': 'Loop animated stickers, in chats and in the keyboard.',
|
||||
// 'LiteMode.Key.animations.Info': 'Other animations that make Telegram look amazing.',
|
||||
'LiteMode.Info': 'Reduce all power-intensive animations and improve performance.',
|
||||
'LiteMode.EnableText': 'Power Saving Mode',
|
||||
'LiteMode.DisableAlert': 'Disable Power Saving Mode',
|
||||
'Message.Context.Select': 'Select',
|
||||
'Message.Context.Pin': 'Pin',
|
||||
'Message.Context.Unpin': 'Unpin',
|
||||
|
|
|
@ -105,6 +105,8 @@ import PopupForward from '../../components/popups/forward';
|
|||
import AppBackgroundTab from '../../components/sidebarLeft/tabs/background';
|
||||
import partition from '../../helpers/array/partition';
|
||||
import indexOfAndSplice from '../../helpers/array/indexOfAndSplice';
|
||||
import liteMode, {LiteModeKey} from '../../helpers/liteMode';
|
||||
import RLottiePlayer from '../rlottie/rlottiePlayer';
|
||||
|
||||
export type ChatSavedPosition = {
|
||||
mids: number[],
|
||||
|
@ -458,6 +460,29 @@ export class AppImManager extends EventListenerBase<{
|
|||
});
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
const mediaStickerWrapper = findUpClassName(e.target, 'media-sticker-wrapper');
|
||||
if(!mediaStickerWrapper ||
|
||||
mediaStickerWrapper.classList.contains('custom-emoji') ||
|
||||
findUpClassName(e.target, 'emoji-big')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const animations = animationIntersector.getAnimations(mediaStickerWrapper);
|
||||
animations?.forEach((animationItem) => {
|
||||
const {liteModeKey, animation} = animationItem;
|
||||
if(!liteModeKey || !animation?.paused || liteMode.isAvailable(liteModeKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(animation instanceof RLottiePlayer) {
|
||||
animation.playOrRestart();
|
||||
} else {
|
||||
animation.play();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
rootScope.addEventListener('sticker_updated', ({type, faved}) => {
|
||||
if(type === 'faved') {
|
||||
toastNew({
|
||||
|
@ -1006,7 +1031,7 @@ export class AppImManager extends EventListenerBase<{
|
|||
private toggleChatGradientAnimation(activatingChat: Chat) {
|
||||
this.chats.forEach((chat) => {
|
||||
if(chat.gradientRenderer) {
|
||||
chat.gradientRenderer.scrollAnimate(rootScope.settings.animationsEnabled && chat === activatingChat);
|
||||
chat.gradientRenderer.scrollAnimate(liteMode.isAvailable('animations') && chat === activatingChat);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1640,18 +1665,21 @@ export class AppImManager extends EventListenerBase<{
|
|||
});
|
||||
}
|
||||
|
||||
document.body.classList.toggle('animation-level-0', !rootScope.settings.animationsEnabled);
|
||||
document.body.classList.toggle('animation-level-0', !liteMode.isAvailable('animations'));
|
||||
document.body.classList.toggle('animation-level-1', false);
|
||||
document.body.classList.toggle('animation-level-2', rootScope.settings.animationsEnabled);
|
||||
document.body.classList.toggle('animation-level-2', liteMode.isAvailable('animations'));
|
||||
|
||||
this.chatsSelectTabDebounced = debounce(() => {
|
||||
const topbar = this.chat.topbar;
|
||||
topbar.pinnedMessage?.setCorrectIndex(0); // * буду молиться богам, чтобы это ничего не сломало, но это исправляет получение пиннеда после анимации
|
||||
|
||||
this.managers.apiFileManager.setQueueId(this.chat.bubbles.lazyLoadQueue.queueId);
|
||||
}, rootScope.settings.animationsEnabled ? 250 : 0, false, true);
|
||||
}, liteMode.isAvailable('animations') ? 250 : 0, false, true);
|
||||
|
||||
if(lottieLoader.setLoop(rootScope.settings.stickers.loop)) {
|
||||
const c: LiteModeKey[] = ['stickers_chat', 'stickers_panel'];
|
||||
const changedLoop = lottieLoader.setLoop(rootScope.settings.stickers.loop);
|
||||
const changedAutoplay = !!c.filter((key) => lottieLoader.setAutoplay(liteMode.isAvailable(key), key)).length;
|
||||
if(changedLoop || changedAutoplay) {
|
||||
animationIntersector.checkAnimations2(false);
|
||||
}
|
||||
|
||||
|
@ -1680,7 +1708,7 @@ export class AppImManager extends EventListenerBase<{
|
|||
this.chatsSelectTabDebounced();
|
||||
|
||||
// ! нужно переделать на animation, так как при лаге анимация будет длиться не 250мс
|
||||
if(rootScope.settings.animationsEnabled && animate !== false) {
|
||||
if(liteMode.isAvailable('animations') && animate !== false) {
|
||||
dispatchHeavyAnimationEvent(pause(250 + 150), 250 + 150);
|
||||
}
|
||||
|
||||
|
@ -1928,11 +1956,11 @@ export class AppImManager extends EventListenerBase<{
|
|||
|
||||
this.log('selectTab', id, prevTabId);
|
||||
|
||||
let animationPromise: Promise<any> = rootScope.settings.animationsEnabled ? doubleRaf() : Promise.resolve();
|
||||
let animationPromise: Promise<any> = liteMode.isAvailable('animations') ? doubleRaf() : Promise.resolve();
|
||||
if(
|
||||
prevTabId !== undefined &&
|
||||
prevTabId !== id &&
|
||||
rootScope.settings.animationsEnabled &&
|
||||
liteMode.isAvailable('animations') &&
|
||||
animate !== false/* &&
|
||||
mediaSizes.activeScreen !== ScreenSize.large */
|
||||
) {
|
||||
|
|
|
@ -87,7 +87,11 @@ export class CustomEmojiElement extends HTMLElement {
|
|||
// }
|
||||
|
||||
if(this.player) {
|
||||
animationIntersector.addAnimation(this, this.renderer.animationGroup, undefined, true);
|
||||
animationIntersector.addAnimation({
|
||||
animation: this,
|
||||
group: this.renderer.animationGroup,
|
||||
controlled: true
|
||||
});
|
||||
}
|
||||
|
||||
// this.connectedCallback = undefined;
|
||||
|
@ -185,7 +189,7 @@ export class CustomEmojiElement extends HTMLElement {
|
|||
this.paused = false;
|
||||
|
||||
if(this.player instanceof HTMLVideoElement) {
|
||||
this.player.currentTime = this.renderer.lastPausedVideo?.currentTime || this.player.currentTime;
|
||||
this.player.currentTime = this.renderer.lastPausedVideo?.currentTime ?? this.player.currentTime;
|
||||
this.player.play().catch(noop);
|
||||
}
|
||||
|
||||
|
@ -206,6 +210,10 @@ export class CustomEmojiElement extends HTMLElement {
|
|||
public get autoplay() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public get loop() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
type CustomEmojiElements = Set<CustomEmojiElement>;
|
||||
|
@ -648,7 +656,11 @@ export class CustomEmojiRendererElement extends HTMLElement {
|
|||
}
|
||||
|
||||
if(element.isConnected) {
|
||||
animationIntersector.addAnimation(element, element.renderer.animationGroup, undefined, true);
|
||||
animationIntersector.addAnimation({
|
||||
animation: element,
|
||||
group: element.renderer.animationGroup,
|
||||
controlled: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import type {LiteModeKey} from '../../helpers/liteMode';
|
||||
import animationIntersector, {AnimationItemGroup} from '../../components/animationIntersector';
|
||||
import {MOUNT_CLASS_TO} from '../../config/debug';
|
||||
import pause from '../../helpers/schedulers/pause';
|
||||
|
@ -45,6 +46,20 @@ export class LottieLoader {
|
|||
return null;
|
||||
}
|
||||
|
||||
public setAutoplay(play: boolean, liteModeKey: LiteModeKey) {
|
||||
let changed = false;
|
||||
for(const i in this.players) {
|
||||
const player = this.players[i];
|
||||
if(player.liteModeKey === liteModeKey) {
|
||||
changed = true;
|
||||
player.autoplay = play ? !!+player.el[0].dataset.stickerPlay : false;
|
||||
player.loop = play ? !!+player.el[0].dataset.stickerLoop : false;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
public setLoop(loop: boolean) {
|
||||
let changed = false;
|
||||
for(const i in this.players) {
|
||||
|
@ -196,7 +211,12 @@ export class LottieLoader {
|
|||
|
||||
const player = this.initPlayer(containers, params);
|
||||
|
||||
animationIntersector.addAnimation(player, group, undefined, middleware);
|
||||
animationIntersector.addAnimation({
|
||||
animation: player,
|
||||
group,
|
||||
controlled: middleware,
|
||||
liteModeKey: params.liteModeKey
|
||||
});
|
||||
|
||||
return player;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import liteMode from '../../helpers/liteMode';
|
||||
import noop from '../../helpers/noop';
|
||||
import safeAssign from '../../helpers/object/safeAssign';
|
||||
import rootScope from '../rootScope';
|
||||
|
@ -181,7 +182,7 @@ export default class RLottieIcon {
|
|||
|
||||
const part = item.getPart(index);
|
||||
item.player.playPart({
|
||||
from: rootScope.settings.animationsEnabled && !this.skipAnimation ? part.startFrame : part.endFrame,
|
||||
from: liteMode.isAvailable('animations') && !this.skipAnimation ? part.startFrame : part.endFrame,
|
||||
to: part.endFrame,
|
||||
callback
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import type {AnimationItemGroup, AnimationItemWrapper} from '../../components/animationIntersector';
|
||||
import type {Middleware} from '../../helpers/middleware';
|
||||
import type {LiteModeKey} from '../../helpers/liteMode';
|
||||
import CAN_USE_TRANSFERABLES from '../../environment/canUseTransferables';
|
||||
import IS_APPLE_MX from '../../environment/appleMx';
|
||||
import {IS_ANDROID, IS_APPLE_MOBILE, IS_APPLE, IS_SAFARI} from '../../environment/userAgent';
|
||||
|
@ -35,7 +36,8 @@ export type RLottieOptions = {
|
|||
name?: string,
|
||||
skipFirstFrameRendering?: boolean,
|
||||
toneIndex?: number,
|
||||
sync?: boolean
|
||||
sync?: boolean,
|
||||
liteModeKey?: LiteModeKey
|
||||
};
|
||||
|
||||
export type RLottieColor = [number, number, number];
|
||||
|
@ -92,6 +94,7 @@ export default class RLottiePlayer extends EventListenerBase<{
|
|||
public loop: number | boolean = true;
|
||||
public _loop: RLottiePlayer['loop']; // ! will be used to store original value for settings.stickers.loop
|
||||
public group: AnimationItemGroup = '';
|
||||
public liteModeKey: LiteModeKey;
|
||||
|
||||
private frInterval: number;
|
||||
private frThen: number;
|
||||
|
@ -153,6 +156,7 @@ export default class RLottiePlayer extends EventListenerBase<{
|
|||
this.skipFirstFrameRendering = options.skipFirstFrameRendering;
|
||||
this.toneIndex = options.toneIndex;
|
||||
this.raw = this.color !== undefined;
|
||||
this.liteModeKey = options.liteModeKey;
|
||||
|
||||
if(this.name) {
|
||||
this.cacheName = RLottiePlayer.CACHE.generateName(this.name, this.width, this.height, this.color, this.toneIndex);
|
||||
|
@ -284,6 +288,18 @@ export default class RLottiePlayer extends EventListenerBase<{
|
|||
this.play();
|
||||
}
|
||||
|
||||
public playOrRestart() {
|
||||
if(!this.paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.curFrame === this.maxFrame) {
|
||||
this.restart();
|
||||
} else {
|
||||
this.play();
|
||||
}
|
||||
}
|
||||
|
||||
public setSpeed(speed: number) {
|
||||
if(this.speed === speed) {
|
||||
return;
|
||||
|
|
|
@ -66,9 +66,9 @@
|
|||
left: -15%;
|
||||
|
||||
background-color: var(--primary-color);
|
||||
transform: scale(1);
|
||||
transform: scale(0);
|
||||
border-radius: 50%;
|
||||
transition: transform .2s 0s ease-in-out;
|
||||
transition: transform .2s .05s ease-in-out;
|
||||
|
||||
@include animation-level(0) {
|
||||
transition: none !important;
|
||||
|
@ -87,10 +87,10 @@
|
|||
stroke: #fff;
|
||||
stroke-width: 3.75;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 24.19, 24.19;
|
||||
stroke-dasharray: 0, 24.19;
|
||||
stroke-dashoffset: 0;
|
||||
transition: stroke-dasharray .1s .15s ease-in-out, visibility 0s .15s;
|
||||
visibility: visible; // fix blinking on parent's transform
|
||||
transition: stroke-dasharray .1s ease-in-out, visibility 0s .1s;
|
||||
visibility: hidden; // fix blinking on parent's transform
|
||||
|
||||
@include animation-level(0) {
|
||||
transition: none !important;
|
||||
|
@ -287,18 +287,18 @@
|
|||
}
|
||||
|
||||
.checkbox-field .checkbox-field-input {
|
||||
&:not(:checked) + .checkbox-box {
|
||||
&:checked:not(.is-fake-disabled) + .checkbox-box {
|
||||
.checkbox-box-check {
|
||||
use {
|
||||
stroke-dasharray: 0, 24.19;
|
||||
visibility: hidden;
|
||||
transition: stroke-dasharray .1s ease-in-out, visibility 0s .1s;
|
||||
stroke-dasharray: 24.19, 24.19;
|
||||
visibility: visible;
|
||||
transition: stroke-dasharray .1s .15s ease-in-out, visibility 0s .15s;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-box-background {
|
||||
transition: transform .2s .05s ease-in-out;
|
||||
transform: scale(0);
|
||||
transition: transform .2s 0s ease-in-out;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -402,7 +402,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
[type="checkbox"]:checked + .checkbox-toggle {
|
||||
[type="checkbox"]:checked:not(.is-fake-disabled) + .checkbox-toggle {
|
||||
background-color: var(--primary-color);
|
||||
|
||||
&:before {
|
||||
|
|
|
@ -1204,6 +1204,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.power-saving-container {
|
||||
.row.is-disabled {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.empty-placeholder {
|
||||
// left: 50%;
|
||||
// transform: translate(-50%, -50%);
|
||||
|
|
Loading…
Reference in New Issue