Fix highlighting when selecting
Fix webpage in input for messageEntityTextUrl Fix webpage clearing Fix length in input field due to emoji length Fix pointer-events for sticky date
This commit is contained in:
parent
3cc7d874bf
commit
e3ce8c1906
@ -35,6 +35,9 @@ export default class ChatContextMenu {
|
|||||||
bubble = bubbleContainer ? bubbleContainer.parentElement : findUpClassName(e.target, 'bubble');
|
bubble = bubbleContainer ? bubbleContainer.parentElement : findUpClassName(e.target, 'bubble');
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
|
|
||||||
|
// ! context menu click by date bubble (there is no pointer-events)
|
||||||
|
if(!bubble) return;
|
||||||
|
|
||||||
if(e instanceof MouseEvent) e.preventDefault();
|
if(e instanceof MouseEvent) e.preventDefault();
|
||||||
if(this.element.classList.contains('active')) {
|
if(this.element.classList.contains('active')) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -22,6 +22,7 @@ import Scrollable from "../scrollable";
|
|||||||
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 } from '../../layer';
|
||||||
|
|
||||||
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.';
|
||||||
@ -195,35 +196,56 @@ export class ChatInput {
|
|||||||
const value = this.messageInput.innerText;
|
const value = this.messageInput.innerText;
|
||||||
|
|
||||||
const entities = RichTextProcessor.parseEntities(value);
|
const entities = RichTextProcessor.parseEntities(value);
|
||||||
//console.log('messageInput entities', entities);
|
console.log('messageInput entities', entities);
|
||||||
|
|
||||||
const entityUrl = entities.find(e => e._ == 'messageEntityUrl');
|
const urlEntities = entities.filter(e => e._ == 'messageEntityUrl');
|
||||||
if(entityUrl) { // need to get webpage
|
if(urlEntities.length) {
|
||||||
const url = value.slice(entityUrl.offset, entityUrl.offset + entityUrl.length);
|
const richEntities: MessageEntity[] = [];
|
||||||
|
const richValue = RichTextProcessor.parseMarkdown(getRichValue(this.messageInput), richEntities);
|
||||||
|
console.log('messageInput url', entities, richEntities);
|
||||||
|
for(const entity of urlEntities) {
|
||||||
|
const url = value.slice(entity.offset, entity.offset + entity.length);
|
||||||
|
|
||||||
//console.log('messageInput url:', url);
|
if(!(url.includes('http://') || url.includes('https://')) && !richEntities.find(e => e._ == 'messageEntityTextUrl')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(this.lastUrl != url) {
|
//console.log('messageInput url:', url);
|
||||||
this.lastUrl = url;
|
|
||||||
this.willSendWebPage = null;
|
if(this.lastUrl != url) {
|
||||||
apiManager.invokeApi('messages.getWebPage', {
|
this.lastUrl = url;
|
||||||
url: url,
|
this.willSendWebPage = null;
|
||||||
hash: 0
|
apiManager.invokeApi('messages.getWebPage', {
|
||||||
}).then((webpage) => {
|
url: url,
|
||||||
webpage = appWebPagesManager.saveWebPage(webpage);
|
hash: 0
|
||||||
if(webpage._ == 'webPage') {
|
}).then((webpage) => {
|
||||||
if(this.lastUrl != url) return;
|
webpage = appWebPagesManager.saveWebPage(webpage);
|
||||||
//console.log('got webpage: ', webpage);
|
if(webpage._ == 'webPage') {
|
||||||
|
if(this.lastUrl != url) return;
|
||||||
|
//console.log('got webpage: ', webpage);
|
||||||
|
|
||||||
|
this.setTopInfo('webpage', () => {}, webpage.site_name || webpage.title || 'Webpage', webpage.description || webpage.url || '');
|
||||||
|
|
||||||
|
delete this.noWebPage;
|
||||||
|
this.willSendWebPage = webpage;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.setTopInfo('webpage', () => {}, webpage.site_name || webpage.title || 'Webpage', webpage.description || webpage.url || '');
|
break;
|
||||||
|
}
|
||||||
delete this.noWebPage;
|
} else if(this.lastUrl) {
|
||||||
this.willSendWebPage = webpage;
|
this.lastUrl = '';
|
||||||
}
|
delete this.noWebPage;
|
||||||
});
|
this.willSendWebPage = null;
|
||||||
|
|
||||||
|
if(this.helperType) {
|
||||||
|
this.helperFunc();
|
||||||
|
} else {
|
||||||
|
this.clearHelper();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!value.trim() && !serializeNodes(Array.from(this.messageInput.childNodes)).trim()) {
|
if(!value.trim() && !serializeNodes(Array.from(this.messageInput.childNodes)).trim()) {
|
||||||
this.messageInput.innerHTML = '';
|
this.messageInput.innerHTML = '';
|
||||||
|
|
||||||
@ -481,7 +503,9 @@ export class ChatInput {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.replyElements.cancelBtn.addEventListener('click', () => {
|
this.replyElements.cancelBtn.addEventListener('click', (e) => {
|
||||||
|
cancelEvent(e);
|
||||||
|
|
||||||
if(this.willSendWebPage) {
|
if(this.willSendWebPage) {
|
||||||
this.noWebPage = true;
|
this.noWebPage = true;
|
||||||
this.willSendWebPage = null;
|
this.willSendWebPage = null;
|
||||||
|
@ -8,31 +8,7 @@ import CheckboxField from "../checkbox";
|
|||||||
import PopupDeleteMessages from "../popupDeleteMessages";
|
import PopupDeleteMessages from "../popupDeleteMessages";
|
||||||
import PopupForward from "../popupForward";
|
import PopupForward from "../popupForward";
|
||||||
import { toast } from "../toast";
|
import { toast } from "../toast";
|
||||||
|
import SetTransition from "../singleTransition";
|
||||||
const SetTransition = (element: HTMLElement, className: string, forwards: boolean, duration: number, onTransitionEnd?: () => void) => {
|
|
||||||
const timeout = element.dataset.timeout;
|
|
||||||
if(timeout !== undefined) {
|
|
||||||
clearTimeout(+timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(forwards) {
|
|
||||||
element.classList.add(className);
|
|
||||||
}
|
|
||||||
|
|
||||||
element.classList.add('animating');
|
|
||||||
|
|
||||||
element.classList.toggle('backwards', !forwards);
|
|
||||||
element.dataset.timeout = '' + setTimeout(() => {
|
|
||||||
delete element.dataset.timeout;
|
|
||||||
if(!forwards) {
|
|
||||||
element.classList.remove('backwards', className);
|
|
||||||
}
|
|
||||||
|
|
||||||
element.classList.remove('animating');
|
|
||||||
|
|
||||||
onTransitionEnd && onTransitionEnd();
|
|
||||||
}, duration);
|
|
||||||
};
|
|
||||||
|
|
||||||
const MAX_SELECTION_LENGTH = 100;
|
const MAX_SELECTION_LENGTH = 100;
|
||||||
//const MIN_CLICK_MOVE = 32; // minimum bubble height
|
//const MIN_CLICK_MOVE = 32; // minimum bubble height
|
||||||
@ -61,7 +37,16 @@ export default class ChatSelection {
|
|||||||
|
|
||||||
bubblesContainer.addEventListener('mousedown', (e) => {
|
bubblesContainer.addEventListener('mousedown', (e) => {
|
||||||
//console.log('selection mousedown', e);
|
//console.log('selection mousedown', e);
|
||||||
if(e.button != 0 || (!this.selectedMids.size && !(e.target as HTMLElement).classList.contains('bubble'))) { // LEFT BUTTON
|
const bubble = findUpClassName(e.target, 'bubble');
|
||||||
|
// LEFT BUTTON
|
||||||
|
// проверка внизу нужна для того, чтобы не активировать селект если target потомок .bubble
|
||||||
|
if(e.button != 0
|
||||||
|
|| (
|
||||||
|
!this.selectedMids.size
|
||||||
|
&& !(e.target as HTMLElement).classList.contains('bubble')
|
||||||
|
&& bubble
|
||||||
|
)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +65,12 @@ export default class ChatSelection {
|
|||||||
} */
|
} */
|
||||||
|
|
||||||
//const foundTargets: Map<HTMLElement, true> = new Map();
|
//const foundTargets: Map<HTMLElement, true> = new Map();
|
||||||
|
let canceledSelection = false;
|
||||||
const onMouseMove = (e: MouseEvent) => {
|
const onMouseMove = (e: MouseEvent) => {
|
||||||
|
if(!canceledSelection) {
|
||||||
|
cancelSelection();
|
||||||
|
canceledSelection = true;
|
||||||
|
}
|
||||||
/* if(!good) {
|
/* if(!good) {
|
||||||
if(Math.abs(e.x - x) > MIN_CLICK_MOVE || Math.abs(e.y - y) > MIN_CLICK_MOVE) {
|
if(Math.abs(e.x - x) > MIN_CLICK_MOVE || Math.abs(e.y - y) > MIN_CLICK_MOVE) {
|
||||||
good = true;
|
good = true;
|
||||||
@ -93,7 +83,7 @@ export default class ChatSelection {
|
|||||||
foundTargets.set(e.target as HTMLElement, true); */
|
foundTargets.set(e.target as HTMLElement, true); */
|
||||||
const bubble = findUpClassName(e.target, 'bubble');
|
const bubble = findUpClassName(e.target, 'bubble');
|
||||||
if(!bubble) {
|
if(!bubble) {
|
||||||
console.error('found no bubble', e);
|
//console.error('found no bubble', e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,8 @@ const InputField = (options: {
|
|||||||
|
|
||||||
processInput = () => {
|
processInput = () => {
|
||||||
const wasError = input.classList.contains('error');
|
const wasError = input.classList.contains('error');
|
||||||
const inputLength = plainText ? input.value.length : getRichValue(input).length;
|
// * https://stackoverflow.com/a/54369605 #2 to count emoji as 1 symbol
|
||||||
|
const inputLength = plainText ? input.value.length : [...getRichValue(input)].length;
|
||||||
const diff = maxLength - inputLength;
|
const diff = maxLength - inputLength;
|
||||||
const isError = diff < 0;
|
const isError = diff < 0;
|
||||||
input.classList.toggle('error', isError);
|
input.classList.toggle('error', isError);
|
||||||
|
@ -204,24 +204,24 @@ export default class AppEditProfileTab implements SliderTab {
|
|||||||
Object.assign(this.originalValues, {
|
Object.assign(this.originalValues, {
|
||||||
firstName: user.first_name,
|
firstName: user.first_name,
|
||||||
lastName: user.last_name,
|
lastName: user.last_name,
|
||||||
userName: user.username
|
userName: user.username,
|
||||||
|
bio: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
this.firstNameInput.innerHTML = user.rFirstName;
|
this.firstNameInput.innerHTML = user.rFirstName;
|
||||||
this.lastNameInput.innerHTML = RichTextProcessor.wrapRichText(user.last_name, {noLinks: true, noLinebreaks: true});
|
this.lastNameInput.innerHTML = RichTextProcessor.wrapRichText(user.last_name, {noLinks: true, noLinebreaks: true});
|
||||||
|
this.bioInput.innerHTML = '';
|
||||||
this.userNameInput.value = this.originalValues.userName = user.username ?? '';
|
this.userNameInput.value = this.originalValues.userName = user.username ?? '';
|
||||||
|
|
||||||
this.firstNameInput.classList.remove('error');
|
|
||||||
this.lastNameInput.classList.remove('error');
|
|
||||||
this.bioInput.classList.remove('error');
|
|
||||||
|
|
||||||
this.userNameInput.classList.remove('valid', 'error');
|
this.userNameInput.classList.remove('valid', 'error');
|
||||||
this.userNameInput.nextElementSibling.innerHTML = 'Username (optional)';
|
this.userNameInput.nextElementSibling.innerHTML = 'Username (optional)';
|
||||||
|
|
||||||
appProfileManager.getProfile(user.id, true).then(userFull => {
|
appProfileManager.getProfile(user.id, true).then(userFull => {
|
||||||
if(userFull.rAbout) {
|
if(userFull.about) {
|
||||||
this.originalValues.bio = userFull.about;
|
this.originalValues.bio = userFull.about;
|
||||||
this.bioInput.innerHTML = userFull.rAbout;
|
this.bioInput.innerHTML = userFull.rAbout;
|
||||||
|
|
||||||
|
this.handleChange();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -233,6 +233,7 @@ export default class AppEditProfileTab implements SliderTab {
|
|||||||
this.uploadAvatar = null;
|
this.uploadAvatar = null;
|
||||||
|
|
||||||
this.setProfileUrl();
|
this.setProfileUrl();
|
||||||
|
this.handleChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
public isUsernameValid(username: string) {
|
public isUsernameValid(username: string) {
|
||||||
@ -268,5 +269,6 @@ export default class AppEditProfileTab implements SliderTab {
|
|||||||
|
|
||||||
onCloseAfterTimeout() {
|
onCloseAfterTimeout() {
|
||||||
this.nextBtn.classList.remove('is-visible');
|
this.nextBtn.classList.remove('is-visible');
|
||||||
|
this.firstNameInput.innerHTML = this.lastNameInput.innerHTML = this.bioInput.innerHTML = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
26
src/components/singleTransition.ts
Normal file
26
src/components/singleTransition.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const SetTransition = (element: HTMLElement, className: string, forwards: boolean, duration: number, onTransitionEnd?: () => void) => {
|
||||||
|
const timeout = element.dataset.timeout;
|
||||||
|
if(timeout !== undefined) {
|
||||||
|
clearTimeout(+timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(forwards) {
|
||||||
|
element.classList.add(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
element.classList.add('animating');
|
||||||
|
|
||||||
|
element.classList.toggle('backwards', !forwards);
|
||||||
|
element.dataset.timeout = '' + setTimeout(() => {
|
||||||
|
delete element.dataset.timeout;
|
||||||
|
if(!forwards) {
|
||||||
|
element.classList.remove('backwards', className);
|
||||||
|
}
|
||||||
|
|
||||||
|
element.classList.remove('animating');
|
||||||
|
|
||||||
|
onTransitionEnd && onTransitionEnd();
|
||||||
|
}, duration);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SetTransition;
|
@ -1515,16 +1515,17 @@ export class AppImManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public highlightBubble(element: HTMLElement) {
|
public highlightBubble(element: HTMLElement) {
|
||||||
if(element.dataset.timeout) {
|
const datasetKey = 'highlightTimeout';
|
||||||
clearTimeout(+element.dataset.timeout);
|
if(element.dataset[datasetKey]) {
|
||||||
|
clearTimeout(+element.dataset[datasetKey]);
|
||||||
element.classList.remove('is-highlighted');
|
element.classList.remove('is-highlighted');
|
||||||
void element.offsetWidth; // reflow
|
void element.offsetWidth; // reflow
|
||||||
}
|
}
|
||||||
|
|
||||||
element.classList.add('is-highlighted');
|
element.classList.add('is-highlighted');
|
||||||
element.dataset.timeout = '' + setTimeout(() => {
|
element.dataset[datasetKey] = '' + setTimeout(() => {
|
||||||
element.classList.remove('is-highlighted');
|
element.classList.remove('is-highlighted');
|
||||||
delete element.dataset.timeout;
|
delete element.dataset[datasetKey];
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,6 +153,7 @@ $bubble-margin: .25rem;
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
transition: opacity .3s ease;
|
transition: opacity .3s ease;
|
||||||
opacity: 0.99999; // for safari
|
opacity: 0.99999; // for safari
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
&.is-sticky {
|
&.is-sticky {
|
||||||
opacity: 0.00001; // for safari
|
opacity: 0.00001; // for safari
|
||||||
@ -172,6 +173,7 @@ $bubble-margin: .25rem;
|
|||||||
|
|
||||||
.bubble__container {
|
.bubble__container {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user