Spoilers
This commit is contained in:
parent
1d285ef3f6
commit
5f6463b870
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(endPart) {
|
if(startIndex === undefined && startOffset >= offset) {
|
||||||
lol.push({part: endPart, offset: entity.offset + entity.length/* , priority */});
|
startIndex = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(endOffset !== undefined) {
|
||||||
|
if(endOffset <= offset) {
|
||||||
|
endIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(startOffset > offset && (endOffset === undefined || endOffset < offset)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex ??= 0;
|
||||||
|
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) {
|
* ! This function is still unsafe to use with .innerHTML
|
||||||
return text;
|
*/
|
||||||
|
export function wrapPlainText(text: string, entities?: MessageEntity[]) {
|
||||||
|
if(entities?.length) {
|
||||||
|
entities = entities.filter(entity => entity._ === 'messageEntitySpoiler');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!text || !text.length) {
|
return wrapRichText(text, {
|
||||||
return '';
|
entities,
|
||||||
}
|
noEncoding: true,
|
||||||
|
noTextFormat: true,
|
||||||
text = text.replace(/\ufe0f/g, '');
|
noLinebreaks: true,
|
||||||
var match;
|
noLinks: true
|
||||||
var raw = text;
|
});
|
||||||
const arr: string[] = [];
|
|
||||||
let emojiTitle;
|
|
||||||
fullRegExp.lastIndex = 0;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
arr.push(raw);
|
|
||||||
return arr.join('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapEmojiText(text: string, isDraft = false) {
|
export function wrapEmojiText(text: string, isDraft = false) {
|
||||||
|
66
src/scss/partials/_spoiler.scss
Normal file
66
src/scss/partials/_spoiler.scss
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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…
x
Reference in New Issue
Block a user