Browse Source

New bot commands menu

master
Eduard Kuzmenko 3 years ago
parent
commit
d6b987eba4
  1. 28
      src/components/chat/autocompleteHelper.ts
  2. 13
      src/components/chat/autocompletePeerHelper.ts
  3. 54
      src/components/chat/botCommands.ts
  4. 59
      src/components/chat/commandsHelper.ts
  5. 109
      src/components/chat/input.ts
  6. 4
      src/scss/components/_global.scss
  7. 51
      src/scss/partials/_animatedIcon.scss
  8. 94
      src/scss/partials/_chat.scss
  9. 85
      src/scss/partials/_chatBotCommands.scss
  10. 1
      src/scss/style.scss

28
src/components/chat/autocompleteHelper.ts

@ -16,6 +16,7 @@ import AutocompleteHelperController from "./autocompleteHelperController";
export default class AutocompleteHelper extends EventListenerBase<{ export default class AutocompleteHelper extends EventListenerBase<{
hidden: () => void, hidden: () => void,
visible: () => void, visible: () => void,
hiding: () => void
}> { }> {
protected hidden = true; protected hidden = true;
protected container: HTMLElement; protected container: HTMLElement;
@ -34,7 +35,7 @@ export default class AutocompleteHelper extends EventListenerBase<{
constructor(options: { constructor(options: {
appendTo: HTMLElement, appendTo: HTMLElement,
controller: AutocompleteHelper['controller'], controller?: AutocompleteHelper['controller'],
listType: AutocompleteHelper['listType'], listType: AutocompleteHelper['listType'],
onSelect: AutocompleteHelper['onSelect'], onSelect: AutocompleteHelper['onSelect'],
waitForKey?: AutocompleteHelper['waitForKey'] waitForKey?: AutocompleteHelper['waitForKey']
@ -50,7 +51,7 @@ export default class AutocompleteHelper extends EventListenerBase<{
this.attachNavigation(); this.attachNavigation();
this.controller.addHelper(this); this.controller && this.controller.addHelper(this);
} }
public toggleListNavigation(enabled: boolean) { public toggleListNavigation(enabled: boolean) {
@ -110,7 +111,7 @@ export default class AutocompleteHelper extends EventListenerBase<{
this.addEventListener('visible', this.onVisible); this.addEventListener('visible', this.onVisible);
} }
public toggle(hide?: boolean, fromController = false) { public toggle(hide?: boolean, fromController = false, skipAnimation?: boolean) {
if(this.init) { if(this.init) {
return; return;
} }
@ -130,7 +131,7 @@ export default class AutocompleteHelper extends EventListenerBase<{
this.hidden = hide; this.hidden = hide;
if(!hide) { if(!hide) {
this.controller.hideOtherHelpers(this); this.controller && this.controller.hideOtherHelpers(this);
this.dispatchEvent('visible'); // fire it before so target will be set this.dispatchEvent('visible'); // fire it before so target will be set
} else { } else {
if(this.navigationItem) { if(this.navigationItem) {
@ -138,7 +139,7 @@ export default class AutocompleteHelper extends EventListenerBase<{
this.navigationItem = undefined; this.navigationItem = undefined;
} }
if(!fromController) { if(!fromController && this.controller) {
this.controller.hideOtherHelpers(); this.controller.hideOtherHelpers();
} }
@ -147,8 +148,21 @@ export default class AutocompleteHelper extends EventListenerBase<{
} }
} }
SetTransition(this.container, 'is-visible', !hide, rootScope.settings.animationsEnabled ? 200 : 0, () => { const useRafs = this.controller || hide ? 0 : 2;
if(hide) {
this.dispatchEvent('hiding');
}
SetTransition(
this.container,
'is-visible',
!hide,
rootScope.settings.animationsEnabled && !skipAnimation ? 300 : 0,
() => {
this.hidden && this.dispatchEvent('hidden'); this.hidden && this.dispatchEvent('hidden');
}); },
useRafs
);
} }
} }

13
src/components/chat/autocompletePeerHelper.ts

@ -16,7 +16,12 @@ export default class AutocompletePeerHelper extends AutocompleteHelper {
protected static BASE_CLASS_LIST_ELEMENT = AutocompletePeerHelper.BASE_CLASS + '-list-element'; protected static BASE_CLASS_LIST_ELEMENT = AutocompletePeerHelper.BASE_CLASS + '-list-element';
private scrollable: Scrollable; private scrollable: Scrollable;
constructor(appendTo: HTMLElement, controller: AutocompleteHelperController, protected className: string, onSelect: (target: Element) => boolean | void) { constructor(
appendTo: HTMLElement,
controller: AutocompleteHelperController,
protected className: string,
onSelect: (target: Element) => boolean | void
) {
super({ super({
appendTo, appendTo,
controller, controller,
@ -29,7 +34,7 @@ export default class AutocompletePeerHelper extends AutocompleteHelper {
protected init() { protected init() {
this.list = document.createElement('div'); this.list = document.createElement('div');
this.list.classList.add(AutocompletePeerHelper.BASE_CLASS + '-list'); this.list.classList.add(AutocompletePeerHelper.BASE_CLASS + '-list', this.className + '-list');
this.container.append(this.list); this.container.append(this.list);
@ -42,7 +47,7 @@ export default class AutocompletePeerHelper extends AutocompleteHelper {
}); });
} }
public render(data: {peerId: PeerId, name?: string, description?: string}[]) { public render(data: {peerId: PeerId, name?: string, description?: string}[], doNotShow?: boolean) {
if(this.init) { if(this.init) {
if(!data.length) { if(!data.length) {
return; return;
@ -66,8 +71,10 @@ export default class AutocompletePeerHelper extends AutocompleteHelper {
}); });
} }
if(!doNotShow) {
this.toggle(!data.length); this.toggle(!data.length);
} }
}
public static listElement(options: { public static listElement(options: {
className: string, className: string,

54
src/components/chat/botCommands.ts

@ -0,0 +1,54 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type { AppProfileManager } from "../../lib/appManagers/appProfileManager";
import type ChatInput from "./input";
import callbackify from "../../helpers/callbackify";
import AutocompletePeerHelper from "./autocompletePeerHelper";
import { processPeerFullForCommands } from "./commandsHelper";
const CLASS_NAME = 'bot-commands';
export default class ChatBotCommands extends AutocompletePeerHelper {
private userId: UserId;
constructor(
appendTo: HTMLElement,
private chatInput: ChatInput,
private appProfileManager: AppProfileManager
) {
super(appendTo, undefined, CLASS_NAME, (target) => {
const innerHTML = target.querySelector(`.${AutocompletePeerHelper.BASE_CLASS_LIST_ELEMENT}-name`).innerHTML;
return chatInput.getReadyToSend(() => {
chatInput.messageInput.innerHTML = innerHTML;
chatInput.sendMessage(true);
this.toggle(true);
});
});
}
public setUserId(userId: UserId, middleware: () => boolean) {
if(this.userId === userId && this.list?.childElementCount) {
this.toggle(false);
return;
}
this.userId = userId;
return callbackify(this.appProfileManager.getProfile(userId), (full) => {
if(!middleware()) return;
const filtered = processPeerFullForCommands(full);
const PADDING_TOP = 8;
// const PADDING_BOTTOM = 8;
const PADDING_BOTTOM = 24;
const height = filtered.length * 50 + PADDING_TOP + PADDING_BOTTOM;
this.container.style.setProperty('--height', height + 'px');
this.render(filtered);
// this.container.style.top =
});
}
}

59
src/components/chat/commandsHelper.ts

@ -7,11 +7,46 @@
import type ChatInput from "./input"; import type ChatInput from "./input";
import type { AppProfileManager } from "../../lib/appManagers/appProfileManager"; import type { AppProfileManager } from "../../lib/appManagers/appProfileManager";
import type { AppUsersManager } from "../../lib/appManagers/appUsersManager"; import type { AppUsersManager } from "../../lib/appManagers/appUsersManager";
import type { BotInfo } from "../../layer"; import type { BotInfo, ChatFull, UserFull } from "../../layer";
import AutocompleteHelperController from "./autocompleteHelperController"; import AutocompleteHelperController from "./autocompleteHelperController";
import AutocompletePeerHelper from "./autocompletePeerHelper"; import AutocompletePeerHelper from "./autocompletePeerHelper";
import SearchIndex from "../../lib/searchIndex"; import SearchIndex from "../../lib/searchIndex";
export function processPeerFullForCommands(full: ChatFull.chatFull | ChatFull.channelFull | UserFull.userFull, query?: string) {
const botInfos: BotInfo.botInfo[] = [].concat(full.bot_info);
let index: SearchIndex<string>;
if(query !== undefined) {
index = new SearchIndex<string>({
ignoreCase: true
});
}
const commands: Map<string, {peerId: PeerId, name: string, description: string}> = new Map();
botInfos.forEach(botInfo => {
botInfo.commands.forEach(botCommand => {
const c = '/' + botCommand.command;
commands.set(botCommand.command, {
peerId: botInfo.user_id.toPeerId(false),
name: c,
description: botCommand.description
});
if(index) {
index.indexObject(botCommand.command, c);
}
});
});
if(!index) {
return [...commands.values()];
}
const found = index.search(query);
const filtered = Array.from(found).map(command => commands.get(command));
return filtered;
}
export default class CommandsHelper extends AutocompletePeerHelper { export default class CommandsHelper extends AutocompletePeerHelper {
constructor(appendTo: HTMLElement, constructor(appendTo: HTMLElement,
controller: AutocompleteHelperController, controller: AutocompleteHelperController,
@ -42,27 +77,7 @@ export default class CommandsHelper extends AutocompletePeerHelper {
return; return;
} }
const botInfos: BotInfo.botInfo[] = [].concat(full.bot_info); const filtered = processPeerFullForCommands(full, query);
const index = new SearchIndex<string>({
ignoreCase: true
});
const commands: Map<string, {peerId: PeerId, name: string, description: string}> = new Map();
botInfos.forEach(botInfo => {
botInfo.commands.forEach(botCommand => {
const c = '/' + botCommand.command;
commands.set(botCommand.command, {
peerId: botInfo.user_id.toPeerId(false),
name: c,
description: botCommand.description
});
index.indexObject(botCommand.command, c);
});
});
const found = index.search(query);
const filtered = Array.from(found).map(command => commands.get(command));
this.render(filtered); this.render(filtered);
// console.log('found commands', found, filtered); // console.log('found commands', found, filtered);
}); });

109
src/components/chat/input.ts

@ -32,7 +32,7 @@ import PopupNewMedia from '../popups/newMedia';
import { toast } from "../toast"; import { toast } from "../toast";
import { wrapReply } from "../wrappers"; import { wrapReply } from "../wrappers";
import InputField from '../inputField'; import InputField from '../inputField';
import { MessageEntity, DraftMessage, WebPage, Message, ChatFull } from '../../layer'; import { MessageEntity, DraftMessage, WebPage, Message, ChatFull, UserFull } from '../../layer';
import StickersHelper from './stickersHelper'; import StickersHelper from './stickersHelper';
import ButtonIcon from '../buttonIcon'; import ButtonIcon from '../buttonIcon';
import ButtonMenuToggle from '../buttonMenuToggle'; import ButtonMenuToggle from '../buttonMenuToggle';
@ -91,6 +91,7 @@ import AvatarElement from '../avatar';
import type { AppProfileManager } from '../../lib/appManagers/appProfileManager'; import type { AppProfileManager } from '../../lib/appManagers/appProfileManager';
import { indexOfAndSplice } from '../../helpers/array'; import { indexOfAndSplice } from '../../helpers/array';
import callbackify from '../../helpers/callbackify'; import callbackify from '../../helpers/callbackify';
import ChatBotCommands from './botCommands';
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.';
@ -222,6 +223,11 @@ export default class ChatInput {
public sendAsPeerId: PeerId; public sendAsPeerId: PeerId;
private updatingSendAsPromise: Promise<void>; private updatingSendAsPromise: Promise<void>;
private botCommandsToggle: HTMLElement;
private botCommands: ChatBotCommands;
private botCommandsIcon: HTMLDivElement;
hasBotCommands: number;
// private activeContainer: HTMLElement; // private activeContainer: HTMLElement;
constructor( constructor(
@ -573,6 +579,38 @@ export default class ChatInput {
}); });
this.listenerSetter.add(this.replyKeyboard)('open', () => this.btnToggleReplyMarkup.classList.add('active')); this.listenerSetter.add(this.replyKeyboard)('open', () => this.btnToggleReplyMarkup.classList.add('active'));
this.listenerSetter.add(this.replyKeyboard)('close', () => this.btnToggleReplyMarkup.classList.remove('active')); this.listenerSetter.add(this.replyKeyboard)('close', () => this.btnToggleReplyMarkup.classList.remove('active'));
this.botCommands = new ChatBotCommands(this.rowsWrapper, this, this.appProfileManager);
this.botCommandsToggle = document.createElement('div');
this.botCommandsToggle.classList.add('new-message-bot-commands');
const scaler = document.createElement('div');
scaler.classList.add('new-message-bot-commands-icon-scale');
const icon = this.botCommandsIcon = document.createElement('div');
icon.classList.add('animated-menu-icon', 'animated-menu-close-icon');
scaler.append(icon);
this.botCommandsToggle.append(scaler);
attachClickEvent(this.botCommandsToggle, (e) => {
cancelEvent(e);
const isShown = icon.classList.contains('state-back');
if(isShown) {
this.botCommands.toggle(true);
icon.classList.remove('state-back');
} else {
this.botCommands.setUserId(this.chat.peerId.toUserId(), this.chat.bubbles.getMiddleware());
icon.classList.add('state-back');
}
}, {listenerSetter: this.listenerSetter});
this.botCommands.addEventListener('visible', () => {
icon.classList.add('state-back');
});
this.botCommands.addEventListener('hiding', () => {
icon.classList.remove('state-back');
});
} }
this.attachMenuButtons = [{ this.attachMenuButtons = [{
@ -619,7 +657,7 @@ export default class ChatInput {
this.fileInput.multiple = true; this.fileInput.multiple = true;
this.fileInput.style.display = 'none'; this.fileInput.style.display = 'none';
this.newMessageWrapper.append(...[this.sendAsContainer, this.btnToggleEmoticons, this.inputMessageContainer, this.btnScheduled, this.btnToggleReplyMarkup, this.attachMenu, this.recordTimeEl, this.fileInput].filter(Boolean)); this.newMessageWrapper.append(...[this.sendAsContainer, this.botCommandsToggle, this.btnToggleEmoticons, this.inputMessageContainer, this.btnScheduled, this.btnToggleReplyMarkup, this.attachMenu, this.recordTimeEl, this.fileInput].filter(Boolean));
this.rowsWrapper.append(this.replyElements.container); this.rowsWrapper.append(this.replyElements.container);
this.autocompleteHelperController = new AutocompleteHelperController(); this.autocompleteHelperController = new AutocompleteHelperController();
@ -1194,7 +1232,7 @@ export default class ChatInput {
public finishPeerChange(startParam?: string) { public finishPeerChange(startParam?: string) {
const peerId = this.chat.peerId; const peerId = this.chat.peerId;
const {forwardElements, btnScheduled, replyKeyboard, sendMenu, goDownBtn, chatInput, sendAsContainer} = this; const {forwardElements, btnScheduled, replyKeyboard, sendMenu, goDownBtn, chatInput, sendAsContainer, botCommandsToggle} = this;
chatInput.style.display = ''; chatInput.style.display = '';
const isBroadcast = this.appPeersManager.isBroadcast(peerId); const isBroadcast = this.appPeersManager.isBroadcast(peerId);
@ -1225,6 +1263,24 @@ export default class ChatInput {
}); });
} }
this.updateOffset(null, false, true);
if(botCommandsToggle) {
this.hasBotCommands = undefined;
this.botCommands.toggle(true, undefined, true);
this.updateBotCommandsToggle(true);
botCommandsToggle.remove();
if(this.appPeersManager.isBot(peerId)) {
const userId = peerId.toUserId();
const middleware = this.chat.bubbles.getMiddleware();
const getUserFullResult = this.appProfileManager.getProfile(userId);
callbackify(getUserFullResult, (userFull) => {
if(!middleware()) return;
this.updateBotCommands(userFull, !(getUserFullResult instanceof Promise));
});
}
}
if(sendAsContainer) { if(sendAsContainer) {
if(this.sendAsAvatar) { if(this.sendAsAvatar) {
this.sendAsAvatar.remove(); this.sendAsAvatar.remove();
@ -1232,7 +1288,6 @@ export default class ChatInput {
} }
sendAsContainer.remove(); sendAsContainer.remove();
SetTransition(this.newMessageWrapper, 'has-send-as', false, 0);
this.sendAsPeerId = undefined; this.sendAsPeerId = undefined;
this.updatingSendAsPromise = undefined; this.updatingSendAsPromise = undefined;
@ -1261,6 +1316,39 @@ export default class ChatInput {
this.center(false); this.center(false);
} }
private updateOffset(type: 'commands' | 'as', forwards: boolean, skipAnimation?: boolean, useRafs?: number) {
if(type) {
this.newMessageWrapper.dataset.offset = type;
} else {
delete this.newMessageWrapper.dataset.offset;
}
SetTransition(this.newMessageWrapper, 'has-offset', forwards, skipAnimation ? 0 : 300, undefined, useRafs);
}
private updateBotCommands(userFull: UserFull.userFull, skipAnimation?: boolean) {
this.hasBotCommands = userFull.bot_info && userFull.bot_info.commands.length;
this.updateBotCommandsToggle(skipAnimation);
}
private updateBotCommandsToggle(skipAnimation?: boolean) {
const {botCommandsToggle, hasBotCommands} = this;
const show = hasBotCommands && this.isInputEmpty();
if(!hasBotCommands) {
botCommandsToggle.remove();
}
const forwards = show;
const useRafs = botCommandsToggle.parentElement ? 0 : 2;
if(!botCommandsToggle.parentElement) {
this.newMessageWrapper.prepend(botCommandsToggle);
}
this.updateOffset('commands', forwards, skipAnimation, useRafs);
}
private updateSendAsButtons(peerIds: PeerId[]) { private updateSendAsButtons(peerIds: PeerId[]) {
const buttons: ButtonMenuItemOptions[] = peerIds.map((sendAsPeerId, idx) => { const buttons: ButtonMenuItemOptions[] = peerIds.map((sendAsPeerId, idx) => {
const textElement = document.createElement('div'); const textElement = document.createElement('div');
@ -1415,7 +1503,7 @@ export default class ChatInput {
useRafs = 2; useRafs = 2;
} }
SetTransition(this.newMessageWrapper, 'has-send-as', true, skipAnimation ? 0 : SEND_AS_ANIMATION_DURATION, undefined, useRafs); this.updateOffset('as', true, skipAnimation, useRafs);
this.updatingSendAsPromise = undefined; this.updatingSendAsPromise = undefined;
}); });
@ -1850,7 +1938,8 @@ export default class ChatInput {
} }
} }
if(!richValue.trim()) { const isEmpty = !richValue.trim();
if(isEmpty) {
if(this.lastTimeType) { if(this.lastTimeType) {
this.appMessagesManager.setTyping(this.chat.peerId, {_: 'sendMessageCancelAction'}); this.appMessagesManager.setTyping(this.chat.peerId, {_: 'sendMessageCancelAction'});
} }
@ -1864,6 +1953,14 @@ export default class ChatInput {
this.lastTimeType = time; this.lastTimeType = time;
this.appMessagesManager.setTyping(this.chat.peerId, {_: 'sendMessageTypingAction'}); this.appMessagesManager.setTyping(this.chat.peerId, {_: 'sendMessageTypingAction'});
} }
if(this.botCommands) {
this.botCommands.toggle(true);
}
}
if(this.botCommands) {
this.updateBotCommandsToggle();
} }
if(!this.editMsgId) { if(!this.editMsgId) {

4
src/scss/components/_global.scss

@ -96,7 +96,11 @@ Utility Classes
} }
.no-transition { .no-transition {
&,
&:before,
&:after {
transition: none !important; transition: none !important;
}
/* &-all, &-all * { /* &-all, &-all * {
transition: none !important; transition: none !important;

51
src/scss/partials/_animatedIcon.scss

@ -31,12 +31,6 @@
&, &:before, &:after { &, &:before, &:after {
transition: transform var(--slide-header-transition); transition: transform var(--slide-header-transition);
} }
&.no-transition {
&, &:before, &:after {
transition: none;
}
}
} }
&.state-back { &.state-back {
@ -53,13 +47,14 @@
} }
.animated-menu-icon { .animated-menu-icon {
--color: var(--secondary-text-color);
position: absolute; position: absolute;
&, &:before, &:after { &, &:before, &:after {
width: 1.125rem; width: 1.125rem;
height: .125rem; height: .125rem;
border-radius: .125rem; border-radius: .125rem;
background-color: var(--secondary-text-color); background-color: var(--color);
transform: rotate(0); transform: rotate(0);
} }
@ -96,6 +91,48 @@
} }
} }
.animated-menu-close-icon {
margin-top: -.625rem;
&:before {
top: .3125rem;
opacity: 1;
@include animation-level(2) {
transition: transform .25s, opacity .125s 0s;
}
}
&:after {
top: .625rem;
}
&.state-back {
transform: translate(0, .3125rem) rotate(135deg);
&:before {
transform: rotate(45deg);
opacity: 0;
}
&:after {
transform: translate(-.0rem, -.625rem) rotate(90deg);
}
}
/* &.state-back {
transform: rotate(135deg) translate(.25rem, -.1875rem);
&:before {
transform: rotate(45deg);
opacity: 0;
}
&:after {
transform: rotate(90deg) translate(-.625rem, 0rem);
}
} */
}
.animated-button-icon { .animated-button-icon {
> .tgico { > .tgico {
position: absolute; position: absolute;

94
src/scss/partials/_chat.scss

@ -5,7 +5,8 @@
*/ */
$btn-send-margin: .5rem; $btn-send-margin: .5rem;
$chat-helper-size: 36px; $chat-helper-size: 45px;
$chat-input-box-shadow: 0px 1px 8px 1px rgb(0 0 0 / 18%);
$input-transition-time: .2s; $input-transition-time: .2s;
$input-half-transition-time: #{$input-transition-time / 2}; $input-half-transition-time: #{$input-transition-time / 2};
@ -765,7 +766,7 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
bottom: 0; bottom: 0;
left: 0; left: 0;
border-radius: inherit; border-radius: inherit;
box-shadow: 0px 1px 8px 1px rgb(0 0 0 / 18%); box-shadow: $chat-input-box-shadow;
background-color: #fff; background-color: #fff;
background-color: var(--surface-color); background-color: var(--surface-color);
opacity: 1; opacity: 1;
@ -1047,9 +1048,9 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
height: 100%; height: 100%;
z-index: 1; */ z-index: 1; */
height: 0; height: 0;
width: calc(100% - var(--padding-horizontal) * 2); width: 100%;
padding: 0; padding: .5625rem var(--padding-horizontal) 0;
margin-top: .5625rem;//var(--padding-vertical); // margin-top: .5625rem;//var(--padding-vertical);
margin-bottom: -.5625rem; margin-bottom: -.5625rem;
//height: calc(#{$chat-helper-size} + .3125rem); //height: calc(#{$chat-helper-size} + .3125rem);
align-items: center; align-items: center;
@ -1064,7 +1065,7 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
} }
@include respond-to(esg-bottom-new) { @include respond-to(esg-bottom-new) {
margin-top: .3125rem; padding-top: .3125rem;
margin-bottom: -.3125rem; margin-bottom: -.3125rem;
} }
@ -1163,29 +1164,78 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
} */ } */
} }
/* &:after {
content: " ";
position: absolute;
top: 0;
width: 100%;
height: .0625rem;
background-color: var(--border-color);
opacity: 0;
z-index: 2;
}
&.have-commands {
&:after {
opacity: 1;
}
} */
.new-message-wrapper { .new-message-wrapper {
--send-as-size: 1.875rem; --send-as-size: 1.875rem;
--send-as-margin-left: .25rem; --send-as-margin-left: .25rem;
--send-as-margin-right: .375rem; --send-as-margin-right: .375rem;
--send-as-total-size: calc(var(--send-as-size) + var(--send-as-margin-left) + var(--send-as-margin-right)); --send-as-total-size: calc(var(--send-as-size) + var(--send-as-margin-left) + var(--send-as-margin-right));
--commands-size: 2.375rem;
--commands-margin-left: .25rem;
--commands-margin-right: .375rem;
--commands-total-size: calc(var(--commands-size) + var(--commands-margin-left) + var(--commands-margin-right));
--offset-translateX: 0px;
//padding: 4.5px 0; //padding: 4.5px 0;
//padding-bottom: 4.5px; //padding-bottom: 4.5px;
align-items: flex-end; align-items: flex-end;
min-height: var(--chat-input-size); min-height: var(--chat-input-size);
.new-message-bot-commands,
.new-message-send-as-container {
position: absolute;
flex: 0 0 auto;
bottom: calc(var(--padding-vertical) + .4375rem);
cursor: pointer;
transform: scale(0);
z-index: 2;
}
.new-message-bot-commands {
width: var(--commands-size);
height: 1.875rem;
border-radius: 1.875rem;
background-color: var(--primary-color);
display: flex;
align-items: center;
justify-content: center;
margin-left: var(--commands-margin-left);
cursor: pointer;
&-icon-scale {
transform: scale(.875);
display: flex;
align-items: center;
justify-content: center;
}
.animated-menu-close-icon {
--color: #fff;
}
}
.new-message-send-as { .new-message-send-as {
&-container { &-container {
width: var(--send-as-size); width: var(--send-as-size);
height: var(--send-as-size); height: var(--send-as-size);
position: absolute;
flex: 0 0 auto;
// margin: 0 0.375rem .4375rem var(--send-as-margin-left); // margin: 0 0.375rem .4375rem var(--send-as-margin-left);
margin-left: var(--send-as-margin-left); margin-left: var(--send-as-margin-left);
bottom: calc(var(--padding-vertical) + .4375rem);
cursor: pointer;
transform: scale(0);
background: none !important; background: none !important;
z-index: 2;
.btn-menu { .btn-menu {
max-height: 20rem; max-height: 20rem;
@ -1253,7 +1303,15 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
} }
} }
&.has-send-as { &.has-offset {
&[data-offset="as"] {
--offset-translateX: var(--send-as-total-size);
}
&[data-offset="commands"] {
--offset-translateX: 48px;
}
.toggle-emoticons, .toggle-emoticons,
.input-message-container { .input-message-container {
transform: translateX(0); transform: translateX(0);
@ -1261,16 +1319,17 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
&:not(.backwards) { &:not(.backwards) {
.toggle-emoticons { .toggle-emoticons {
transform: translateX(var(--send-as-total-size)); transform: translateX(var(--offset-translateX));
} }
.input-message-container { .input-message-container {
--translateX: calc(var(--send-as-total-size)); --translateX: calc(var(--offset-translateX));
padding-right: var(--translateX); padding-right: var(--translateX);
transform: translate(var(--translateX)); transform: translate(var(--translateX));
} }
.new-message-send-as-container { .new-message-send-as-container,
.new-message-bot-commands {
transform: scale(1); transform: scale(1);
} }
} }
@ -1278,7 +1337,8 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
&.animating { &.animating {
.toggle-emoticons, .toggle-emoticons,
.input-message-container, .input-message-container,
.new-message-send-as-container { .new-message-send-as-container,
.new-message-bot-commands {
transition: transform var(--transition-standard-in); transition: transform var(--transition-standard-in);
} }
} }

85
src/scss/partials/_chatBotCommands.scss

@ -0,0 +1,85 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
.bot-commands {
--border-radius-padding: #{$border-radius-big * 2};
--offset: .5rem;
position: absolute !important;
// bottom: 100%;
bottom: calc(100% - var(--border-radius-padding));
right: calc(var(--offset) * -1);
left: calc(var(--offset) * -1);
width: auto !important;
max-height: 20rem;
max-width: none;
border-radius: $border-radius-big $border-radius-big 0 0 !important;
background-color: transparent !important;
pointer-events: none;
overflow: hidden;
padding: var(--offset) var(--offset) 0 !important;
box-shadow: none;
animation: none !important;
visibility: visible !important;
transition: none !important;
display: flex !important;
.scrollable {
background-color: var(--surface-color);
box-shadow: $chat-input-box-shadow;
border-radius: inherit;
height: auto;
pointer-events: all;
// max-height: 20rem;
@include animation-level(2) {
opacity: 0;
transform: translateY(var(--height));
}
}
&.is-visible {
&.animating {
.scrollable {
transition: transform var(--transition-standard-in), opacity var(--transition-standard-in);
}
}
&:not(.backwards) {
.scrollable {
transform: translateY(0);
opacity: 1;
}
}
}
&-list {
border-radius: inherit;
width: 100%;
height: var(--height);
// padding-bottom: var(--border-radius-padding);
padding-bottom: 0;
&-element {
border-radius: 0 !important;
flex-direction: column;
align-items: flex-start;
justify-content: center;
padding-left: 3.375rem;
&-avatar {
position: absolute;
left: .75rem;
}
&-name,
&-description {
margin-left: 0;
font-size: .875rem;
line-height: var(--line-height-14);
}
}
}
}

1
src/scss/style.scss

@ -307,6 +307,7 @@ $chat-input-inner-padding-handhelds: .25rem;
@import "partials/chatInlineHelper"; @import "partials/chatInlineHelper";
@import "partials/chatSearch"; @import "partials/chatSearch";
@import "partials/chatDrop"; @import "partials/chatDrop";
@import "partials/chatBotCommands";
@import "partials/crop"; @import "partials/crop";
@import "partials/sidebar"; @import "partials/sidebar";
@import "partials/profile"; @import "partials/profile";

Loading…
Cancel
Save