Limit popup

This commit is contained in:
Eduard Kuzmenko 2023-01-14 01:28:45 +04:00
parent beabcb2a23
commit 6984d20674
11 changed files with 231 additions and 142 deletions

View File

@ -5875,8 +5875,8 @@ export default class ChatBubbles {
const message = await this.generateLocalFirstMessage(false, (message) => {
const botInfo = userFull.bot_info;
message.message = botInfo.description;
if(botInfo.description_document) message.media = {_: 'messageMediaDocument', document: botInfo.description_document};
if(botInfo.description_photo) message.media = {_: 'messageMediaPhoto', photo: botInfo.description_photo};
if(botInfo.description_document) message.media = {_: 'messageMediaDocument', document: botInfo.description_document, pFlags: {}};
if(botInfo.description_photo) message.media = {_: 'messageMediaPhoto', photo: botInfo.description_photo, pFlags: {}};
});
if(!middleware()) {

View File

@ -13,15 +13,13 @@ import {ButtonMenuItemOptionsVerifiable, ButtonMenuSync} from './buttonMenu';
import PopupDeleteDialog from './popups/deleteDialog';
import {i18n, LangPackKey, _i18n} from '../lib/langPack';
import findUpTag from '../helpers/dom/findUpTag';
import PopupPeer, {PopupPeerButton} from './popups/peer';
import AppChatFoldersTab from './sidebarLeft/tabs/chatFolders';
import appSidebarLeft from './sidebarLeft';
import {toastNew} from './toast';
import PopupMute from './popups/mute';
import {AppManagers} from '../lib/appManagers/managers';
import positionMenu from '../helpers/positionMenu';
import contextMenuController from '../helpers/contextMenuController';
import {GENERAL_TOPIC_ID} from '../lib/mtproto/mtproto_config';
import showLimitPopup from './popups/limit';
export default class DialogsContextMenu {
private element: HTMLElement;
@ -182,121 +180,7 @@ export default class DialogsContextMenu {
} else if(filterId >= 1) {
toastNew({langPackKey: 'PinFolderLimitReached'});
} else {
// const a: {[type in ApiLimitType]?: {
// title: LangPackKey,
// description: LangPackKey,
// descriptionPremium: LangPackKey,
// descriptionLocked: LangPackKey,
// icon: string
// }} = {
// pin: {
// title: 'LimitReached',
// description: 'LimitReachedPinDialogs',
// descriptionPremium: 'LimitReachedPinDialogsPremium',
// descriptionLocked: 'LimitReachedPinDialogsLocked',
// icon: 'limit_pin'
// }
// };
// class P extends PopupPeer {
// constructor(options: {
// isPremium: boolean,
// limit: number,
// limitPremium: number
// }, _a: typeof a[keyof typeof a]) {
// super('popup-limit', {
// buttons: options.isPremium === undefined ? [{
// langKey: 'LimitReached.Ok',
// isCancel: true
// }] : (options.isPremium ? [{
// langKey: 'OK',
// isCancel: true
// }] : [{
// langKey: 'IncreaseLimit',
// callback: () => {
// }
// }, {
// langKey: 'Cancel',
// isCancel: true
// }]),
// descriptionLangKey: options.isPremium === undefined ? _a.descriptionLocked : (options.isPremium ? _a.descriptionPremium : _a.description),
// descriptionLangArgs: options.isPremium ? [options.limitPremium] : [options.limit, options.limitPremium],
// titleLangKey: _a.title
// });
// const isLocked = options.isPremium === undefined;
// if(isLocked) {
// this.element.classList.add('is-locked');
// }
// const limitContainer = document.createElement('div');
// limitContainer.classList.add('popup-limit-line');
// const hint = document.createElement('div');
// hint.classList.add('popup-limit-hint');
// const i = document.createElement('span');
// i.classList.add('popup-limit-hint-icon', 'tgico-' + _a.icon);
// hint.append(i, '' + (options.isPremium ? options.limitPremium : options.limit));
// limitContainer.append(hint);
// if(!isLocked) {
// const limit = document.createElement('div');
// limit.classList.add('limit-line');
// const free = document.createElement('div');
// free.classList.add('limit-line-free');
// const premium = document.createElement('div');
// premium.classList.add('limit-line-premium');
// limit.append(free, premium);
// _i18n(free, 'LimitFree');
// premium.append(i18n('LimitPremium'), '' + options.limitPremium);
// limitContainer.append(limit);
// }
// this.container.insertBefore(limitContainer, this.description);
// if(options.isPremium === false) {
// this.buttons.pop().element.remove();
// }
// }
// }
// async function showLimitPopup(type: keyof typeof a) {
// const _a = a[type];
// const [appConfig, limit, limitPremium] = await Promise.all([
// rootScope.managers.apiManager.getAppConfig(),
// ...[false, true].map((v) => rootScope.managers.apiManager.getLimit(type, v))
// ]);
// const isLocked = appConfig.premium_purchase_blocked;
// new P({
// isPremium: isLocked ? undefined : rootScope.premium,
// limit,
// limitPremium
// }, _a).show();
// }
// showLimitPopup('pin');
const config = await this.managers.apiManager.getConfig();
new PopupPeer('pinned-dialogs-too-much', {
buttons: [{
langKey: 'OK',
isCancel: true
}, {
langKey: 'FiltersSetupPinAlert',
callback: () => {
appSidebarLeft.createTab(AppChatFoldersTab).open();
}
}],
descriptionLangKey: 'PinToTopLimitReached2',
descriptionLangArgs: [i18n('Chats', [config.pinned_dialogs_count_max])]
}).show();
showLimitPopup('pin');
}
}
});

