Browse Source

Spoilers

master
Eduard Kuzmenko 3 years ago
parent
commit
5f6463b870
  1. 26
      src/components/chat/bubbles.ts
  2. 6
      src/components/chat/input.ts
  3. 2
      src/components/chat/replyContainer.ts
  4. 8
      src/helpers/dom/getRichElementValue.ts
  5. 24
      src/lib/appManagers/appMessagesManager.ts
  6. 150
      src/lib/richtextprocessor.ts
  7. 66
      src/scss/partials/_spoiler.scss
  8. 5
      src/scss/style.scss

26
src/components/chat/bubbles.ts

@ -1048,6 +1048,32 @@ export default class ChatBubbles {
return; return;
} }
const spoiler: HTMLElement = findUpClassName(target, 'spoiler');
if(spoiler) {
const messageDiv = findUpClassName(spoiler, 'message');
const className = 'is-spoiler-visible';
const isVisible = messageDiv.classList.contains(className);
if(!isVisible) {
cancelEvent(e);
}
const duration = 400 / 2;
const showDuration = 5000;
const useRafs = !isVisible ? 1 : 0;
if(useRafs) {
messageDiv.classList.add('will-change');
}
SetTransition(messageDiv, className, true, duration + showDuration, () => {
SetTransition(messageDiv, className, false, duration, () => {
messageDiv.classList.remove('will-change');
});
}, useRafs);
return;
}
const commentsDiv: HTMLElement = findUpClassName(target, 'replies'); const commentsDiv: HTMLElement = findUpClassName(target, 'replies');
if(commentsDiv) { if(commentsDiv) {
const bubbleMid = +bubble.dataset.mid; const bubbleMid = +bubble.dataset.mid;

6
src/components/chat/input.ts

@ -1368,7 +1368,8 @@ export default class ChatInput {
underline: 'Underline', underline: 'Underline',
strikethrough: 'Strikethrough', strikethrough: 'Strikethrough',
monospace: () => document.execCommand('fontName', false, 'monospace'), monospace: () => document.execCommand('fontName', false, 'monospace'),
link: href ? () => document.execCommand('createLink', false, href) : () => document.execCommand('unlink', false, null) link: href ? () => document.execCommand('createLink', false, href) : () => document.execCommand('unlink', false, null),
spoiler: () => document.execCommand('fontName', false, 'spoiler')
}; };
if(!commandsMap[type]) { if(!commandsMap[type]) {
@ -1463,7 +1464,8 @@ export default class ChatInput {
'KeyI': 'italic', 'KeyI': 'italic',
'KeyU': 'underline', 'KeyU': 'underline',
'KeyS': 'strikethrough', 'KeyS': 'strikethrough',
'KeyM': 'monospace' 'KeyM': 'monospace',
'KeyP': 'spoiler'
}; };
if(this.appImManager.markupTooltip) { if(this.appImManager.markupTooltip) {

2
src/components/chat/replyContainer.ts

@ -96,7 +96,7 @@ export function wrapReplyDivAndCaption(options: {
} else { } else {
if(message) { if(message) {
subtitleEl.textContent = ''; subtitleEl.textContent = '';
subtitleEl.append(appMessagesManager.wrapMessageForReply(message, message.message && limitSymbols(message.message, 140))); subtitleEl.append(appMessagesManager.wrapMessageForReply(message));
} else { } else {
if(typeof(subtitle) === 'string') { if(typeof(subtitle) === 'string') {
subtitle = limitSymbols(subtitle, 140); subtitle = limitSymbols(subtitle, 140);

8
src/helpers/dom/getRichElementValue.ts

@ -11,10 +11,10 @@
import { MessageEntity } from "../../layer"; import { MessageEntity } from "../../layer";
export type MarkdownType = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'monospace' | 'link' | 'mentionName'; export type MarkdownType = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'monospace' | 'link' | 'mentionName' | 'spoiler';
export type MarkdownTag = { export type MarkdownTag = {
match: string, match: string,
entityName: 'messageEntityBold' | 'messageEntityUnderline' | 'messageEntityItalic' | 'messageEntityPre' | 'messageEntityStrike' | 'messageEntityTextUrl' | 'messageEntityMentionName'; entityName: Extract<MessageEntity['_'], 'messageEntityBold' | 'messageEntityUnderline' | 'messageEntityItalic' | 'messageEntityPre' | 'messageEntityStrike' | 'messageEntityTextUrl' | 'messageEntityMentionName' | 'messageEntitySpoiler'>;
}; };
// https://core.telegram.org/bots/api#html-style // https://core.telegram.org/bots/api#html-style
@ -46,6 +46,10 @@ export const markdownTags: {[type in MarkdownType]: MarkdownTag} = {
mentionName: { mentionName: {
match: 'A.follow', match: 'A.follow',
entityName: 'messageEntityMentionName' entityName: 'messageEntityMentionName'
},
spoiler: {
match: '[style*="spoiler"]',
entityName: 'messageEntitySpoiler'
} }
}; };

24
src/lib/appManagers/appMessagesManager.ts

@ -2531,12 +2531,12 @@ export class AppMessagesManager {
messageId: mid messageId: mid
}; };
if(isMessage) { /* if(isMessage) {
const entities = message.entities; const entities = message.entities;
if(entities && entities.find(entity => entity._ === 'messageEntitySpoiler')) { if(entities && entities.find(entity => entity._ === 'messageEntitySpoiler')) {
message.media = {_: 'messageMediaUnsupported'}; message.media = {_: 'messageMediaUnsupported'};
} }
} } */
if(isMessage && message.media) { if(isMessage && message.media) {
switch(message.media._) { switch(message.media._) {
@ -2802,6 +2802,7 @@ export class AppMessagesManager {
} }
}; };
let entities = (message as Message.message).totalEntities;
if((message as Message.message).media) { if((message as Message.message).media) {
assumeType<Message.message>(message); assumeType<Message.message>(message);
let usingFullAlbum = true; let usingFullAlbum = true;
@ -2821,7 +2822,9 @@ export class AppMessagesManager {
} }
if(usingFullAlbum) { if(usingFullAlbum) {
text = this.getAlbumText(message.grouped_id).message; const albumText = this.getAlbumText(message.grouped_id);
text = albumText.message;
entities = albumText.totalEntities;
if(!withoutMediaType) { if(!withoutMediaType) {
addPart('AttachAlbum'); addPart('AttachAlbum');
@ -2859,8 +2862,8 @@ export class AppMessagesManager {
addPart('AttachContact'); addPart('AttachContact');
break; break;
case 'messageMediaGame': { case 'messageMediaGame': {
const prefix = '🎮' + ' '; const f = '🎮' + ' ' + media.game.title;
text = prefix + media.game.title; addPart(undefined, plain ? f : RichTextProcessor.wrapEmojiText(f));
break; break;
} }
case 'messageMediaDocument': { case 'messageMediaDocument': {
@ -2924,14 +2927,17 @@ export class AppMessagesManager {
if(text) { if(text) {
text = limitSymbols(text, 100); text = limitSymbols(text, 100);
if(!entities) {
entities = [];
}
if(plain) { if(plain) {
parts.push(text); parts.push(RichTextProcessor.wrapPlainText(text, entities));
} else { } else {
let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' ')); // let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '));
if(highlightWord) { if(highlightWord) {
highlightWord = highlightWord.trim(); highlightWord = highlightWord.trim();
if(!entities) entities = [];
let found = false; let found = false;
let match: any; let match: any;
let regExp = new RegExp(escapeRegExp(highlightWord), 'gi'); let regExp = new RegExp(escapeRegExp(highlightWord), 'gi');
@ -2941,7 +2947,7 @@ export class AppMessagesManager {
} }
if(found) { if(found) {
entities.sort((a, b) => a.offset - b.offset); RichTextProcessor.sortEntities(entities);
} }
} }

150
src/lib/richtextprocessor.ts

@ -18,6 +18,7 @@ import { encodeEntities } from '../helpers/string';
import { IS_SAFARI } from '../environment/userAgent'; import { IS_SAFARI } from '../environment/userAgent';
import { MOUNT_CLASS_TO } from '../config/debug'; import { MOUNT_CLASS_TO } from '../config/debug';
import IS_EMOJI_SUPPORTED from '../environment/emojiSupport'; import IS_EMOJI_SUPPORTED from '../environment/emojiSupport';
import { copy } from '../helpers/object';
const EmojiHelper = { const EmojiHelper = {
emojiMap: (code: string) => { return code; }, emojiMap: (code: string) => { return code; },
@ -82,7 +83,7 @@ const botCommandRegExp = '\\/([a-zA-Z\\d_]{1,32})(?:@(' + usernameRegExp + '))?(
const fullRegExp = new RegExp('(^| )(@)(' + usernameRegExp + ')|(' + urlRegExp + ')|(\\n)|(' + emojiRegExp + ')|(^|[\\s\\(\\]])(#[' + alphaNumericRegExp + ']{2,64})|(^|\\s)' + botCommandRegExp, 'i'); const fullRegExp = new RegExp('(^| )(@)(' + usernameRegExp + ')|(' + urlRegExp + ')|(\\n)|(' + emojiRegExp + ')|(^|[\\s\\(\\]])(#[' + alphaNumericRegExp + ']{2,64})|(^|\\s)' + botCommandRegExp, 'i');
const emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; const emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
//const markdownTestRegExp = /[`_*@~]/; //const markdownTestRegExp = /[`_*@~]/;
const markdownRegExp = /(^|\s|\n)(````?)([\s\S]+?)(````?)([\s\n\.,:?!;]|$)|(^|\s|\x01)(`|~~|\*\*|__|_-_)([^\n]+?)\7([\x01\s\.,:?!;]|$)|@(\d+)\s*\((.+?)\)|(\[(.+?)\]\((.+?)\))/m; const markdownRegExp = /(^|\s|\n)(````?)([\s\S]+?)(````?)([\s\n\.,:?!;]|$)|(^|\s|\x01)(`|~~|\*\*|__|_-_|\|\|)([^\n]+?)\7([\x01\s\.,:?!;]|$)|@(\d+)\s*\((.+?)\)|(\[(.+?)\]\((.+?)\))/m;
const siteHashtags: {[siteName: string]: string} = { const siteHashtags: {[siteName: string]: string} = {
Telegram: 'tg://search_hashtag?hashtag={1}', Telegram: 'tg://search_hashtag?hashtag={1}',
Twitter: 'https://twitter.com/hashtag/{1}', Twitter: 'https://twitter.com/hashtag/{1}',
@ -102,7 +103,8 @@ const markdownEntities: {[markdown: string]: MessageEntity['_']} = {
'**': 'messageEntityBold', '**': 'messageEntityBold',
'__': 'messageEntityItalic', '__': 'messageEntityItalic',
'~~': 'messageEntityStrike', '~~': 'messageEntityStrike',
'_-_': 'messageEntityUnderline' '_-_': 'messageEntityUnderline',
'||': 'messageEntitySpoiler'
}; };
const passConflictingEntities: Set<MessageEntity['_']> = new Set([ const passConflictingEntities: Set<MessageEntity['_']> = new Set([
@ -287,7 +289,7 @@ namespace RichTextProcessor {
const isSOH = match[6] === '\x01'; const isSOH = match[6] === '\x01';
entity = { entity = {
_: markdownEntities[match[7]] as (MessageEntity.messageEntityBold | MessageEntity.messageEntityCode | MessageEntity.messageEntityItalic)['_'], _: markdownEntities[match[7]] as (MessageEntity.messageEntityBold | MessageEntity.messageEntityCode | MessageEntity.messageEntityItalic | MessageEntity.messageEntitySpoiler)['_'],
//offset: matchIndex + match[6].length, //offset: matchIndex + match[6].length,
offset: matchIndex + (isSOH ? 0 : match[6].length), offset: matchIndex + (isSOH ? 0 : match[6].length),
length: text.length length: text.length
@ -407,7 +409,9 @@ namespace RichTextProcessor {
// currentEntities.sort((a, b) => a.offset - b.offset); // currentEntities.sort((a, b) => a.offset - b.offset);
// currentEntities.sort((a, b) => (a.offset - b.offset) || (a._ === 'messageEntityCaret' && -1)); // currentEntities.sort((a, b) => (a.offset - b.offset) || (a._ === 'messageEntityCaret' && -1));
if(!IS_EMOJI_SUPPORTED) { // fix splitted emoji. messageEntityTextUrl can split the emoji if starts before its end (e.g. on fe0f) // * fix splitted emoji. messageEntityTextUrl can split the emoji if starts before its end (e.g. on fe0f)
// * have to fix even if emoji supported since it's being wrapped in span
// if(!IS_EMOJI_SUPPORTED) {
for(let i = 0; i < currentEntities.length; ++i) { for(let i = 0; i < currentEntities.length; ++i) {
const entity = currentEntities[i]; const entity = currentEntities[i];
if(entity._ === 'messageEntityEmoji') { if(entity._ === 'messageEntityEmoji') {
@ -417,7 +421,7 @@ namespace RichTextProcessor {
} }
} }
} }
} // }
return currentEntities; return currentEntities;
} }
@ -466,16 +470,17 @@ namespace RichTextProcessor {
entities: MessageEntity[], entities: MessageEntity[],
contextSite: string, contextSite: string,
highlightUsername: string, highlightUsername: string,
noLinks: true, noLinks: boolean,
noLinebreaks: true, noLinebreaks: boolean,
noCommands: true, noCommands: boolean,
wrappingDraft: boolean, wrappingDraft: boolean,
//mustWrapEmoji: boolean, //mustWrapEmoji: boolean,
fromBot: boolean, fromBot: boolean,
noTextFormat: true, noTextFormat: boolean,
passEntities: Partial<{ passEntities: Partial<{
[_ in MessageEntity['_']]: boolean [_ in MessageEntity['_']]: boolean
}>, }>,
noEncoding: boolean,
contextHashtag?: string, contextHashtag?: string,
}> = {}) { }> = {}) {
@ -495,17 +500,50 @@ namespace RichTextProcessor {
const contextExternal = contextSite !== 'Telegram'; const contextExternal = contextSite !== 'Telegram';
const insertPart = (entity: MessageEntity, startPart: string, endPart?: string/* , priority = 0 */) => { const insertPart = (entity: MessageEntity, startPart: string, endPart?: string/* , priority = 0 */) => {
lol.push({part: startPart, offset: entity.offset/* , priority */}); const startOffset = entity.offset, endOffset = endPart ? entity.offset + entity.length : undefined;
let startIndex: number, endIndex: number, length = lol.length;
for(let i = length - 1; i >= 0; --i) {
const offset = lol[i].offset;
if(startIndex === undefined && startOffset >= offset) {
startIndex = i + 1;
}
if(endOffset !== undefined) {
if(endOffset <= offset) {
endIndex = i;
}
}
if(startOffset > offset && (endOffset === undefined || endOffset < offset)) {
break;
}
}
if(endPart) { startIndex ??= 0;
lol.push({part: endPart, offset: entity.offset + entity.length/* , priority */}); lol.splice(startIndex, 0, {part: startPart, offset: entity.offset/* , priority */});
if(endOffset !== undefined) {
endIndex ??= startIndex;
++endIndex;
lol.splice(endIndex, 0, {part: endPart, offset: entity.offset + entity.length/* , priority */});
} }
}; };
const pushPartsAfterSort: typeof lol = []; const pushPartsAfterSort: typeof lol = [];
const textLength = text.length;
for(let i = 0, length = entities.length; i < length; ++i) { for(let i = 0, length = entities.length; i < length; ++i) {
const entity = entities[i]; let entity = entities[i];
// * check whether text was sliced
// TODO: consider about moving it to other function
if(entity.offset >= textLength) {
continue;
} else if((entity.offset + entity.length) > textLength) {
entity = copy(entity);
entity.length = entity.offset + entity.length - textLength;
}
switch(entity._) { switch(entity._) {
case 'messageEntityBold': { case 'messageEntityBold': {
if(!options.noTextFormat) { if(!options.noTextFormat) {
@ -535,7 +573,7 @@ namespace RichTextProcessor {
if(options.wrappingDraft) { if(options.wrappingDraft) {
const styleName = IS_SAFARI ? 'text-decoration' : 'text-decoration-line'; const styleName = IS_SAFARI ? 'text-decoration' : 'text-decoration-line';
insertPart(entity, `<span style="${styleName}: line-through;">`, '</span>'); insertPart(entity, `<span style="${styleName}: line-through;">`, '</span>');
} else { } else if(!options.noTextFormat) {
insertPart(entity, '<del>', '</del>'); insertPart(entity, '<del>', '</del>');
} }
@ -546,7 +584,7 @@ namespace RichTextProcessor {
if(options.wrappingDraft) { if(options.wrappingDraft) {
const styleName = IS_SAFARI ? 'text-decoration' : 'text-decoration-line'; const styleName = IS_SAFARI ? 'text-decoration' : 'text-decoration-line';
insertPart(entity, `<span style="${styleName}: underline;">`, '</span>'); insertPart(entity, `<span style="${styleName}: underline;">`, '</span>');
} else { } else if(!options.noTextFormat) {
insertPart(entity, '<u>', '</u>'); insertPart(entity, '<u>', '</u>');
} }
@ -556,7 +594,7 @@ namespace RichTextProcessor {
case 'messageEntityCode': { case 'messageEntityCode': {
if(options.wrappingDraft) { if(options.wrappingDraft) {
insertPart(entity, '<span style="font-family: monospace;">', '</span>'); insertPart(entity, '<span style="font-family: monospace;">', '</span>');
} else { } else if(!options.noTextFormat) {
insertPart(entity, '<code>', '</code>'); insertPart(entity, '<code>', '</code>');
} }
@ -732,14 +770,28 @@ namespace RichTextProcessor {
break; break;
} }
case 'messageEntitySpoiler': {
if(options.noTextFormat) {
const before = text.slice(0, entity.offset);
const after = text.slice(entity.offset + entity.length);
text = before + '▚'.repeat(entity.length) + after;
} else if(options.wrappingDraft) {
insertPart(entity, '<span style="font-family: spoiler;">', '</span>');
} else {
insertPart(entity, '<span class="spoiler"><span class="spoiler-text">', '</span></span>');
}
break;
}
} }
} }
// lol.sort((a, b) => (a.offset - b.offset) || (a.priority - b.priority)); // lol.sort((a, b) => (a.offset - b.offset) || (a.priority - b.priority));
lol.sort((a, b) => a.offset - b.offset); // have to sort because of nested entities // lol.sort((a, b) => a.offset - b.offset); // have to sort because of nested entities
let partsLength = lol.length, partsAfterSortLength = pushPartsAfterSort.length; let partsLength = lol.length, pushPartsAfterSortLength = pushPartsAfterSort.length;
for(let i = 0; i < partsAfterSortLength; ++i) { for(let i = 0; i < pushPartsAfterSortLength; ++i) {
const part = pushPartsAfterSort[i]; const part = pushPartsAfterSort[i];
let insertAt = 0; let insertAt = 0;
while(insertAt < partsLength) { while(insertAt < partsLength) {
@ -751,14 +803,15 @@ namespace RichTextProcessor {
lol.splice(insertAt, 0, part); lol.splice(insertAt, 0, part);
} }
partsLength += partsAfterSortLength; partsLength += pushPartsAfterSortLength;
const arr: string[] = []; const arr: string[] = [];
let usedLength = 0; let usedLength = 0;
for(let i = 0; i < partsLength; ++i) { for(let i = 0; i < partsLength; ++i) {
const {part, offset} = lol[i]; const {part, offset} = lol[i];
if(offset > usedLength) { if(offset > usedLength) {
arr.push(encodeEntities(text.slice(usedLength, offset))); const sliced = text.slice(usedLength, offset);
arr.push(options.noEncoding ? sliced : encodeEntities(sliced));
usedLength = offset; usedLength = offset;
} }
@ -766,7 +819,8 @@ namespace RichTextProcessor {
} }
if(usedLength < text.length) { if(usedLength < text.length) {
arr.push(encodeEntities(text.slice(usedLength))); const sliced = text.slice(usedLength);
arr.push(options.noEncoding ? sliced : encodeEntities(sliced));
} }
return arr.join(''); return arr.join('');
@ -834,7 +888,7 @@ namespace RichTextProcessor {
return url; return url;
} }
export function replaceUrlEncodings(urlWithEncoded: string) { /* export function replaceUrlEncodings(urlWithEncoded: string) {
return urlWithEncoded.replace(/(%[A-Z\d]{2})+/g, (str) => { return urlWithEncoded.replace(/(%[A-Z\d]{2})+/g, (str) => {
try { try {
return decodeURIComponent(str); return decodeURIComponent(str);
@ -842,43 +896,23 @@ namespace RichTextProcessor {
return str; return str;
} }
}); });
} } */
export function wrapPlainText(text: string) {
if(IS_EMOJI_SUPPORTED) {
return text;
}
if(!text || !text.length) {
return '';
}
text = text.replace(/\ufe0f/g, ''); /**
var match; * ! This function is still unsafe to use with .innerHTML
var raw = text; */
const arr: string[] = []; export function wrapPlainText(text: string, entities?: MessageEntity[]) {
let emojiTitle; if(entities?.length) {
fullRegExp.lastIndex = 0; entities = entities.filter(entity => entity._ === 'messageEntitySpoiler');
while((match = raw.match(fullRegExp))) {
arr.push(raw.substr(0, match.index))
if(match[8]) {
// @ts-ignore
const emojiCode = EmojiHelper.emojiMap[match[8]];
if(emojiCode &&
// @ts-ignore
(emojiTitle = emojiData[emojiCode][1][0])) {
arr.push(':' + emojiTitle + ':');
} else {
arr.push(match[0]);
}
} else {
arr.push(match[0]);
} }
raw = raw.substr(match.index + match[0].length); return wrapRichText(text, {
} entities,
arr.push(raw); noEncoding: true,
return arr.join(''); noTextFormat: true,
noLinebreaks: true,
noLinks: true
});
} }
export function wrapEmojiText(text: string, isDraft = false) { export function wrapEmojiText(text: string, isDraft = false) {

66
src/scss/partials/_spoiler.scss

@ -0,0 +1,66 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
.spoiler {
--anim: .4s ease;
position: relative;
background-color: var(--spoiler-background-color);
&-text {
opacity: 0;
}
/* &-draft {
background-color: var(--spoiler-draft-background-color);
} */
}
[style*="spoiler"] {
background-color: var(--spoiler-draft-background-color);
font-family: inherit !important;
}
.message {
&.will-change {
.spoiler {
// box-shadow: 0 0 var(--spoiler-background-color);
&-text {
filter: blur(6px);
}
}
}
&.is-spoiler-visible {
&.animating {
.spoiler {
transition: /* box-shadow var(--anim), */ background-color var(--anim);
&-text {
transition: opacity var(--anim), filter var(--anim);
}
}
}
&:not(.backwards) {
.spoiler {
background-color: transparent;
// box-shadow: 0 0 30px 30px transparent;
&-text {
filter: blur(0);
opacity: 1;
}
}
}
&.backwards {
.spoiler-text {
filter: blur(3px);
}
}
}
}

5
src/scss/style.scss

@ -175,6 +175,8 @@ $chat-input-inner-padding-handhelds: .25rem;
--link-color: #00488f; --link-color: #00488f;
--ripple-color: rgba(0, 0, 0, .08); --ripple-color: rgba(0, 0, 0, .08);
--poll-circle-color: var(--border-color); --poll-circle-color: var(--border-color);
--spoiler-background-color: #e3e5e8;
--spoiler-draft-background-color: #d9d9d9;
--message-background-color: var(--surface-color); --message-background-color: var(--surface-color);
--message-checkbox-color: #61c642; --message-checkbox-color: #61c642;
@ -241,6 +243,8 @@ $chat-input-inner-padding-handhelds: .25rem;
--link-color: var(--primary-color); --link-color: var(--primary-color);
--ripple-color: rgba(255, 255, 255, .08); --ripple-color: rgba(255, 255, 255, .08);
--poll-circle-color: #fff; --poll-circle-color: #fff;
--spoiler-background-color: #373e4e;
--spoiler-draft-background-color: #484848;
--message-background-color: var(--surface-color); --message-background-color: var(--surface-color);
--message-checkbox-color: var(--primary-color); --message-checkbox-color: var(--primary-color);
@ -300,6 +304,7 @@ $chat-input-inner-padding-handhelds: .25rem;
@import "partials/colorPicker"; @import "partials/colorPicker";
@import "partials/replyKeyboard"; @import "partials/replyKeyboard";
@import "partials/peopleNearby"; @import "partials/peopleNearby";
@import "partials/spoiler";
@import "partials/popups/popup"; @import "partials/popups/popup";
@import "partials/popups/editAvatar"; @import "partials/popups/editAvatar";

Loading…
Cancel
Save