Browse Source

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
master
Eduard Kuzmenko 4 years ago
parent
commit
e3ce8c1906
  1. 3
      src/components/chat/contextMenu.ts
  2. 72
      src/components/chat/input.ts
  3. 44
      src/components/chat/selection.ts
  4. 3
      src/components/inputField.ts
  5. 14
      src/components/sidebarLeft/tabs/editProfile.ts
  6. 26
      src/components/singleTransition.ts
  7. 9
      src/lib/appManagers/appImManager.ts
  8. 2
      src/scss/partials/_chatBubble.scss

3
src/components/chat/contextMenu.ts

@ -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;

72
src/components/chat/input.ts

@ -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) { }
this.lastUrl = url;
this.willSendWebPage = null;
apiManager.invokeApi('messages.getWebPage', {
url: url,
hash: 0
}).then((webpage) => {
webpage = appWebPagesManager.saveWebPage(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 || ''); //console.log('messageInput url:', url);
if(this.lastUrl != url) {
this.lastUrl = url;
this.willSendWebPage = null;
apiManager.invokeApi('messages.getWebPage', {
url: url,
hash: 0
}).then((webpage) => {
webpage = appWebPagesManager.saveWebPage(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;
}
});
}
delete this.noWebPage; break;
this.willSendWebPage = webpage; }
} } else if(this.lastUrl) {
}); 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;

44
src/components/chat/selection.ts

@ -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;
} }

3
src/components/inputField.ts

@ -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);

14
src/components/sidebarLeft/tabs/editProfile.ts

@ -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

@ -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;

9
src/lib/appManagers/appImManager.ts

@ -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);
} }

2
src/scss/partials/_chatBubble.scss

@ -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…
Cancel
Save