Favorite stickers
Stickers context menu
This commit is contained in:
parent
df2f809565
commit
df814f2a68
@ -4,7 +4,7 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import attachListNavigation from '../../helpers/dom/attachListNavigation';
|
||||
import attachListNavigation, {ListNavigationOptions} from '../../helpers/dom/attachListNavigation';
|
||||
import EventListenerBase from '../../helpers/eventListenerBase';
|
||||
import {IS_MOBILE} from '../../environment/userAgent';
|
||||
import rootScope from '../../lib/rootScope';
|
||||
@ -28,7 +28,7 @@ export default class AutocompleteHelper extends EventListenerBase<{
|
||||
|
||||
protected controller: AutocompleteHelperController;
|
||||
protected listType: 'xy' | 'x' | 'y';
|
||||
protected onSelect: (target: Element) => boolean | void;
|
||||
protected onSelect: ListNavigationOptions['onSelect'];
|
||||
protected waitForKey?: string[];
|
||||
|
||||
protected navigationItem: NavigationItem;
|
||||
|
@ -945,7 +945,7 @@ export default class ChatBubbles {
|
||||
return;
|
||||
}
|
||||
|
||||
awaited.forEach(({bubble, message}) => {
|
||||
awaited.filter(Boolean).forEach(({bubble, message}) => {
|
||||
if(this.bubbles[message.mid] !== bubble) {
|
||||
return;
|
||||
}
|
||||
@ -4011,8 +4011,9 @@ export default class ChatBubbles {
|
||||
}
|
||||
|
||||
const sizes = mediaSizes.active;
|
||||
const size = bubble.classList.contains('emoji-big') ? sizes.emojiSticker : (doc.animated ? sizes.animatedSticker : sizes.staticSticker);
|
||||
setAttachmentSize(doc, attachmentDiv, size.width, size.height);
|
||||
const isEmoji = bubble.classList.contains('emoji-big');
|
||||
const boxSize = isEmoji ? sizes.emojiSticker : (doc.animated ? sizes.animatedSticker : sizes.staticSticker);
|
||||
setAttachmentSize(doc, attachmentDiv, boxSize.width, boxSize.height);
|
||||
// let preloader = new ProgressivePreloader(attachmentDiv, false);
|
||||
bubbleContainer.style.minWidth = attachmentDiv.style.width;
|
||||
bubbleContainer.style.minHeight = attachmentDiv.style.height;
|
||||
@ -4026,7 +4027,7 @@ export default class ChatBubbles {
|
||||
// play: !!message.pending || !multipleRender,
|
||||
play: true,
|
||||
loop: true,
|
||||
emoji: bubble.classList.contains('emoji-big') ? messageMessage : undefined,
|
||||
emoji: isEmoji ? messageMessage : undefined,
|
||||
withThumb: true,
|
||||
loadPromises,
|
||||
isOut,
|
||||
|
@ -281,6 +281,21 @@ export default class ChatContextMenu {
|
||||
}
|
||||
|
||||
private setButtons() {
|
||||
const verifyFavoriteSticker = async(toAdd: boolean) => {
|
||||
const doc = ((this.message as Message.message).media as MessageMedia.messageMediaDocument)?.document;
|
||||
if(!(doc as MyDocument)?.sticker) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const favedStickers = await this.managers.acknowledged.appStickersManager.getFavedStickersStickers();
|
||||
if(!favedStickers.cached) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const found = (await favedStickers.result).some((_doc) => _doc.id === doc.id);
|
||||
return toAdd ? !found : found;
|
||||
};
|
||||
|
||||
this.buttons = [{
|
||||
icon: 'send2',
|
||||
text: 'MessageScheduleSend',
|
||||
@ -317,6 +332,16 @@ export default class ChatContextMenu {
|
||||
!!this.chat.input.messageInput &&
|
||||
this.chat.type !== 'scheduled'/* ,
|
||||
cancelEvent: true */
|
||||
}, {
|
||||
icon: 'favourites',
|
||||
text: 'AddToFavorites',
|
||||
onClick: this.onFaveStickerClick.bind(this, false),
|
||||
verify: () => verifyFavoriteSticker(true)
|
||||
}, {
|
||||
icon: 'favourites',
|
||||
text: 'DeleteFromFavorites',
|
||||
onClick: this.onFaveStickerClick.bind(this, true),
|
||||
verify: () => verifyFavoriteSticker(false)
|
||||
}, {
|
||||
icon: 'edit',
|
||||
text: 'Edit',
|
||||
@ -682,6 +707,11 @@ export default class ChatContextMenu {
|
||||
this.chat.input.initMessageReply(this.mid);
|
||||
};
|
||||
|
||||
private onFaveStickerClick = (unfave?: boolean) => {
|
||||
const docId = ((this.message as Message.message).media as MessageMedia.messageMediaDocument).document.id;
|
||||
this.managers.appStickersManager.faveSticker(docId, unfave);
|
||||
};
|
||||
|
||||
private onEditClick = () => {
|
||||
this.chat.input.initMessageEditing(this.mid);
|
||||
};
|
||||
|
@ -95,6 +95,7 @@ import ChatSendAs from './sendAs';
|
||||
import filterAsync from '../../helpers/array/filterAsync';
|
||||
import InputFieldAnimated from '../inputFieldAnimated';
|
||||
import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb';
|
||||
import PopupStickers from '../popups/stickers';
|
||||
|
||||
const RECORD_MIN_TIME = 500;
|
||||
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
||||
@ -1059,6 +1060,9 @@ export default class ChatInput {
|
||||
return;
|
||||
}
|
||||
|
||||
const popups = PopupElement.getPopups(PopupStickers);
|
||||
popups.forEach((popup) => popup.hide());
|
||||
|
||||
this.appImManager.openScheduled(peerId);
|
||||
}, 0);
|
||||
}
|
||||
@ -2435,7 +2439,12 @@ export default class ChatInput {
|
||||
// this.onMessageSent();
|
||||
}
|
||||
|
||||
public async sendMessageWithDocument(document: MyDocument | string, force = false, clearDraft = false) {
|
||||
public async sendMessageWithDocument(
|
||||
document: MyDocument | DocId,
|
||||
force = false,
|
||||
clearDraft = false,
|
||||
silent = false
|
||||
) {
|
||||
document = await this.managers.appDocsManager.getDoc(document);
|
||||
|
||||
const flag = document.type === 'sticker' ? 'send_stickers' : (document.type === 'gif' ? 'send_gifs' : 'send_media');
|
||||
@ -2445,7 +2454,7 @@ export default class ChatInput {
|
||||
}
|
||||
|
||||
if(this.chat.type === 'scheduled' && !force) {
|
||||
this.scheduleSending(() => this.sendMessageWithDocument(document, true, clearDraft));
|
||||
this.scheduleSending(() => this.sendMessageWithDocument(document, true, clearDraft, silent));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2460,12 +2469,13 @@ export default class ChatInput {
|
||||
this.managers.appMessagesManager.sendFile(this.chat.peerId, document, {
|
||||
...this.chat.getMessageSendingParams(),
|
||||
isMedia: true,
|
||||
clearDraft: clearDraft || undefined
|
||||
clearDraft: clearDraft || undefined,
|
||||
silent
|
||||
});
|
||||
this.onMessageSent(clearDraft, true);
|
||||
|
||||
if(document.type === 'sticker') {
|
||||
emoticonsDropdown.stickersTab?.pushRecentSticker(document);
|
||||
emoticonsDropdown.stickersTab?.unshiftRecentSticker(document);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -32,8 +32,8 @@ export default class StickersHelper extends AutocompleteHelper {
|
||||
appendTo,
|
||||
controller,
|
||||
listType: 'xy',
|
||||
onSelect: (target) => {
|
||||
return !EmoticonsDropdown.onMediaClick({target}, true);
|
||||
onSelect: async(target) => {
|
||||
return !(await EmoticonsDropdown.onMediaClick({target}, true));
|
||||
},
|
||||
waitForKey: ['ArrowUp', 'ArrowDown']
|
||||
});
|
||||
|
@ -30,6 +30,7 @@ import {IS_APPLE_MOBILE} from '../../environment/userAgent';
|
||||
import {AppManagers} from '../../lib/appManagers/managers';
|
||||
import type LazyLoadQueueIntersector from '../lazyLoadQueueIntersector';
|
||||
import {simulateClickEvent} from '../../helpers/dom/clickEvent';
|
||||
import overlayCounter from '../../helpers/overlayCounter';
|
||||
|
||||
export const EMOTICONSSTICKERGROUP: AnimationItemGroup = 'emoticons-dropdown';
|
||||
|
||||
@ -88,7 +89,7 @@ export class EmoticonsDropdown extends DropdownHover {
|
||||
EmoticonsDropdown.lazyLoadQueue.unlock();
|
||||
EmoticonsDropdown.lazyLoadQueue.refresh();
|
||||
|
||||
this.container.classList.remove('disable-hover');
|
||||
// this.container.classList.remove('disable-hover');
|
||||
});
|
||||
|
||||
this.addEventListener('close', () => {
|
||||
@ -106,7 +107,7 @@ export class EmoticonsDropdown extends DropdownHover {
|
||||
EmoticonsDropdown.lazyLoadQueue.unlock();
|
||||
EmoticonsDropdown.lazyLoadQueue.refresh();
|
||||
|
||||
this.container.classList.remove('disable-hover');
|
||||
// this.container.classList.remove('disable-hover');
|
||||
|
||||
this.savedRange = undefined;
|
||||
});
|
||||
@ -182,6 +183,26 @@ export class EmoticonsDropdown extends DropdownHover {
|
||||
this.tabs[INIT_TAB_ID].init(); // onTransitionEnd не вызовется, т.к. это первая открытая вкладка
|
||||
}
|
||||
|
||||
if(!IS_TOUCH_SUPPORTED) {
|
||||
let lastMouseMoveEvent: MouseEvent, mouseMoveEventAttached = false;
|
||||
const onMouseMove = (e: MouseEvent) => {
|
||||
lastMouseMoveEvent = e;
|
||||
};
|
||||
overlayCounter.addEventListener('change', (isActive) => {
|
||||
if(isActive) {
|
||||
if(!mouseMoveEventAttached) {
|
||||
document.body.addEventListener('mousemove', onMouseMove);
|
||||
mouseMoveEventAttached = true;
|
||||
}
|
||||
} else if(mouseMoveEventAttached) {
|
||||
document.body.removeEventListener('mousemove', onMouseMove);
|
||||
if(lastMouseMoveEvent) {
|
||||
this.onMouseOut(lastMouseMoveEvent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
appImManager.addEventListener('peer_changed', this.checkRights);
|
||||
this.checkRights();
|
||||
|
||||
@ -204,15 +225,17 @@ export class EmoticonsDropdown extends DropdownHover {
|
||||
this.deleteBtn.classList.toggle('hide', this.tabId !== 0);
|
||||
};
|
||||
|
||||
private checkRights = () => {
|
||||
private checkRights = async() => {
|
||||
const {peerId, threadId} = appImManager.chat;
|
||||
const children = this.tabsEl.children;
|
||||
const tabsElements = Array.from(children) as HTMLElement[];
|
||||
|
||||
const canSendStickers = this.managers.appMessagesManager.canSendToPeer(peerId, threadId, 'send_stickers');
|
||||
tabsElements[2].toggleAttribute('disabled', !canSendStickers);
|
||||
const [canSendStickers, canSendGifs] = await Promise.all([
|
||||
this.managers.appMessagesManager.canSendToPeer(peerId, threadId, 'send_stickers'),
|
||||
this.managers.appMessagesManager.canSendToPeer(peerId, threadId, 'send_gifs')
|
||||
]);
|
||||
|
||||
const canSendGifs = this.managers.appMessagesManager.canSendToPeer(peerId, threadId, 'send_gifs');
|
||||
tabsElements[2].toggleAttribute('disabled', !canSendStickers);
|
||||
tabsElements[3].toggleAttribute('disabled', !canSendGifs);
|
||||
|
||||
const active = this.tabsEl.querySelector('.active');
|
||||
@ -290,30 +313,34 @@ export class EmoticonsDropdown extends DropdownHover {
|
||||
return {stickyIntersector, setActive};
|
||||
};
|
||||
|
||||
public static onMediaClick = (e: {target: EventTarget | Element}, clearDraft = false) => {
|
||||
public static onMediaClick = async(e: {target: EventTarget | Element}, clearDraft = false, silent?: boolean) => {
|
||||
let target = e.target as HTMLElement;
|
||||
target = findUpTag(target, 'DIV');
|
||||
|
||||
if(!target) return false;
|
||||
|
||||
const fileId = target.dataset.docId;
|
||||
if(!fileId) return false;
|
||||
const docId = target.dataset.docId;
|
||||
if(!docId) return false;
|
||||
|
||||
if(appImManager.chat.input.sendMessageWithDocument(fileId, undefined, clearDraft)) {
|
||||
return this.sendDocId(docId, clearDraft, silent);
|
||||
};
|
||||
|
||||
public static async sendDocId(docId: DocId, clearDraft?: boolean, silent?: boolean) {
|
||||
if(await appImManager.chat.input.sendMessageWithDocument(docId, undefined, clearDraft, silent)) {
|
||||
/* dropdown.classList.remove('active');
|
||||
toggleEl.classList.remove('active'); */
|
||||
if(emoticonsDropdown.container) {
|
||||
emoticonsDropdown.forceClose = true;
|
||||
emoticonsDropdown.container.classList.add('disable-hover');
|
||||
// emoticonsDropdown.container.classList.add('disable-hover');
|
||||
emoticonsDropdown.toggle(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
console.warn('got no doc by id:', fileId);
|
||||
console.warn('got no doc by id:', docId);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public addLazyLoadQueueRepeat(lazyLoadQueue: LazyLoadQueueIntersector, processInvisibleDiv: (div: HTMLElement) => void) {
|
||||
this.addEventListener('close', () => {
|
||||
|
@ -28,6 +28,10 @@ import noop from '../../../helpers/noop';
|
||||
import ButtonIcon from '../../buttonIcon';
|
||||
import confirmationPopup from '../../confirmationPopup';
|
||||
import VisibilityIntersector, {OnVisibilityChange} from '../../visibilityIntersector';
|
||||
import createStickersContextMenu from '../../../helpers/dom/createStickersContextMenu';
|
||||
import findUpAsChild from '../../../helpers/dom/findUpAsChild';
|
||||
import forEachReverse from '../../../helpers/array/forEachReverse';
|
||||
import {MTAppConfig} from '../../../lib/mtproto/appConfig';
|
||||
|
||||
export class SuperStickerRenderer {
|
||||
public lazyLoadQueue: LazyLoadQueueRepeat;
|
||||
@ -158,18 +162,18 @@ type StickersTabCategory = {
|
||||
document: MyDocument,
|
||||
element: HTMLElement
|
||||
}[],
|
||||
pos?: number
|
||||
mounted?: boolean,
|
||||
id: string,
|
||||
limit?: number
|
||||
};
|
||||
|
||||
const RECENT_STICKERS_COUNT = 20;
|
||||
|
||||
export default class StickersTab implements EmoticonsTab {
|
||||
private content: HTMLElement;
|
||||
|
||||
private categories: {[id: string]: StickersTabCategory};
|
||||
private categoriesMap: Map<HTMLElement, StickersTabCategory>;
|
||||
private categoriesIntersector: VisibilityIntersector;
|
||||
private localCategoryIndex: number;
|
||||
private localCategories: StickersTabCategory[];
|
||||
|
||||
private scroll: Scrollable;
|
||||
private menu: HTMLElement;
|
||||
@ -180,7 +184,13 @@ export default class StickersTab implements EmoticonsTab {
|
||||
constructor(private managers: AppManagers) {
|
||||
this.categories = {};
|
||||
this.categoriesMap = new Map();
|
||||
this.localCategoryIndex = 0;
|
||||
this.localCategories = [];
|
||||
}
|
||||
|
||||
private setFavedLimit(appConfig: MTAppConfig) {
|
||||
const limit = rootScope.premium ? appConfig.stickers_faved_limit_premium : appConfig.stickers_faved_limit_default;
|
||||
const category = this.categories['faved'];
|
||||
category.limit = limit;
|
||||
}
|
||||
|
||||
private createCategory(stickerSet: StickerSet.stickerSet, _title: HTMLElement | DocumentFragment) {
|
||||
@ -211,7 +221,8 @@ export default class StickersTab implements EmoticonsTab {
|
||||
menuTabPadding
|
||||
},
|
||||
set: stickerSet,
|
||||
items: []
|
||||
items: [],
|
||||
id: '' + stickerSet.id
|
||||
};
|
||||
|
||||
container.append(title, items);
|
||||
@ -267,7 +278,7 @@ export default class StickersTab implements EmoticonsTab {
|
||||
const category = this.createCategory(set, wrapEmojiText(set.title));
|
||||
const {menuTab, menuTabPadding, container} = category.elements;
|
||||
|
||||
const pos = prepend ? this.localCategoryIndex : 0xFFFF;
|
||||
const pos = prepend ? this.localCategories.filter((category) => category.mounted).length : 0xFFFF;
|
||||
positionElementByIndex(menuTab, this.menu, pos);
|
||||
|
||||
const promise = this.managers.appStickersManager.getStickerSet(set);
|
||||
@ -373,16 +384,44 @@ export default class StickersTab implements EmoticonsTab {
|
||||
|
||||
const createLocalCategory = (id: string, title: LangPackKey, icon?: string) => {
|
||||
const category = this.createCategory({id} as any, i18n(title));
|
||||
this.localCategories.push(category);
|
||||
category.elements.title.classList.add('disable-hover');
|
||||
icon && category.elements.menuTab.classList.add('tgico-' + icon);
|
||||
category.elements.menuTabPadding.remove();
|
||||
category.pos = this.localCategoryIndex++;
|
||||
this.toggleLocalCategory(category, false);
|
||||
return category;
|
||||
};
|
||||
|
||||
const onCategoryStickers = (category: StickersTabCategory, stickers: MyDocument[]) => {
|
||||
// if(category.id === 'faved' && category.limit && category.limit < stickers.length) {
|
||||
// category.limit = stickers.length;
|
||||
// }
|
||||
|
||||
if(category.limit) {
|
||||
stickers = stickers.slice(0, category.limit);
|
||||
}
|
||||
|
||||
const ids = new Set(stickers.map((doc) => doc.id));
|
||||
forEachReverse(category.items, (item) => {
|
||||
if(!ids.has(item.document.id)) {
|
||||
this.deleteSticker(category, item.document, true);
|
||||
}
|
||||
});
|
||||
|
||||
this.toggleLocalCategory(category, !!stickers.length);
|
||||
forEachReverse(stickers, (doc, idx) => {
|
||||
this.unshiftSticker(category, doc, true, idx);
|
||||
});
|
||||
this.spliceExceed(category);
|
||||
category.elements.container.classList.remove('hide');
|
||||
};
|
||||
|
||||
const favedCategory = createLocalCategory('faved', 'FavoriteStickers', 'saved');
|
||||
favedCategory.elements.menuTab.classList.add('active');
|
||||
|
||||
const recentCategory = createLocalCategory('recent', 'Stickers.Recent', 'recent');
|
||||
recentCategory.elements.menuTab.classList.add('active');
|
||||
recentCategory.limit = 20;
|
||||
// recentCategory.elements.menuTab.classList.add('active');
|
||||
|
||||
const clearButton = ButtonIcon('close', {noRipple: true});
|
||||
recentCategory.elements.title.append(clearButton);
|
||||
@ -398,22 +437,22 @@ export default class StickersTab implements EmoticonsTab {
|
||||
}, noop);
|
||||
});
|
||||
|
||||
const onRecentStickers = (stickers: MyDocument[]) => {
|
||||
const sliced = stickers.slice(0, RECENT_STICKERS_COUNT) as MyDocument[];
|
||||
|
||||
clearCategoryItems(recentCategory);
|
||||
this.toggleLocalCategory(recentCategory, !!sliced.length);
|
||||
this.categoryAppendStickers(recentCategory, Promise.resolve(sliced));
|
||||
};
|
||||
|
||||
const premiumCategory = createLocalCategory('premium', 'PremiumStickersShort');
|
||||
const s = document.createElement('span');
|
||||
s.classList.add('tgico-star', 'color-premium');
|
||||
premiumCategory.elements.menuTab.append(s);
|
||||
|
||||
const promises = [
|
||||
this.managers.appStickersManager.getRecentStickers().then((stickers) => {
|
||||
onRecentStickers(stickers.stickers as MyDocument[]);
|
||||
Promise.all([
|
||||
this.managers.apiManager.getAppConfig(),
|
||||
this.managers.appStickersManager.getFavedStickersStickers()
|
||||
]).then(([appConfig, stickers]) => {
|
||||
this.setFavedLimit(appConfig);
|
||||
onCategoryStickers(favedCategory, stickers);
|
||||
}),
|
||||
|
||||
this.managers.appStickersManager.getRecentStickersStickers().then((stickers) => {
|
||||
onCategoryStickers(recentCategory, stickers);
|
||||
}),
|
||||
|
||||
this.managers.appStickersManager.getAllStickers().then((res) => {
|
||||
@ -492,12 +531,32 @@ export default class StickersTab implements EmoticonsTab {
|
||||
}
|
||||
});
|
||||
|
||||
rootScope.addEventListener('stickers_recent', (stickers) => {
|
||||
if(this.mounted) {
|
||||
onRecentStickers(stickers);
|
||||
rootScope.addEventListener('sticker_updated', ({type, document, faved}) => {
|
||||
// if(type === 'faved') {
|
||||
// return;
|
||||
// }
|
||||
|
||||
const category = this.categories[type === 'faved' ? 'faved' : 'recent'];
|
||||
if(category) {
|
||||
if(faved) {
|
||||
this.unshiftSticker(category, document);
|
||||
} else {
|
||||
this.deleteSticker(category, document);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rootScope.addEventListener('stickers_updated', ({type, stickers}) => {
|
||||
if(this.mounted) {
|
||||
const category = this.categories[type === 'faved' ? 'faved' : 'recent'];
|
||||
onCategoryStickers(category, stickers);
|
||||
}
|
||||
});
|
||||
|
||||
rootScope.addEventListener('app_config', (appConfig) => {
|
||||
this.setFavedLimit(appConfig);
|
||||
});
|
||||
|
||||
const resizeCategories = () => {
|
||||
for(const [container, category] of this.categoriesMap) {
|
||||
this.setCategoryItemsHeight(category);
|
||||
@ -508,6 +567,17 @@ export default class StickersTab implements EmoticonsTab {
|
||||
|
||||
emoticonsDropdown.addEventListener('opened', resizeCategories);
|
||||
|
||||
createStickersContextMenu({
|
||||
listenTo: this.content,
|
||||
verifyRecent: (target) => !!findUpAsChild(target, this.categories['recent'].elements.items),
|
||||
onOpen: () => {
|
||||
emoticonsDropdown.setIgnoreMouseOut(true);
|
||||
},
|
||||
onClose: () => {
|
||||
emoticonsDropdown.setIgnoreMouseOut(false);
|
||||
}
|
||||
});
|
||||
|
||||
this.init = null;
|
||||
}
|
||||
|
||||
@ -516,23 +586,50 @@ export default class StickersTab implements EmoticonsTab {
|
||||
category.elements.menuTab.remove();
|
||||
category.elements.container.remove();
|
||||
} else {
|
||||
const pos = category.pos;
|
||||
positionElementByIndex(category.elements.menuTab, this.menu, pos);
|
||||
positionElementByIndex(category.elements.container, this.scroll.container, pos);
|
||||
let idx = this.localCategories.indexOf(category);
|
||||
const notMounted = this.localCategories.slice(0, idx).filter((category) => !category.mounted);
|
||||
idx -= notMounted.length;
|
||||
positionElementByIndex(category.elements.menuTab, this.menu, idx);
|
||||
positionElementByIndex(category.elements.container, this.scroll.container, idx);
|
||||
}
|
||||
|
||||
category.mounted = visible;
|
||||
// category.elements.container.classList.toggle('hide', !visible);
|
||||
}
|
||||
|
||||
public pushRecentSticker(doc: MyDocument) {
|
||||
this.managers.appStickersManager.pushRecentSticker(doc.id);
|
||||
private onLocalCategoryUpdate(category: StickersTabCategory) {
|
||||
this.setCategoryItemsHeight(category);
|
||||
this.toggleLocalCategory(category, !!category.items.length);
|
||||
}
|
||||
|
||||
const category = this.categories['recent'];
|
||||
if(!category) {
|
||||
return;
|
||||
public deleteSticker(category: StickersTabCategory, doc: MyDocument, batch?: boolean) {
|
||||
const item = findAndSplice(category.items, (item) => item.document.id === doc.id);
|
||||
if(item) {
|
||||
item.element.remove();
|
||||
|
||||
if(!batch) {
|
||||
this.onLocalCategoryUpdate(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private spliceExceed(category: StickersTabCategory) {
|
||||
const {items, limit} = category;
|
||||
items.splice(limit, items.length - limit).forEach(({element}) => {
|
||||
element.remove();
|
||||
});
|
||||
|
||||
this.onLocalCategoryUpdate(category);
|
||||
}
|
||||
|
||||
public unshiftSticker(category: StickersTabCategory, doc: MyDocument, batch?: boolean, idx?: number) {
|
||||
if(idx !== undefined) {
|
||||
const i = category.items[idx];
|
||||
if(i && i.document.id === doc.id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const items = category.elements.items;
|
||||
let item = findAndSplice(category.items, (item) => item.document.id === doc.id);
|
||||
if(!item) {
|
||||
item = {
|
||||
@ -542,13 +639,19 @@ export default class StickersTab implements EmoticonsTab {
|
||||
}
|
||||
|
||||
category.items.unshift(item);
|
||||
if(items.childElementCount) items.prepend(item.element);
|
||||
if(items.childElementCount > RECENT_STICKERS_COUNT) {
|
||||
(Array.from(items.children) as HTMLElement[]).slice(RECENT_STICKERS_COUNT).forEach((el) => el.remove());
|
||||
}
|
||||
category.elements.items.prepend(item.element);
|
||||
|
||||
this.setCategoryItemsHeight(category);
|
||||
this.toggleLocalCategory(category, true);
|
||||
if(!batch) {
|
||||
this.spliceExceed(category);
|
||||
}
|
||||
}
|
||||
|
||||
public unshiftRecentSticker(doc: MyDocument) {
|
||||
this.managers.appStickersManager.saveRecentSticker(doc.id);
|
||||
}
|
||||
|
||||
public deleteRecentSticker(doc: MyDocument) {
|
||||
this.managers.appStickersManager.saveRecentSticker(doc.id, true);
|
||||
}
|
||||
|
||||
onClose() {
|
||||
|
@ -351,7 +351,7 @@ export default class PopupPayment extends PopupElement {
|
||||
}
|
||||
|
||||
tipsLabel.label.addEventListener('mousedown', (e) => {
|
||||
if(!findUpAsChild(e.target, input)) {
|
||||
if(!findUpAsChild(e.target as HTMLElement, input)) {
|
||||
placeCaretAtEnd(input);
|
||||
}
|
||||
});
|
||||
|
@ -20,6 +20,7 @@ import {attachClickEvent} from '../../helpers/dom/clickEvent';
|
||||
import {toastNew} from '../toast';
|
||||
import setInnerHTML from '../../helpers/dom/setInnerHTML';
|
||||
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
|
||||
import createStickersContextMenu from '../../helpers/dom/createStickersContextMenu';
|
||||
|
||||
const ANIMATION_GROUP: AnimationItemGroup = 'STICKERS-POPUP';
|
||||
|
||||
@ -34,6 +35,7 @@ export default class PopupStickers extends PopupElement {
|
||||
|
||||
this.addEventListener('close', () => {
|
||||
animationIntersector.setOnlyOnePlayableGroup();
|
||||
destroy();
|
||||
});
|
||||
|
||||
const div = document.createElement('div');
|
||||
@ -57,10 +59,10 @@ export default class PopupStickers extends PopupElement {
|
||||
this.scrollable.append(div);
|
||||
this.body.append(this.stickersFooter);
|
||||
|
||||
// const editButton = document.createElement('button');
|
||||
// editButton.classList.add('btn-primary');
|
||||
|
||||
// this.stickersFooter.append(editButton);
|
||||
const {destroy} = createStickersContextMenu({
|
||||
listenTo: this.stickersDiv,
|
||||
isStickerPack: true
|
||||
});
|
||||
|
||||
this.loadStickerSet();
|
||||
}
|
||||
@ -69,11 +71,9 @@ export default class PopupStickers extends PopupElement {
|
||||
const target = findUpClassName(e.target, 'sticker-set-sticker');
|
||||
if(!target) return;
|
||||
|
||||
const fileId = target.dataset.docId;
|
||||
if(appImManager.chat.input.sendMessageWithDocument(fileId)) {
|
||||
const docId = target.dataset.docId;
|
||||
if(appImManager.chat.input.sendMessageWithDocument(docId)) {
|
||||
this.hide();
|
||||
} else {
|
||||
console.warn('got no doc by id:', fileId);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -141,7 +141,7 @@ export default function ripple(
|
||||
findUpClassName(e.target as HTMLElement, 'c-ripple') !== r
|
||||
) && (
|
||||
attachListenerTo === elem ||
|
||||
!findUpAsChild(e.target, attachListenerTo)
|
||||
!findUpAsChild(e.target as HTMLElement, attachListenerTo)
|
||||
);
|
||||
|
||||
// TODO: rename this variable
|
||||
|
@ -17,6 +17,8 @@ import renderImageFromUrl from '../../helpers/dom/renderImageFromUrl';
|
||||
import getImageFromStrippedThumb from '../../helpers/getImageFromStrippedThumb';
|
||||
import getPreviewURLFromThumb from '../../helpers/getPreviewURLFromThumb';
|
||||
import makeError from '../../helpers/makeError';
|
||||
import {makeMediaSize} from '../../helpers/mediaSize';
|
||||
import mediaSizes from '../../helpers/mediaSizes';
|
||||
import onMediaLoad from '../../helpers/onMediaLoad';
|
||||
import {isSavingLottiePreview, saveLottiePreview} from '../../helpers/saveLottiePreview';
|
||||
import throttle from '../../helpers/schedulers/throttle';
|
||||
@ -79,12 +81,12 @@ export default async function wrapSticker({doc, div, middleware, lazyLoadQueue,
|
||||
asStatic = true;
|
||||
}
|
||||
|
||||
if(!width) {
|
||||
width = !emoji ? 200 : undefined;
|
||||
}
|
||||
|
||||
if(!height) {
|
||||
height = !emoji ? 200 : undefined;
|
||||
if(!width && !height) {
|
||||
const sizes = mediaSizes.active;
|
||||
const boxSize = emoji ? sizes.emojiSticker : (doc.animated ? sizes.animatedSticker : sizes.staticSticker);
|
||||
const size = makeMediaSize(doc.w, doc.h).aspectFitted(boxSize);
|
||||
width = size.width;
|
||||
height = size.height;
|
||||
}
|
||||
|
||||
if(stickerType === 2) {
|
||||
|
@ -17,13 +17,15 @@ const ACTIVE_CLASS_NAME = 'active';
|
||||
const AXIS_Y_KEYS: ArrowKey[] = ['ArrowUp', 'ArrowDown'];
|
||||
const AXIS_X_KEYS: ArrowKey[] = ['ArrowLeft', 'ArrowRight'];
|
||||
|
||||
export default function attachListNavigation({list, type, onSelect, once, waitForKey}: {
|
||||
export type ListNavigationOptions = {
|
||||
list: HTMLElement,
|
||||
type: 'xy' | 'x' | 'y',
|
||||
onSelect: (target: Element) => void | boolean,
|
||||
onSelect: (target: Element) => void | boolean | Promise<boolean>,
|
||||
once: boolean,
|
||||
waitForKey?: string[]
|
||||
}) {
|
||||
};
|
||||
|
||||
export default function attachListNavigation({list, type, onSelect, once, waitForKey}: ListNavigationOptions) {
|
||||
let waitForKeySet = waitForKey?.length ? new Set(waitForKey) : undefined;
|
||||
const keyNames = new Set(type === 'xy' ? AXIS_Y_KEYS.concat(AXIS_X_KEYS) : (type === 'x' ? AXIS_X_KEYS : AXIS_Y_KEYS));
|
||||
|
||||
@ -118,7 +120,7 @@ export default function attachListNavigation({list, type, onSelect, once, waitFo
|
||||
list.classList.add('navigable-list');
|
||||
|
||||
const onMouseMove = (e: MouseEvent) => {
|
||||
const target = findUpAsChild(e.target, list) as HTMLElement;
|
||||
const target = findUpAsChild(e.target as HTMLElement, list) as HTMLElement;
|
||||
if(!target) {
|
||||
return;
|
||||
}
|
||||
@ -129,7 +131,7 @@ export default function attachListNavigation({list, type, onSelect, once, waitFo
|
||||
const onClick = (e: Event) => {
|
||||
cancelEvent(e); // cancel keyboard closening
|
||||
|
||||
const target = findUpAsChild(e.target, list) as HTMLElement;
|
||||
const target = findUpAsChild(e.target as HTMLElement, list) as HTMLElement;
|
||||
if(!target) {
|
||||
return;
|
||||
}
|
||||
@ -138,8 +140,8 @@ export default function attachListNavigation({list, type, onSelect, once, waitFo
|
||||
fireSelect(getCurrentTarget());
|
||||
};
|
||||
|
||||
const fireSelect = (target: Element) => {
|
||||
const canContinue = onSelect(target);
|
||||
const fireSelect = async(target: Element) => {
|
||||
const canContinue = await onSelect(target);
|
||||
if(canContinue !== undefined ? !canContinue : once) {
|
||||
detach();
|
||||
}
|
||||
|
113
src/helpers/dom/createContextMenu.ts
Normal file
113
src/helpers/dom/createContextMenu.ts
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import ButtonMenu, {ButtonMenuItemOptions} from '../../components/buttonMenu';
|
||||
import filterAsync from '../array/filterAsync';
|
||||
import contextMenuController from '../contextMenuController';
|
||||
import ListenerSetter from '../listenerSetter';
|
||||
import {getMiddleware} from '../middleware';
|
||||
import positionMenu from '../positionMenu';
|
||||
import {attachContextMenuListener} from './attachContextMenuListener';
|
||||
|
||||
export default function createContextMenu<T extends ButtonMenuItemOptions & {verify?: () => boolean | Promise<boolean>}>({
|
||||
buttons,
|
||||
findElement,
|
||||
listenTo,
|
||||
appendTo,
|
||||
filterButtons,
|
||||
onOpen,
|
||||
onClose
|
||||
}: {
|
||||
buttons: T[],
|
||||
findElement: (e: MouseEvent) => HTMLElement,
|
||||
listenTo: HTMLElement,
|
||||
appendTo?: HTMLElement,
|
||||
filterButtons?: (buttons: T[]) => Promise<T[]>,
|
||||
onOpen?: (target: HTMLElement) => any,
|
||||
onClose?: () => any
|
||||
}) {
|
||||
appendTo ??= document.body;
|
||||
|
||||
const attachListenerSetter = new ListenerSetter();
|
||||
const listenerSetter = new ListenerSetter();
|
||||
const middleware = getMiddleware();
|
||||
let element: HTMLElement;
|
||||
|
||||
attachContextMenuListener(listenTo, (e) => {
|
||||
const target = findElement(e as any);
|
||||
if(!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
let _element = element;
|
||||
if(e instanceof MouseEvent || e.hasOwnProperty('preventDefault')) (e as any).preventDefault();
|
||||
if(_element && _element.classList.contains('active')) {
|
||||
return false;
|
||||
}
|
||||
if(e instanceof MouseEvent || e.hasOwnProperty('cancelBubble')) (e as any).cancelBubble = true;
|
||||
|
||||
const r = async() => {
|
||||
await onOpen?.(target);
|
||||
|
||||
const initResult = await init();
|
||||
if(!initResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
_element = initResult.element;
|
||||
const {cleanup, destroy} = initResult;
|
||||
|
||||
positionMenu(e, _element);
|
||||
contextMenuController.openBtnMenu(_element, () => {
|
||||
onClose?.();
|
||||
cleanup();
|
||||
|
||||
setTimeout(() => {
|
||||
destroy();
|
||||
}, 300);
|
||||
});
|
||||
};
|
||||
|
||||
r();
|
||||
}, attachListenerSetter);
|
||||
|
||||
const cleanup = () => {
|
||||
listenerSetter.removeAll();
|
||||
middleware.clean();
|
||||
};
|
||||
|
||||
const destroy = () => {
|
||||
cleanup();
|
||||
attachListenerSetter.removeAll();
|
||||
};
|
||||
|
||||
const init = async() => {
|
||||
cleanup();
|
||||
|
||||
buttons.forEach((button) => button.element = undefined);
|
||||
const f = filterButtons || ((buttons: T[]) => filterAsync(buttons, (button) => button?.verify?.() ?? true));
|
||||
|
||||
const filteredButtons = await f(buttons);
|
||||
if(!filteredButtons.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const _element = element = ButtonMenu(filteredButtons, listenerSetter);
|
||||
_element.classList.add('contextmenu');
|
||||
|
||||
appendTo.append(_element);
|
||||
|
||||
return {
|
||||
element: _element,
|
||||
cleanup,
|
||||
destroy: () => {
|
||||
_element.remove();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return {element, destroy};
|
||||
}
|
76
src/helpers/dom/createStickersContextMenu.ts
Normal file
76
src/helpers/dom/createStickersContextMenu.ts
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import type {MyDocument} from '../../lib/appManagers/appDocsManager';
|
||||
import PopupStickers from '../../components/popups/stickers';
|
||||
import appImManager from '../../lib/appManagers/appImManager';
|
||||
import rootScope from '../../lib/rootScope';
|
||||
import createContextMenu from './createContextMenu';
|
||||
import findUpClassName from './findUpClassName';
|
||||
import emoticonsDropdown, {EmoticonsDropdown} from '../../components/emoticonsDropdown';
|
||||
|
||||
export default function createStickersContextMenu(options: {
|
||||
listenTo: HTMLElement,
|
||||
isStickerPack?: boolean,
|
||||
verifyRecent?: (target: HTMLElement) => boolean,
|
||||
appendTo?: HTMLElement,
|
||||
onOpen?: () => any,
|
||||
onClose?: () => any
|
||||
}) {
|
||||
const {listenTo, isStickerPack, verifyRecent, appendTo, onOpen, onClose} = options;
|
||||
let target: HTMLElement, doc: MyDocument;
|
||||
const verifyFavoriteSticker = async(toAdd: boolean) => {
|
||||
const favedStickers = await rootScope.managers.acknowledged.appStickersManager.getFavedStickersStickers();
|
||||
if(!favedStickers.cached) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const found = (await favedStickers.result).some((_doc) => _doc.id === doc.id);
|
||||
return toAdd ? !found : found;
|
||||
};
|
||||
|
||||
return createContextMenu({
|
||||
listenTo: listenTo,
|
||||
appendTo,
|
||||
findElement: (e) => target = findUpClassName(e.target, 'media-sticker-wrapper'),
|
||||
onOpen: async() => {
|
||||
doc = await rootScope.managers.appDocsManager.getDoc(target.dataset.docId);
|
||||
return onOpen?.();
|
||||
},
|
||||
onClose,
|
||||
buttons: [{
|
||||
icon: 'stickers',
|
||||
text: 'Context.ViewStickerSet',
|
||||
onClick: () => new PopupStickers(doc.stickerSetInput).show(),
|
||||
verify: () => !isStickerPack
|
||||
}, {
|
||||
icon: 'favourites',
|
||||
text: 'AddToFavorites',
|
||||
onClick: () => rootScope.managers.appStickersManager.faveSticker(doc.id, false),
|
||||
verify: () => verifyFavoriteSticker(true)
|
||||
}, {
|
||||
icon: 'favourites',
|
||||
text: 'DeleteFromFavorites',
|
||||
onClick: () => rootScope.managers.appStickersManager.faveSticker(doc.id, true),
|
||||
verify: () => verifyFavoriteSticker(false)
|
||||
}, {
|
||||
icon: 'delete',
|
||||
text: 'DeleteFromRecent',
|
||||
onClick: () => emoticonsDropdown.stickersTab.deleteRecentSticker(doc),
|
||||
verify: () => verifyRecent?.(target) ?? false
|
||||
}, {
|
||||
icon: 'mute',
|
||||
text: 'Chat.Send.WithoutSound',
|
||||
onClick: () => EmoticonsDropdown.sendDocId(doc.id, false, true),
|
||||
verify: () => !!(appImManager.chat.peerId && appImManager.chat.peerId !== rootScope.myId)
|
||||
}, {
|
||||
icon: 'schedule',
|
||||
text: 'Chat.Send.ScheduledMessage',
|
||||
onClick: () => appImManager.chat.input.scheduleSending(() => appImManager.chat.input.sendMessageWithDocument(doc)),
|
||||
verify: () => !!appImManager.chat.peerId
|
||||
}]
|
||||
});
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
export default function findUpAsChild(el: any, parent: any) {
|
||||
export default function findUpAsChild(el: HTMLElement, parent: HTMLElement): HTMLElement {
|
||||
if(el.parentElement === parent) return el;
|
||||
|
||||
while(el.parentElement) {
|
||||
|
@ -25,6 +25,7 @@ export default class DropdownHover extends EventListenerBase<{
|
||||
protected displayTimeout: number;
|
||||
protected forceClose = false;
|
||||
protected inited = false;
|
||||
protected ignoreMouseOut = false;
|
||||
|
||||
constructor(options: {
|
||||
element: DropdownHover['element']
|
||||
@ -63,11 +64,15 @@ export default class DropdownHover extends EventListenerBase<{
|
||||
}
|
||||
}
|
||||
|
||||
private onMouseOut = (e: MouseEvent) => {
|
||||
protected onMouseOut = (e: MouseEvent) => {
|
||||
if(KEEP_OPEN || !this.isActive()) return;
|
||||
clearTimeout(this.displayTimeout);
|
||||
|
||||
const toElement = (e as any).toElement as Element;
|
||||
if(this.ignoreMouseOut) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toElement = (e as any).toElement as HTMLElement;
|
||||
if(toElement && findUpAsChild(toElement, this.element)) {
|
||||
return;
|
||||
}
|
||||
@ -162,4 +167,8 @@ export default class DropdownHover extends EventListenerBase<{
|
||||
public isActive() {
|
||||
return this.element.classList.contains('active');
|
||||
}
|
||||
|
||||
public setIgnoreMouseOut(ignore: boolean) {
|
||||
this.ignoreMouseOut = ignore;
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,11 @@ export default class OverlayClickHandler extends EventListenerBase<{
|
||||
protected withOverlay?: boolean
|
||||
) {
|
||||
super(false);
|
||||
this.listenerOptions = withOverlay ? undefined : {capture: true};
|
||||
this.listenerOptions = withOverlay ? {} : {capture: true};
|
||||
}
|
||||
|
||||
protected onClick = (e: MouseEvent | TouchEvent) => {
|
||||
if(this.element && findUpAsChild(e.target, this.element)) {
|
||||
if(this.element && findUpAsChild(e.target as HTMLElement, this.element)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ export default class OverlayClickHandler extends EventListenerBase<{
|
||||
|
||||
if(!IS_TOUCH_SUPPORTED) {
|
||||
// window.removeEventListener('keydown', onKeyDown, {capture: true});
|
||||
window.removeEventListener('contextmenu', this.onClick);
|
||||
window.removeEventListener('contextmenu', this.onClick, this.listenerOptions);
|
||||
}
|
||||
|
||||
document.removeEventListener(CLICK_EVENT_NAME, this.onClick, this.listenerOptions);
|
||||
@ -89,7 +89,7 @@ export default class OverlayClickHandler extends EventListenerBase<{
|
||||
|
||||
if(!IS_TOUCH_SUPPORTED) {
|
||||
// window.addEventListener('keydown', onKeyDown, {capture: true});
|
||||
window.addEventListener('contextmenu', this.onClick, {once: true});
|
||||
window.addEventListener('contextmenu', this.onClick, {...this.listenerOptions, once: true});
|
||||
}
|
||||
|
||||
/* // ! because this event must be canceled, and can't cancel on menu click (below)
|
||||
|
11
src/lang.ts
11
src/lang.ts
@ -749,6 +749,16 @@ const lang = {
|
||||
'ClearRecentStickersAlertTitle': 'Clear recent stickers',
|
||||
'ClearRecentStickersAlertMessage': 'Do you want to clear all your recent stickers?',
|
||||
'PremiumStickersShort': 'Premium',
|
||||
'FavoriteStickers': 'Favorites',
|
||||
'AddToFavorites': 'Add to Favorites',
|
||||
'AddedToFavorites': 'Sticker added to Favorites.',
|
||||
'RemovedFromFavorites': 'Sticker was removed from Favorites',
|
||||
'RemovedFromRecent': 'Sticker was removed from Recent',
|
||||
'DeleteFromFavorites': 'Delete from Favorites',
|
||||
'DeleteFromRecent': 'Remove from Recent',
|
||||
'NewChatsFromNonContacts': 'New chats from unknown users',
|
||||
'ArchiveAndMute': 'Archive and Mute',
|
||||
'ArchiveAndMuteInfo': 'Automatically archive and mute new chats, groups and channels from non-contacts.',
|
||||
|
||||
// * macos
|
||||
'AccountSettings.Filters': 'Chat Folders',
|
||||
@ -949,6 +959,7 @@ const lang = {
|
||||
'ChatList.Mute.3Days': 'For 3 Days',
|
||||
'ChatList.Mute.Forever': 'Forever',
|
||||
'Channel.DescriptionHolderDescrpiton': 'You can provide an optional description for your channel.',
|
||||
'Context.ViewStickerSet': 'View Sticker Set',
|
||||
'CreateGroup.NameHolder': 'Group Name',
|
||||
'Date.Today': 'Today',
|
||||
'DeleteChat.DeleteGroupForAll': 'Delete for all members',
|
||||
|
@ -20,7 +20,7 @@ import assumeType from '../../helpers/assumeType';
|
||||
import {getEnvironment} from '../../environment/utils';
|
||||
import {isServiceWorkerOnline} from '../mtproto/mtproto.worker';
|
||||
import MTProtoMessagePort from '../mtproto/mtprotoMessagePort';
|
||||
import getDocumentInput from './utils/docs/getDocumentInput';
|
||||
import getDocumentInputFileLocation from './utils/docs/getDocumentInputFileLocation';
|
||||
import getDocumentURL from './utils/docs/getDocumentURL';
|
||||
import type {ThumbCache} from '../storages/thumbs';
|
||||
import makeError from '../../helpers/makeError';
|
||||
@ -405,6 +405,6 @@ export class AppDocsManager extends AppManager {
|
||||
public requestDocPart(docId: DocId, dcId: number, offset: number, limit: number) {
|
||||
const doc = this.getDoc(docId);
|
||||
if(!doc) return Promise.reject(makeError('NO_DOC'));
|
||||
return this.apiFileManager.requestFilePart(dcId, getDocumentInput(doc), offset, limit);
|
||||
return this.apiFileManager.requestFilePart(dcId, getDocumentInputFileLocation(doc), offset, limit);
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ import confirmationPopup from '../../components/confirmationPopup';
|
||||
import IS_GROUP_CALL_SUPPORTED from '../../environment/groupCallSupport';
|
||||
import IS_CALL_SUPPORTED from '../../environment/callSupport';
|
||||
import {CallType} from '../calls/types';
|
||||
import {Modify, SendMessageEmojiInteractionData} from '../../types';
|
||||
import {Awaited, Modify, SendMessageEmojiInteractionData} from '../../types';
|
||||
import htmlToSpan from '../../helpers/dom/htmlToSpan';
|
||||
import getVisibleRect from '../../helpers/dom/getVisibleRect';
|
||||
import {attachClickEvent, simulateClickEvent} from '../../helpers/dom/clickEvent';
|
||||
@ -579,12 +579,20 @@ export class AppImManager extends EventListenerBase<{
|
||||
const doc = await this.managers.appDocsManager.getDoc(docId);
|
||||
if(!middleware()) return;
|
||||
|
||||
const {ready, transformer} = await doThatSticker({
|
||||
doc,
|
||||
mediaContainer,
|
||||
middleware,
|
||||
lockGroups: true
|
||||
});
|
||||
let result: Awaited<ReturnType<typeof doThatSticker>>;
|
||||
try {
|
||||
result = await doThatSticker({
|
||||
doc,
|
||||
mediaContainer,
|
||||
middleware,
|
||||
lockGroups: true
|
||||
});
|
||||
if(!result) return;
|
||||
} catch(err) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {ready, transformer} = result;
|
||||
|
||||
previousTransformer = transformer;
|
||||
|
||||
@ -614,12 +622,20 @@ export class AppImManager extends EventListenerBase<{
|
||||
const doc = await this.managers.appDocsManager.getDoc(docId);
|
||||
if(!middleware()) return;
|
||||
|
||||
const {ready, transformer} = await doThatSticker({
|
||||
doc,
|
||||
mediaContainer,
|
||||
middleware,
|
||||
isSwitching: true
|
||||
});
|
||||
let r: Awaited<ReturnType<typeof doThatSticker>>;
|
||||
try {
|
||||
r = await doThatSticker({
|
||||
doc,
|
||||
mediaContainer,
|
||||
middleware,
|
||||
isSwitching: true
|
||||
});
|
||||
if(!r) return;
|
||||
} catch(err) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {ready, transformer} = r;
|
||||
|
||||
const _previousTransformer = previousTransformer;
|
||||
SetTransition(_previousTransformer, 'is-switching', true, switchDuration, () => {
|
||||
@ -660,6 +676,18 @@ export class AppImManager extends EventListenerBase<{
|
||||
document.addEventListener('mouseup', onMouseUp, {once: true});
|
||||
});
|
||||
|
||||
rootScope.addEventListener('sticker_updated', ({type, faved}) => {
|
||||
if(type === 'faved') {
|
||||
toastNew({
|
||||
langPackKey: faved ? 'AddedToFavorites' : 'RemovedFromFavorites'
|
||||
});
|
||||
} else if(!faved) {
|
||||
toastNew({
|
||||
langPackKey: 'RemovedFromRecent'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
apiManagerProxy.addEventListener('notificationBuild', (options) => {
|
||||
if(this.chat.peerId === options.message.peerId && !idleController.isIdle) {
|
||||
return;
|
||||
|
@ -61,6 +61,7 @@ import getAlbumText from './utils/messages/getAlbumText';
|
||||
import pause from '../../helpers/schedulers/pause';
|
||||
import makeError from '../../helpers/makeError';
|
||||
import getStickerEffectThumb from './utils/stickers/getStickerEffectThumb';
|
||||
import getDocumentInput from './utils/docs/getDocumentInput';
|
||||
|
||||
// console.trace('include');
|
||||
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
|
||||
@ -682,9 +683,9 @@ export class AppMessagesManager extends AppManager {
|
||||
size: MediaSize
|
||||
},
|
||||
duration: number,
|
||||
background: true,
|
||||
silent: true,
|
||||
clearDraft: true,
|
||||
background: boolean,
|
||||
silent: boolean,
|
||||
clearDraft: boolean,
|
||||
scheduleDate: number,
|
||||
noSound: boolean,
|
||||
|
||||
@ -930,16 +931,9 @@ export class AppMessagesManager extends AppManager {
|
||||
|
||||
message.send = () => {
|
||||
if(isDocument) {
|
||||
const {id, access_hash, file_reference} = file as MyDocument;
|
||||
|
||||
const inputMedia: InputMedia = {
|
||||
_: 'inputMediaDocument',
|
||||
id: {
|
||||
_: 'inputDocument',
|
||||
id,
|
||||
access_hash,
|
||||
file_reference
|
||||
}
|
||||
id: getDocumentInput(file)
|
||||
};
|
||||
|
||||
sentDeferred.resolve(inputMedia);
|
||||
@ -1465,10 +1459,10 @@ export class AppMessagesManager extends AppManager {
|
||||
} */
|
||||
|
||||
private beforeMessageSending(message: Message.message, options: Partial<{
|
||||
isGroupedItem: true,
|
||||
isScheduled: true,
|
||||
isGroupedItem: boolean,
|
||||
isScheduled: boolean,
|
||||
threadId: number,
|
||||
clearDraft: true,
|
||||
clearDraft: boolean,
|
||||
sequential: boolean,
|
||||
processAfter?: (cb: () => void) => void
|
||||
}> = {}) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import type {MyDocument} from './appDocsManager';
|
||||
import {Document, InputFileLocation, InputStickerSet, MessagesAllStickers, MessagesFeaturedStickers, MessagesFoundStickerSets, MessagesRecentStickers, MessagesStickers, MessagesStickerSet, PhotoSize, StickerPack, StickerSet, StickerSetCovered} from '../../layer';
|
||||
import {Document, InputFileLocation, InputStickerSet, MessagesAllStickers, MessagesFavedStickers, MessagesFeaturedStickers, MessagesFoundStickerSets, MessagesRecentStickers, MessagesStickers, MessagesStickerSet, PhotoSize, StickerPack, StickerSet, StickerSetCovered, Update} from '../../layer';
|
||||
import {Modify} from '../../types';
|
||||
import AppStorage from '../storage';
|
||||
import DATABASE_STATE from '../../config/databases/state';
|
||||
@ -17,6 +17,7 @@ import {AppManager} from './manager';
|
||||
import fixEmoji from '../richTextProcessor/fixEmoji';
|
||||
import ctx from '../../environment/ctx';
|
||||
import {getEnvironment} from '../../environment/utils';
|
||||
import getDocumentInput from './utils/docs/getDocumentInput';
|
||||
|
||||
const CACHE_TIME = 3600e3;
|
||||
|
||||
@ -49,6 +50,9 @@ export class AppStickersManager extends AppManager {
|
||||
private sounds: Record<string, MyDocument>;
|
||||
private getAnimatedEmojiSoundsPromise: Promise<void>;
|
||||
|
||||
private favedStickers: MyDocument[];
|
||||
private recentStickers: MyDocument[];
|
||||
|
||||
protected after() {
|
||||
this.getStickerSetPromises = {};
|
||||
this.getStickersByEmoticonsPromises = {};
|
||||
@ -57,6 +61,7 @@ export class AppStickersManager extends AppManager {
|
||||
this.rootScope.addEventListener('user_auth', () => {
|
||||
setTimeout(() => {
|
||||
this.getAnimatedEmojiStickerSet();
|
||||
this.getFavedStickersStickers();
|
||||
}, 1000);
|
||||
|
||||
if(!this.getGreetingStickersPromise && this.getGreetingStickersTimeout === undefined) {
|
||||
@ -67,6 +72,8 @@ export class AppStickersManager extends AppManager {
|
||||
}
|
||||
});
|
||||
|
||||
this.rootScope.addEventListener('app_config', () => this.onStickersUpdated('faved', true));
|
||||
|
||||
this.apiUpdatesManager.addMultipleEventsListeners({
|
||||
updateNewStickerSet: (update) => {
|
||||
const stickerSet = update.stickerset as MyMessagesStickerSet;
|
||||
@ -74,11 +81,17 @@ export class AppStickersManager extends AppManager {
|
||||
this.rootScope.dispatchEvent('stickers_installed', stickerSet.set);
|
||||
},
|
||||
|
||||
updateRecentStickers: () => {
|
||||
this.getRecentStickers().then(({stickers}) => {
|
||||
this.rootScope.dispatchEvent('stickers_recent', stickers as MyDocument[]);
|
||||
});
|
||||
}
|
||||
updateRecentStickers: () => this.onStickersUpdated('recent', true),
|
||||
|
||||
updateFavedStickers: () => this.onStickersUpdated('faved', true)
|
||||
});
|
||||
}
|
||||
|
||||
private async onStickersUpdated(type: 'faved' | 'recent', overwrite: boolean) {
|
||||
const stickers = await (type === 'faved' ? this.getFavedStickersStickers(overwrite) : this.getRecentStickersStickers(overwrite));
|
||||
this.rootScope.dispatchEvent('stickers_updated', {
|
||||
type,
|
||||
stickers
|
||||
});
|
||||
}
|
||||
|
||||
@ -233,6 +246,7 @@ export class AppStickersManager extends AppManager {
|
||||
processResult: (res) => {
|
||||
assumeType<MessagesRecentStickers.messagesRecentStickers>(res);
|
||||
|
||||
this.recentStickers = res.stickers as MyDocument[];
|
||||
this.saveStickers(res.stickers);
|
||||
return res;
|
||||
}
|
||||
@ -241,6 +255,46 @@ export class AppStickersManager extends AppManager {
|
||||
return res;
|
||||
}
|
||||
|
||||
public getRecentStickersStickers(overwrite?: boolean) {
|
||||
if(overwrite) this.recentStickers = undefined;
|
||||
else if(this.recentStickers) return this.recentStickers;
|
||||
return this.getRecentStickers().then(() => this.recentStickers);
|
||||
}
|
||||
|
||||
public saveRecentSticker(docId: DocId, unsave?: boolean, attached?: boolean) {
|
||||
const doc = this.appDocsManager.getDoc(docId);
|
||||
|
||||
findAndSplice(this.recentStickers, (_doc) => _doc.id === docId);
|
||||
if(!unsave) {
|
||||
this.recentStickers.unshift(doc);
|
||||
|
||||
const docEmoticon = fixEmoji(doc.stickerEmojiRaw);
|
||||
for(const emoticon in this.getStickersByEmoticonsPromises) {
|
||||
const promise = this.getStickersByEmoticonsPromises[emoticon];
|
||||
promise.then((stickers) => {
|
||||
const _doc = findAndSplice(stickers, (_doc) => _doc.id === doc.id);
|
||||
if(_doc) {
|
||||
stickers.unshift(_doc);
|
||||
} else if(emoticon.includes(docEmoticon)) {
|
||||
stickers.unshift(doc);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.rootScope.dispatchEvent('sticker_updated', {type: 'recent', faved: !unsave, document: doc});
|
||||
|
||||
if(unsave) {
|
||||
this.onStickersUpdated('recent', false);
|
||||
}
|
||||
|
||||
return this.apiManager.invokeApi('messages.saveRecentSticker', {
|
||||
id: getDocumentInput(doc),
|
||||
unsave,
|
||||
attached
|
||||
});
|
||||
}
|
||||
|
||||
private cleanEmoji(emoji: string) {
|
||||
return emoji.replace(/\ufe0f/g, '').replace(/🏻|🏼|🏽|🏾|🏿/g, '');
|
||||
}
|
||||
@ -409,14 +463,64 @@ export class AppStickersManager extends AppManager {
|
||||
return res.sets;
|
||||
}
|
||||
|
||||
public async getPromoPremiumStickers() {
|
||||
public getPromoPremiumStickers() {
|
||||
return this.getStickersByEmoticon('⭐️⭐️', false);
|
||||
}
|
||||
|
||||
public async getPremiumStickers() {
|
||||
public getPremiumStickers() {
|
||||
return this.getStickersByEmoticon('📂⭐️', false);
|
||||
}
|
||||
|
||||
public getFavedStickers() {
|
||||
return this.apiManager.invokeApiHashable({
|
||||
method: 'messages.getFavedStickers',
|
||||
processResult: (favedStickers) => {
|
||||
assumeType<MessagesFavedStickers.messagesFavedStickers>(favedStickers);
|
||||
this.saveStickers(favedStickers.stickers);
|
||||
this.favedStickers = favedStickers.stickers as MyDocument[];
|
||||
return favedStickers;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getFavedStickersStickers(overwrite?: boolean) {
|
||||
if(overwrite) this.favedStickers = undefined;
|
||||
else if(this.favedStickers) return this.favedStickers;
|
||||
return this.getFavedStickers().then(() => this.favedStickers);
|
||||
}
|
||||
|
||||
public async getFavedStickersLimit() {
|
||||
const appConfig = await this.apiManager.getAppConfig();
|
||||
return this.rootScope.premium ? appConfig.stickers_faved_limit_premium : appConfig.stickers_faved_limit_default;
|
||||
}
|
||||
|
||||
public async faveSticker(docId: DocId, unfave?: boolean) {
|
||||
if(!this.favedStickers) {
|
||||
await this.getFavedStickersStickers();
|
||||
}
|
||||
|
||||
const limit = await this.getFavedStickersLimit();
|
||||
|
||||
const doc = this.appDocsManager.getDoc(docId);
|
||||
findAndSplice(this.favedStickers, (_doc) => _doc.id === doc.id);
|
||||
|
||||
if(!unfave) {
|
||||
this.favedStickers.unshift(doc);
|
||||
const spliced = this.favedStickers.splice(limit, this.favedStickers.length - limit);
|
||||
}
|
||||
|
||||
this.rootScope.dispatchEvent('sticker_updated', {type: 'faved', faved: !unfave, document: doc});
|
||||
|
||||
return this.apiManager.invokeApi('messages.faveSticker', {
|
||||
id: getDocumentInput(doc),
|
||||
unfave
|
||||
}).then(() => {
|
||||
if(unfave) {
|
||||
this.onStickersUpdated('faved', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async toggleStickerSet(set: StickerSet.stickerSet) {
|
||||
set = this.storage.getFromCache(set.id).set;
|
||||
|
||||
@ -578,24 +682,12 @@ export class AppStickersManager extends AppManager {
|
||||
});
|
||||
}
|
||||
|
||||
public pushRecentSticker(docId: DocId) {
|
||||
const doc = this.appDocsManager.getDoc(docId);
|
||||
const docEmoticon = fixEmoji(doc.stickerEmojiRaw);
|
||||
for(const emoticon in this.getStickersByEmoticonsPromises) {
|
||||
const promise = this.getStickersByEmoticonsPromises[emoticon];
|
||||
promise.then((stickers) => {
|
||||
const _doc = findAndSplice(stickers, _doc => _doc.id === doc.id);
|
||||
if(_doc) {
|
||||
stickers.unshift(_doc);
|
||||
} else if(emoticon.includes(docEmoticon)) {
|
||||
stickers.unshift(doc);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public clearRecentStickers() {
|
||||
this.rootScope.dispatchEvent('stickers_recent', []);
|
||||
if(this.recentStickers) {
|
||||
this.recentStickers.length = 0;
|
||||
this.onStickersUpdated('recent', false);
|
||||
}
|
||||
|
||||
return this.apiManager.invokeApi('messages.clearRecentStickers');
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,10 @@
|
||||
|
||||
import type {Document, PhotoSize, VideoSize} from '../../../../layer';
|
||||
import type {DownloadOptions} from '../../../mtproto/apiFileManager';
|
||||
import getDocumentInput from './getDocumentInput';
|
||||
import getDocumentInputFileLocation from './getDocumentInputFileLocation';
|
||||
|
||||
export default function getDocumentDownloadOptions(doc: Document.document, thumb?: PhotoSize.photoSize | VideoSize, queueId?: number, onlyCache?: boolean): DownloadOptions {
|
||||
const inputFileLocation = getDocumentInput(doc, thumb?.type);
|
||||
const inputFileLocation = getDocumentInputFileLocation(doc, thumb?.type);
|
||||
|
||||
let mimeType: string;
|
||||
if(thumb?._ === 'photoSize') {
|
||||
|
@ -1,17 +1,11 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import {Document, InputFileLocation} from '../../../../layer';
|
||||
|
||||
export default function getInput(doc: Document.document, thumbSize?: string): InputFileLocation.inputDocumentFileLocation {
|
||||
return {
|
||||
_: 'inputDocumentFileLocation',
|
||||
id: doc.id,
|
||||
access_hash: doc.access_hash,
|
||||
file_reference: doc.file_reference,
|
||||
thumb_size: thumbSize
|
||||
};
|
||||
}
|
||||
import {InputDocument} from '../../../../layer';
|
||||
import type {MyDocument} from '../../appDocsManager';
|
||||
|
||||
export default function getDocumentInput(doc: MyDocument): InputDocument {
|
||||
return {
|
||||
_: 'inputDocument',
|
||||
id: doc.id,
|
||||
access_hash: doc.access_hash,
|
||||
file_reference: doc.file_reference
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import {Document, InputFileLocation} from '../../../../layer';
|
||||
|
||||
export default function getDocumentInputFileLocation(doc: Document.document, thumbSize?: string): InputFileLocation.inputDocumentFileLocation {
|
||||
return {
|
||||
_: 'inputDocumentFileLocation',
|
||||
id: doc.id,
|
||||
access_hash: doc.access_hash,
|
||||
file_reference: doc.file_reference,
|
||||
thumb_size: thumbSize
|
||||
};
|
||||
}
|
@ -6,8 +6,8 @@
|
||||
|
||||
import {getFileNameByLocation} from '../../../../helpers/fileName';
|
||||
import {Document} from '../../../../layer';
|
||||
import getDocumentInput from './getDocumentInput';
|
||||
import getDocumentInputFileLocation from './getDocumentInputFileLocation';
|
||||
|
||||
export default function getDocumentInputFileName(doc: Document.document, thumbSize?: string) {
|
||||
return getFileNameByLocation(getDocumentInput(doc, thumbSize), {fileName: doc.file_name});
|
||||
return getFileNameByLocation(getDocumentInputFileLocation(doc, thumbSize), {fileName: doc.file_name});
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ export class ApiManager extends ApiManagerMethods {
|
||||
protected after() {
|
||||
this.apiUpdatesManager.addMultipleEventsListeners({
|
||||
updateConfig: () => {
|
||||
this.getConfig();
|
||||
this.getConfig(true);
|
||||
this.getAppConfig(true);
|
||||
}
|
||||
});
|
||||
|
@ -17,14 +17,17 @@ import isObject from '../../helpers/object/isObject';
|
||||
import gzipUncompress from '../../helpers/gzipUncompress';
|
||||
import bigInt from 'big-integer';
|
||||
import ulongFromInts from '../../helpers/long/ulongFromInts';
|
||||
import { safeBigInt } from '../../helpers/bigInt/bigIntConstants';
|
||||
import { bigIntToSigned, bigIntToUnsigned } from '../../helpers/bigInt/bigIntConversion';
|
||||
import {safeBigInt} from '../../helpers/bigInt/bigIntConstants';
|
||||
import {bigIntToSigned, bigIntToUnsigned} from '../../helpers/bigInt/bigIntConversion';
|
||||
|
||||
const boolFalse = +Schema.API.constructors.find((c) => c.predicate === 'boolFalse').id;
|
||||
const boolTrue = +Schema.API.constructors.find((c) => c.predicate === 'boolTrue').id;
|
||||
const vector = +Schema.API.constructors.find((c) => c.predicate === 'vector').id;
|
||||
const gzipPacked = +Schema.MTProto.constructors.find((c) => c.predicate === 'gzip_packed').id;
|
||||
|
||||
// * using slice to have a new buffer, otherwise the buffer will be copied to main thread
|
||||
const sliceMethod: 'slice' | 'subarray' = 'slice'; // subarray
|
||||
|
||||
class TLSerialization {
|
||||
private maxLength = 2048; // 2Kb
|
||||
private offset = 0; // in bytes
|
||||
@ -565,7 +568,7 @@ class TLDeserialization<FetchLongAs extends Long> {
|
||||
(this.byteView[this.offset++] << 16);
|
||||
}
|
||||
|
||||
const bytes = this.byteView.subarray(this.offset, this.offset + len);
|
||||
const bytes = this.byteView[sliceMethod](this.offset, this.offset + len);
|
||||
this.offset += len;
|
||||
|
||||
// Padding
|
||||
@ -587,7 +590,7 @@ class TLDeserialization<FetchLongAs extends Long> {
|
||||
|
||||
const len = bits / 8;
|
||||
if(typed) {
|
||||
const result = this.byteView.subarray(this.offset, this.offset + len);
|
||||
const result = this.byteView[sliceMethod](this.offset, this.offset + len);
|
||||
this.offset += len;
|
||||
return result;
|
||||
}
|
||||
@ -614,7 +617,7 @@ class TLDeserialization<FetchLongAs extends Long> {
|
||||
|
||||
if(typed) {
|
||||
const bytes = new Uint8Array(len);
|
||||
bytes.set(this.byteView.subarray(this.offset, this.offset + len));
|
||||
bytes.set(this.byteView[sliceMethod](this.offset, this.offset + len));
|
||||
this.offset += len;
|
||||
return bytes;
|
||||
}
|
||||
|
@ -87,7 +87,8 @@ export type BroadcastEvents = {
|
||||
|
||||
'stickers_installed': StickerSet.stickerSet,
|
||||
'stickers_deleted': StickerSet.stickerSet,
|
||||
'stickers_recent': MyDocument[],
|
||||
'stickers_updated': {type: 'recent' | 'faved', stickers: MyDocument[]},
|
||||
'sticker_updated': {type: 'recent' | 'faved', document: MyDocument, faved: boolean},
|
||||
|
||||
'state_cleared': void,
|
||||
'state_synchronized': ChatId | void,
|
||||
|
@ -4,6 +4,8 @@
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
$btn-menu-z-index: 4;
|
||||
|
||||
.btn,
|
||||
.btn-icon {
|
||||
background: none;
|
||||
@ -97,7 +99,7 @@
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
background-color: var(--menu-background-color);
|
||||
z-index: 3;
|
||||
z-index: $btn-menu-z-index;
|
||||
top: 100%;
|
||||
padding: .3125rem 0;
|
||||
border-radius: $border-radius-medium;
|
||||
@ -324,7 +326,7 @@
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
z-index: $btn-menu-z-index;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
//background-color: rgba(0, 0, 0, .2);
|
||||
|
@ -188,7 +188,7 @@
|
||||
}
|
||||
|
||||
&.active {
|
||||
&:not(.tgico-recent) {
|
||||
&:not(.tgico-recent):not(.tgico-saved) {
|
||||
background-color: var(--light-secondary-text-color);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
&-container {
|
||||
padding: .75rem .5rem;
|
||||
min-width: 17.5rem;
|
||||
max-width: unquote('min(400px, 100%)');
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user