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