View File

@ -719,7 +719,7 @@ export default class EmojiTab extends EmoticonsTabC<EmojiTabCategory> {
) {
const a = document.createElement('a');
a.onclick = () => {
appImManager.openUsername({userName: 'premiumbot'});
appImManager.openPremiumBot();
hideToast();
};
toastNew({

View File

@ -28,9 +28,10 @@ export type PopupButton = {
callback?: () => void,
langKey?: LangPackKey,
langArgs?: any[],
isDanger?: true,
isCancel?: true,
element?: HTMLButtonElement
isDanger?: boolean,
isCancel?: boolean,
element?: HTMLButtonElement,
noRipple?: boolean
};
export type PopupOptions = Partial<{
@ -180,16 +181,18 @@ export default class PopupElement<T extends EventListenerListeners = {}> extends
const button = document.createElement('button');
button.className = 'btn' + (b.isDanger ? ' danger' : ' primary');
ripple(button);
if(!b.noRipple) {
ripple(button);
}
if(b.text) {
button.innerHTML = b.text;
button.textContent = b.text;
} else {
button.append(i18n(b.langKey, b.langArgs));
}
attachClickEvent(button, () => {
b.callback && b.callback();
b.callback?.();
this.destroy();
}, {listenerSetter: this.listenerSetter, once: true});

View File

@ -0,0 +1,137 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import {doubleRaf} from '../../helpers/schedulers';
import appImManager from '../../lib/appManagers/appImManager';
import {LangPackKey, _i18n, i18n} from '../../lib/langPack';
import {ApiLimitType} from '../../lib/mtproto/api_methods';
import rootScope from '../../lib/rootScope';
import PopupPeer from './peer';
const a: {[type in ApiLimitType]?: {
title: LangPackKey,
description: LangPackKey,
descriptionPremium: LangPackKey,
descriptionLocked: LangPackKey,
icon: string
}} = {
pin: {
title: 'LimitReached',
description: 'LimitReachedPinDialogs',
descriptionPremium: 'LimitReachedPinDialogsPremium',
descriptionLocked: 'LimitReachedPinDialogsLocked',
icon: 'limit_pin'
},
folders: {
title: 'LimitReached',
description: 'LimitReachedFolders',
descriptionPremium: 'LimitReachedFoldersPremium',
descriptionLocked: 'LimitReachedFoldersLocked',
icon: 'limit_folders'
}
};
class P extends PopupPeer {
constructor(options: {
isPremium: boolean,
limit: number,
limitPremium: number
}, _a: typeof a[keyof typeof a]) {
super('popup-limit', {
buttons: options.isPremium === undefined ? [{
langKey: 'LimitReached.Ok',
isCancel: true
}] : (options.isPremium ? [{
langKey: 'OK',
isCancel: true
}] : [{
langKey: 'IncreaseLimit',
callback: () => {
appImManager.openPremiumBot();
},
noRipple: true
}, {
langKey: 'Cancel',
isCancel: true
}]),
descriptionLangKey: options.isPremium === undefined ? _a.descriptionLocked : (options.isPremium ? _a.descriptionPremium : _a.description),
descriptionLangArgs: options.isPremium ? [options.limitPremium] : [options.limit, options.limitPremium],
titleLangKey: _a.title
});
const isLocked = options.isPremium === undefined;
if(isLocked) {
this.element.classList.add('is-locked');
} else if(options.isPremium) {
this.element.classList.add('is-premium');
} else {
const button = this.buttons.find((b) => !b.isCancel);
button.element.classList.add('popup-limit-button');
const i = document.createElement('i');
i.classList.add('popup-limit-button-icon', 'tgico-premium_double');
button.element.append(i);
}
const limitContainer = document.createElement('div');
limitContainer.classList.add('popup-limit-line');
const hint = document.createElement('div');
hint.classList.add('popup-limit-hint');
const i = document.createElement('span');
i.classList.add('popup-limit-hint-icon', 'tgico-' + _a.icon);
hint.append(i, '' + (options.isPremium ? options.limitPremium : options.limit));
limitContainer.append(hint);
if(!isLocked) {
const limit = document.createElement('div');
limit.classList.add('limit-line');
const free = document.createElement('div');
free.classList.add('limit-line-free');
const premium = document.createElement('div');
premium.classList.add('limit-line-premium');
limit.append(free, premium);
_i18n(free, 'LimitFree');
premium.append(i18n('LimitPremium'), '' + options.limitPremium);
limitContainer.append(limit);
}
this.container.insertBefore(limitContainer, this.description);
// if(options.isPremium === false) {
// this.buttons.pop().element.remove();
// }
const setHintActive = () => {
hint.classList.add('active');
};
if(rootScope.settings.animationsEnabled) {
doubleRaf().then(setHintActive);
} else {
setHintActive();
}
}
}
export default async function showLimitPopup(type: keyof typeof a) {
const _a = a[type];
const [appConfig, limit, limitPremium] = await Promise.all([
rootScope.managers.apiManager.getAppConfig(),
...[false, true].map((v) => rootScope.managers.apiManager.getLimit(type, v))
]);
const isLocked = appConfig.premium_purchase_blocked;
new P({
isPremium: isLocked ? undefined : rootScope.premium,
limit,
limitPremium
}, _a).show();
}

View File

@ -9,7 +9,6 @@ import type {DialogFilterSuggested} from '../../../layer';
import type _rootScope from '../../../lib/rootScope';
import {SliderSuperTab} from '../../slider';
import lottieLoader, {LottieLoader} from '../../../lib/rlottie/lottieLoader';
import {toast} from '../../toast';
import Button from '../../button';
import rootScope from '../../../lib/rootScope';
import AppEditFolderTab from './editFolder';
@ -26,6 +25,7 @@ import SettingSection from '../../settingSection';
import Sortable from '../../../helpers/dom/sortable';
import whichChild from '../../../helpers/dom/whichChild';
import indexOfAndSplice from '../../../helpers/array/indexOfAndSplice';
import showLimitPopup from '../../popups/limit';
export default class AppChatFoldersTab extends SliderSuperTab {
private createFolderBtn: HTMLElement;
@ -160,7 +160,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
this.foldersSection = new SettingSection({
name: 'Filters'
});
this.foldersSection.container.style.display = 'none';
this.foldersSection.container.classList.add('hide');
this.list = document.createElement('div');
this.foldersSection.content.append(this.list);
@ -168,20 +168,26 @@ export default class AppChatFoldersTab extends SliderSuperTab {
this.suggestedSection = new SettingSection({
name: 'FilterRecommended'
});
this.suggestedSection.container.style.display = 'none';
this.suggestedSection.container.classList.add('hide');
this.scrollable.append(this.stickerContainer, caption, this.createFolderBtn, this.foldersSection.container, this.suggestedSection.container);
this.scrollable.append(
this.stickerContainer,
caption,
this.createFolderBtn,
this.foldersSection.container,
this.suggestedSection.container
);
attachClickEvent(this.createFolderBtn, async() => {
if(!(await this.canCreateFolder())) {
toast('Sorry, you can\'t create more folders.');
showLimitPopup('folders');
} else {
this.slider.createTab(AppEditFolderTab).open();
}
}, {listenerSetter: this.listenerSetter});
const onFiltersContainerUpdate = () => {
this.foldersSection.container.style.display = Object.keys(this.filtersRendered).length ? '' : 'none';
this.foldersSection.container.classList.toggle('hide', !Object.keys(this.filtersRendered).length);
};
this.managers.filtersStorage.getDialogFilters().then(async(filters) => {
@ -310,7 +316,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
private getSuggestedFilters() {
return this.managers.filtersStorage.getSuggestedDialogsFilters().then(async(suggestedFilters) => {
this.suggestedSection.container.style.display = suggestedFilters.length ? '' : 'none';
this.suggestedSection.container.classList.toggle('hide', !suggestedFilters.length);
Array.from(this.suggestedSection.content.children).slice(1).forEach((el) => el.remove());
for(const filter of suggestedFilters) {
@ -322,7 +328,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
cancelEvent(e);
if(!(await this.canCreateFolder())) {
toast('Sorry, you can\'t create more folders.');
showLimitPopup('folders');
return;
}
@ -333,10 +339,9 @@ export default class AppChatFoldersTab extends SliderSuperTab {
f.excludePeerIds = [];
f.pinnedPeerIds = [];
this.managers.filtersStorage.createDialogFilter(f, true).then((bool) => {
if(bool) {
row.container.remove();
}
this.managers.filtersStorage.createDialogFilter(f, true).then(() => {
row.container.remove();
this.suggestedSection.container.classList.toggle('hide', this.suggestedSection.content.childElementCount === 1);
}).finally(() => {
button.removeAttribute('disabled');
});

View File

@ -179,7 +179,13 @@ export default class SwipeHandler {
this.listenerSetter.add(attachGlobalListenerTo)('mousemove', this.handleMove, MOUSE_MOVE_OPTIONS);
}
this.onStart?.();
if(this.onStart) {
this.onStart();
// have to initiate move instantly
this.hadMove = true;
this.handleMove(e);
}
};
protected handleMove = (_e: EE) => {
@ -213,7 +219,6 @@ export default class SwipeHandler {
this.onFirstSwipe?.(_e);
}
/* reset values */
const onSwipeResult = this.onSwipe(xDiff, yDiff, _e);
if(onSwipeResult !== undefined && onSwipeResult) {
this.reset();

View File

@ -21,7 +21,7 @@ const App = {
version: process.env.VERSION,
versionFull: process.env.VERSION_FULL,
build: +process.env.BUILD,
langPackVersion: '0.7.7',
langPackVersion: '0.7.8',
langPack: 'webk',
langPackCode: 'en',
domains: MAIN_DOMAINS,

View File

@ -869,6 +869,9 @@ const lang = {
'TerminateWebSessionInfo': 'Tap to disconnect from your Telegram account.',
'EnablePhotoSpoiler': 'Hide with spoiler',
'DisablePhotoSpoiler': 'Remove spoiler',
'LimitReachedFolders': 'You have reached the limit of **%1$d** folders. You can double the limit to **%2$d** folders by subscribing to **Telegram Premium**.',
'LimitReachedFoldersPremium': 'You have reached the limit of **%1$d** folders for this account.',
'LimitReachedFoldersLocked': 'You have reached the limit of **%1$d** folders for this account. We are working to let you increase this limit in the future.',
// * macos
'AccountSettings.Filters': 'Chat Folders',

View File

@ -1293,6 +1293,12 @@ export class AppImManager extends EventListenerBase<{
});
}
public openPremiumBot() {
return this.managers.apiManager.getAppConfig().then((appConfig) => {
return this.openUsername({userName: appConfig.premium_bot_username});
});
}
public openUsername(options: {
userName: string
} & Omit<ChatSetPeerOptions, 'peerId'>) {

View File

@ -22,6 +22,15 @@
align-items: center;
justify-content: center;
font-weight: var(--font-weight-bold);
transform: scale(.6) translate(-280px, 16px) rotate(-30deg);
@include animation-level(2) {
transition: transform .3s cubic-bezier(.12,1.1,.56,1.2);
}
&.active {
transform: scale(1) translate(0, 0) rotate(0);
}
&-icon {
font-size: 1.25rem;
@ -30,6 +39,21 @@
}
}
&-button {
&.primary {
background: linear-gradient(88.39deg, #6C93FF -2.56%, #976FFF 51.27%, #DF69D1 107.39%);
color: #fff !important;
display: flex;
align-items: center;
}
&-icon {
margin-left: .625rem;
font-size: 1.5rem;
display: flex;
}
}
.limit-line {
align-self: stretch;
margin: 1rem .5rem 0;
@ -67,4 +91,26 @@
margin-right: -.25rem;
background-color: var(--primary-color);
}
&.is-premium &-hint {
align-self: flex-end;
margin-right: .5rem;
border-bottom-right-radius: 0;
background: linear-gradient(84.4deg,#6c93ff -4.85%,#976fff 51.72%,#df69d1 110.7%);
background-size: 200px 2rem;
background-position-x: 100%;
@include animation-level(2) {
transition: transform .3s cubic-bezier(.12,1.1,.56,1.1);
}
&:after {
height: 12px;
left: 100%;
bottom: -11.5px;
margin-left: -20.6px;
background-position-x: 134%;
clip-path: path("M8.44528 0.5H20.5V10.1943C20.5 10.9154 19.9154 11.5 19.1943 11.5C18.8178 11.5 18.4597 11.3375 18.2117 11.0541L10.2274 1.92918C9.75146 1.38523 9.18812 0.924478 8.56057 0.565879L8.44528 0.5Z");
}
}
}