Browse Source

Top bar menu:

Add contact
Block/unblock

Fix Firefox scrollbar
Fix selecting text in country input
master
morethanwords 3 years ago
parent
commit
306478b7d5
  1. 52
      src/components/chat/topbar.ts
  2. 18
      src/components/inputField.ts
  3. 49
      src/components/sidebarRight/tabs/editContact.ts
  4. 8
      src/helpers/dom/selectElementContents.ts
  5. 9
      src/lang.ts
  6. 7
      src/lib/appManagers/appPeersManager.ts
  7. 19
      src/lib/appManagers/appProfileManager.ts
  8. 2
      src/lib/appManagers/appUsersManager.ts
  9. 4
      src/scss/partials/_scrollable.scss

52
src/components/chat/topbar.ts

@ -35,13 +35,14 @@ import blurActiveElement from "../../helpers/dom/blurActiveElement";
import { cancelEvent } from "../../helpers/dom/cancelEvent"; import { cancelEvent } from "../../helpers/dom/cancelEvent";
import { attachClickEvent } from "../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../helpers/dom/clickEvent";
import findUpTag from "../../helpers/dom/findUpTag"; import findUpTag from "../../helpers/dom/findUpTag";
import { toast } from "../toast"; import { toast, toastNew } from "../toast";
import replaceContent from "../../helpers/dom/replaceContent"; import replaceContent from "../../helpers/dom/replaceContent";
import { ChatFull } from "../../layer"; import { ChatFull } from "../../layer";
import PopupPickUser from "../popups/pickUser"; import PopupPickUser from "../popups/pickUser";
import PopupPeer from "../popups/peer"; import PopupPeer from "../popups/peer";
import generateVerifiedIcon from "../generateVerifiedIcon"; import generateVerifiedIcon from "../generateVerifiedIcon";
import { fastRaf } from "../../helpers/schedulers"; import { fastRaf } from "../../helpers/schedulers";
import AppEditContactTab from "../sidebarRight/tabs/editContact";
export default class ChatTopbar { export default class ChatTopbar {
public container: HTMLDivElement; public container: HTMLDivElement;
@ -273,6 +274,15 @@ export default class ChatTopbar {
this.chat.selection.cancelSelection(); this.chat.selection.cancelSelection();
}, },
verify: () => this.chat.selection.isSelecting verify: () => this.chat.selection.isSelecting
}, {
icon: 'adduser',
text: 'AddContact',
onClick: () => {
const tab = new AppEditContactTab(this.appSidebarRight);
tab.peerId = this.peerId;
tab.open();
},
verify: () => this.peerId > 0 && !this.appUsersManager.isContact(this.peerId)
}, { }, {
icon: 'forward', icon: 'forward',
text: 'ShareContact', text: 'ShareContact',
@ -312,6 +322,46 @@ export default class ChatTopbar {
}); });
}, },
verify: () => rootScope.myId !== this.peerId && this.peerId > 0 && this.appUsersManager.isContact(this.peerId) verify: () => rootScope.myId !== this.peerId && this.peerId > 0 && this.appUsersManager.isContact(this.peerId)
}, {
icon: 'lock',
text: 'BlockUser',
onClick: () => {
new PopupPeer('', {
peerId: this.peerId,
titleLangKey: 'BlockUser',
descriptionLangKey: 'AreYouSureBlockContact2',
descriptionLangArgs: [new PeerTitle({peerId: this.peerId}).element],
buttons: [{
langKey: 'BlockUser',
isDanger: true,
callback: () => {
this.appUsersManager.toggleBlock(this.peerId, true).then(value => {
if(value) {
toastNew({langPackKey: 'UserBlocked'});
}
});
}
}]
}).show();
},
verify: () => {
const userFull = this.appProfileManager.usersFull[this.peerId];
return this.peerId > 0 && userFull && !userFull.pFlags?.blocked;
}
}, {
icon: 'lockoff',
text: 'Unblock',
onClick: () => {
this.appUsersManager.toggleBlock(this.peerId, false).then(value => {
if(value) {
toastNew({langPackKey: 'UserUnblocked'});
}
});
},
verify: () => {
const userFull = this.appProfileManager.usersFull[this.peerId];
return this.peerId > 0 && !!userFull?.pFlags?.blocked;
}
}, { }, {
icon: 'delete danger', icon: 'delete danger',
text: 'Delete', text: 'Delete',

18
src/components/inputField.ts

@ -8,6 +8,7 @@ import simulateEvent from "../helpers/dom/dispatchEvent";
import findUpAttribute from "../helpers/dom/findUpAttribute"; import findUpAttribute from "../helpers/dom/findUpAttribute";
import getRichValue from "../helpers/dom/getRichValue"; import getRichValue from "../helpers/dom/getRichValue";
import isInputEmpty from "../helpers/dom/isInputEmpty"; import isInputEmpty from "../helpers/dom/isInputEmpty";
import selectElementContents from "../helpers/dom/selectElementContents";
import { i18n, LangPackKey, _i18n } from "../lib/langPack"; import { i18n, LangPackKey, _i18n } from "../lib/langPack";
import RichTextProcessor from "../lib/richtextprocessor"; import RichTextProcessor from "../lib/richtextprocessor";
@ -215,8 +216,14 @@ class InputField {
} }
public select() { public select() {
if((this.input as HTMLInputElement).value) { // * avoid selecting whole empty field on iOS devices if(!this.value) { // * avoid selecting whole empty field on iOS devices
return;
}
if(this.options.plainText) {
(this.input as HTMLInputElement).select(); // * select text (this.input as HTMLInputElement).select(); // * select text
} else {
selectElementContents(this.input);
} }
} }
@ -278,9 +285,7 @@ class InputField {
(!this.required || !isInputEmpty(this.input)); (!this.required || !isInputEmpty(this.input));
} }
public setOriginalValue(value: InputField['originalValue'] = '', silent = false) { public setDraftValue(value = '', silent = false) {
this.originalValue = value;
if(!this.options.plainText) { if(!this.options.plainText) {
value = RichTextProcessor.wrapDraftText(value); value = RichTextProcessor.wrapDraftText(value);
} }
@ -292,6 +297,11 @@ class InputField {
} }
} }
public setOriginalValue(value: InputField['originalValue'] = '', silent = false) {
this.originalValue = value;
this.setDraftValue(value, silent);
}
public setState(state: InputState, label?: LangPackKey) { public setState(state: InputState, label?: LangPackKey) {
if(label) { if(label) {
this.label.textContent = ''; this.label.textContent = '';

49
src/components/sidebarRight/tabs/editContact.ts

@ -31,7 +31,8 @@ export default class AppEditContactTab extends SliderSuperTab {
protected init() { protected init() {
this.container.classList.add('edit-peer-container', 'edit-contact-container'); this.container.classList.add('edit-peer-container', 'edit-contact-container');
this.setTitle(this.peerId ? 'Edit' : 'AddContactTitle'); const isNew = !appUsersManager.isContact(this.peerId);
this.setTitle(isNew ? 'AddContactTitle' : 'Edit');
{ {
const section = new SettingSection({noDelimiter: true}); const section = new SettingSection({noDelimiter: true});
@ -41,12 +42,13 @@ export default class AppEditContactTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
this.nameInputField = new InputField({ this.nameInputField = new InputField({
label: 'EditProfile.FirstNameLabel', label: 'FirstName',
name: 'contact-name', name: 'contact-name',
maxLength: 70 maxLength: 70,
required: true
}); });
this.lastNameInputField = new InputField({ this.lastNameInputField = new InputField({
label: 'Login.Register.LastName.Placeholder', label: 'LastName',
name: 'contact-lastname', name: 'contact-lastname',
maxLength: 70 maxLength: 70
}); });
@ -54,9 +56,14 @@ export default class AppEditContactTab extends SliderSuperTab {
if(this.peerId) { if(this.peerId) {
const user = appUsersManager.getUser(this.peerId); const user = appUsersManager.getUser(this.peerId);
if(isNew) {
this.nameInputField.setDraftValue(user.first_name);
this.lastNameInputField.setDraftValue(user.last_name);
} else {
this.nameInputField.setOriginalValue(user.first_name); this.nameInputField.setOriginalValue(user.first_name);
this.lastNameInputField.setOriginalValue(user.last_name); this.lastNameInputField.setOriginalValue(user.last_name);
} }
}
inputWrapper.append(this.nameInputField.container, this.lastNameInputField.container); inputWrapper.append(this.nameInputField.container, this.lastNameInputField.container);
inputFields.push(this.nameInputField, this.lastNameInputField); inputFields.push(this.nameInputField, this.lastNameInputField);
@ -97,13 +104,6 @@ export default class AppEditContactTab extends SliderSuperTab {
} }
}); });
const notificationsRow = new Row({
checkboxField: notificationsCheckboxField
});
const enabled = !appNotificationsManager.isPeerLocalMuted(this.peerId, false);
notificationsCheckboxField.checked = enabled;
const profileNameDiv = document.createElement('div'); const profileNameDiv = document.createElement('div');
profileNameDiv.classList.add('profile-name'); profileNameDiv.classList.add('profile-name');
profileNameDiv.append(new PeerTitle({ profileNameDiv.append(new PeerTitle({
@ -115,7 +115,30 @@ export default class AppEditContactTab extends SliderSuperTab {
profileSubtitleDiv.classList.add('profile-subtitle'); profileSubtitleDiv.classList.add('profile-subtitle');
profileSubtitleDiv.append(i18n('EditContact.OriginalName')); profileSubtitleDiv.append(i18n('EditContact.OriginalName'));
section.content.append(div, profileNameDiv, profileSubtitleDiv, inputWrapper, notificationsRow.container); section.content.append(div, profileNameDiv, profileSubtitleDiv, inputWrapper);
if(!isNew) {
const notificationsRow = new Row({
checkboxField: notificationsCheckboxField
});
const enabled = !appNotificationsManager.isPeerLocalMuted(this.peerId, false);
notificationsCheckboxField.checked = enabled;
section.content.append(notificationsRow.container);
} else {
const user = appUsersManager.getUser(this.peerId);
const phoneRow = new Row({
icon: 'phone',
titleLangKey: user.phone ? undefined : 'MobileHidden',
title: user.phone ? appUsersManager.formatUserPhone(user.phone) : undefined,
subtitleLangKey: user.phone ? 'Phone' : 'MobileHiddenExceptionInfo',
subtitleLangArgs: user.phone ? undefined : [new PeerTitle({peerId: this.peerId}).element]
});
section.content.append(phoneRow.container);
}
} else { } else {
section.content.append(inputWrapper); section.content.append(inputWrapper);
} }
@ -133,7 +156,7 @@ export default class AppEditContactTab extends SliderSuperTab {
}, {listenerSetter: this.listenerSetter}); }, {listenerSetter: this.listenerSetter});
} }
if(this.peerId) { if(!isNew) {
const section = new SettingSection({ const section = new SettingSection({
}); });

8
src/helpers/dom/selectElementContents.ts

@ -0,0 +1,8 @@
// https://stackoverflow.com/a/6150060
export default function selectElementContents(el: HTMLElement) {
const range = document.createRange();
range.selectNodeContents(el);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}

9
src/lang.ts

@ -575,6 +575,15 @@ const lang = {
"DiscardVoiceMessageTitle": "Discard Voice Message", "DiscardVoiceMessageTitle": "Discard Voice Message",
"DiscardVoiceMessageDescription": "Are you sure you want to stop recording and discard your voice message?", "DiscardVoiceMessageDescription": "Are you sure you want to stop recording and discard your voice message?",
"DiscardVoiceMessageAction": "Discard", "DiscardVoiceMessageAction": "Discard",
"AddContact": "Add to contacts",
"BlockUser": "Block user",
"MobileHidden": "Mobile hidden",
"MobileHiddenExceptionInfo": "Phone number will be visible once %1$s adds you as a contact.",
"FirstName": "First name (required)",
"LastName": "Last name (optional)",
"AreYouSureBlockContact2": "Are you sure you want to block **%1$s**?",
"UserBlocked": "User blocked",
"UserUnblocked": "User unblocked",
// * macos // * macos
"AccountSettings.Filters": "Chat Folders", "AccountSettings.Filters": "Chat Folders",

7
src/lib/appManagers/appPeersManager.ts

@ -37,13 +37,6 @@ const DialogColorsMap = [0, 7, 4, 1, 6, 3, 5];
export type PeerType = 'channel' | 'chat' | 'megagroup' | 'group' | 'saved'; export type PeerType = 'channel' | 'chat' | 'megagroup' | 'group' | 'saved';
export class AppPeersManager { export class AppPeersManager {
constructor() {
rootScope.addMultipleEventsListeners({
updatePeerBlocked: (update) => {
rootScope.dispatchEvent('peer_block', {peerId: this.getPeerId(update.peer_id), blocked: update.blocked});
}
});
}
/* public savePeerInstance(peerId: number, instance: any) { /* public savePeerInstance(peerId: number, instance: any) {
if(peerId < 0) appChatsManager.saveApiChat(instance); if(peerId < 0) appChatsManager.saveApiChat(instance);
else appUsersManager.saveApiUser(instance); else appUsersManager.saveApiUser(instance);

19
src/lib/appManagers/appProfileManager.ts

@ -31,7 +31,7 @@ export type UserTyping = Partial<{userId: number, action: SendMessageAction, tim
export class AppProfileManager { export class AppProfileManager {
//private botInfos: any = {}; //private botInfos: any = {};
private usersFull: {[id: string]: UserFull.userFull} = {}; public usersFull: {[id: string]: UserFull.userFull} = {};
public chatsFull: {[id: string]: ChatFull} = {}; public chatsFull: {[id: string]: ChatFull} = {};
private fullPromises: {[peerId: string]: Promise<ChatFull.chatFull | ChatFull.channelFull | UserFull>} = {}; private fullPromises: {[peerId: string]: Promise<ChatFull.chatFull | ChatFull.channelFull | UserFull>} = {};
@ -94,7 +94,9 @@ export class AppProfileManager {
updateUserTyping: this.onUpdateUserTyping, updateUserTyping: this.onUpdateUserTyping,
updateChatUserTyping: this.onUpdateUserTyping, updateChatUserTyping: this.onUpdateUserTyping,
updateChannelUserTyping: this.onUpdateUserTyping updateChannelUserTyping: this.onUpdateUserTyping,
updatePeerBlocked: this.onUpdatePeerBlocked
}); });
rootScope.addEventListener('chat_update', (chatId) => { rootScope.addEventListener('chat_update', (chatId) => {
@ -639,6 +641,19 @@ export class AppProfileManager {
} }
}; };
private onUpdatePeerBlocked = (update: Update.updatePeerBlocked) => {
const peerId = appPeersManager.getPeerId(update.peer_id);
if(peerId > 0) {
const userFull = this.usersFull[peerId];
if(userFull) {
if(update.blocked) userFull.pFlags.blocked = true;
else delete userFull.pFlags.blocked;
}
}
rootScope.dispatchEvent('peer_block', {peerId, blocked: update.blocked});
};
public getPeerTypings(peerId: number) { public getPeerTypings(peerId: number) {
return this.typingsInPeer[peerId]; return this.typingsInPeer[peerId];
} }

2
src/lib/appManagers/appUsersManager.ts

@ -31,8 +31,6 @@ import appChatsManager from "./appChatsManager";
import appPeersManager from "./appPeersManager"; import appPeersManager from "./appPeersManager";
import appStateManager from "./appStateManager"; import appStateManager from "./appStateManager";
// TODO: updateUserBlocked
export type User = MTUser.user; export type User = MTUser.user;
export type TopPeerType = 'correspondents' | 'bots_inline'; export type TopPeerType = 'correspondents' | 'bots_inline';
export type MyTopPeer = {id: number, rating: number}; export type MyTopPeer = {id: number, rating: number};

4
src/scss/partials/_scrollable.scss

@ -80,14 +80,14 @@ html:not(.is-safari):not(.is-ios) {
&.scrollable-x { &.scrollable-x {
overflow-x: auto; overflow-x: auto;
scrollbar-width: none; scrollbar-width: thin; // Firefox only
-ms-overflow-style: none; -ms-overflow-style: none;
} }
&.scrollable-y { &.scrollable-y {
overflow-y: auto; overflow-y: auto;
overflow-y: overlay; overflow-y: overlay;
scrollbar-width: none; scrollbar-width: thin; // Firefox only
-ms-overflow-style: none; -ms-overflow-style: none;
/* html.is-safari & { /* html.is-safari & {

Loading…
Cancel
Save