Browse Source

Privacy alpha version

master
morethanwords 3 years ago
parent
commit
f60d0f82ce
  1. 2
      src/components/chat/bubbles.ts
  2. 15
      src/components/radioForm.ts
  3. 83
      src/components/row.ts
  4. 62
      src/components/sidebarLeft/index.ts
  5. 95
      src/components/sidebarLeft/tabs/addMembers.ts
  6. 11
      src/components/sidebarLeft/tabs/chatFolders.ts
  7. 11
      src/components/sidebarLeft/tabs/editFolder.ts
  8. 2
      src/components/sidebarLeft/tabs/editProfile.ts
  9. 93
      src/components/sidebarLeft/tabs/generalSettings.ts
  10. 24
      src/components/sidebarLeft/tabs/newChannel.ts
  11. 64
      src/components/sidebarLeft/tabs/newGroup.ts
  12. 98
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  13. 6
      src/components/sidebarLeft/tabs/settings.ts
  14. 24
      src/components/slider.ts
  15. 4
      src/helpers/dom.ts
  16. 4
      src/lib/appManagers/appDialogsManager.ts
  17. 66
      src/lib/appManagers/appPrivacyManager.ts
  18. 13
      src/scss/partials/_leftSidebar.scss
  19. 33
      src/scss/style.scss

2
src/components/chat/bubbles.ts

@ -970,7 +970,7 @@ export default class ChatBubbles { @@ -970,7 +970,7 @@ export default class ChatBubbles {
});
//if(scrolledDown) this.scrollable.scrollTop = this.scrollable.scrollHeight;
if(this.messagesQueuePromise && scrolledDown) {
if(this.messagesQueuePromise && scrolledDown/* && false */) {
if(this.scrollable.isScrolledDown && !this.scrollable.scrollLocked) {
//this.log('renderNewMessagesByIDs: messagesQueuePromise before will set prev max');
this.scrollable.scrollTo(this.scrollable.scrollHeight - 1, 'top', false, true);

15
src/components/radioForm.ts

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
export default function RadioForm(radios: {container: HTMLElement, input: HTMLInputElement}[], onChange: (value: string) => void) {
const form = document.createElement('form');
radios.forEach(r => {
const {container, input} = r;
form.append(container);
input.addEventListener('change', () => {
if(input.checked) {
onChange(input.value);
}
});
});
return form;
}

83
src/components/row.ts

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
import CheckboxField from "./checkbox";
import RadioField from "./radioField";
import { ripple } from "./ripple";
import { SliderSuperTab } from "./slider";
import RadioForm from "./radioForm";
export default class Row {
public container: HTMLElement;
public title: HTMLDivElement;
public subtitle: HTMLElement;
public checkboxField: ReturnType<typeof CheckboxField>;
public radioField: ReturnType<typeof RadioField>;
constructor(options: Partial<{
icon: string,
subtitle: string,
radioField: Row['radioField'],
checkboxField: Row['checkboxField'],
title: string,
clickable: boolean,
navigationTab: SliderSuperTab
}> = {}) {
this.container = document.createElement('div');
this.container.classList.add('row');
this.subtitle = document.createElement('div');
this.subtitle.classList.add('row-subtitle');
if(options.subtitle) {
this.subtitle.innerHTML = options.subtitle;
}
let havePadding = false;
if(options.radioField || options.checkboxField) {
havePadding = true;
if(options.radioField) {
this.radioField = options.radioField;
this.container.append(this.radioField.label);
}
if(options.checkboxField) {
this.checkboxField = options.checkboxField;
this.container.append(this.checkboxField.label);
}
} else {
if(options.title) {
this.title = document.createElement('div');
this.title.classList.add('row-title');
this.title.innerHTML = options.title;
this.container.append(this.title);
}
if(options.icon) {
havePadding = true;
this.title.classList.add('tgico', 'tgico-' + options.icon);
}
}
if(havePadding) {
this.container.classList.add('row-with-padding');
}
if(options.navigationTab) {
this.container.addEventListener('click', () => {
options.navigationTab.open();
});
options.clickable = true;
}
if(options.clickable) {
this.container.classList.add('row-clickable', 'hover-effect');
ripple(this.container);
}
this.container.append(this.subtitle);
}
}
export const RadioFormFromRows = (rows: Row[], onChange: (value: string) => void) => {
return RadioForm(rows.map(r => ({container: r.container, input: r.radioField.input})), onChange);
};

62
src/components/sidebarLeft/index.ts

@ -29,16 +29,15 @@ import apiManagerProxy from "../../lib/mtproto/mtprotoworker"; @@ -29,16 +29,15 @@ import apiManagerProxy from "../../lib/mtproto/mtprotoworker";
import AppSearchSuper from "../appSearchSuper.";
import { DateData, fillTipDates } from "../../helpers/date";
import AppGeneralSettingsTab from "./tabs/generalSettings";
import AppPrivacyAndSecurityTab from "./tabs/privacyAndSecurity";
const addMembersTab = new AppAddMembersTab();
const contactsTab = new AppContactsTab();
const archivedTab = new AppArchivedTab();
export class AppSidebarLeft extends SidebarSlider {
public static SLIDERITEMSIDS = {
archived: 1,
contacts: 2,
addMembers: 3
contacts: 2
};
private toolsBtn: HTMLButtonElement;
@ -75,6 +74,7 @@ export class AppSidebarLeft extends SidebarSlider { @@ -75,6 +74,7 @@ export class AppSidebarLeft extends SidebarSlider {
public editFolderTab: AppEditFolderTab;
public includedChatsTab: AppIncludedChatsTab;
public generalSettingsTab: AppGeneralSettingsTab;
public privacyAndSecurityTab: AppPrivacyAndSecurityTab;
//private log = logger('SL');
@ -86,8 +86,7 @@ export class AppSidebarLeft extends SidebarSlider { @@ -86,8 +86,7 @@ export class AppSidebarLeft extends SidebarSlider {
Object.assign(this.tabs, {
[AppSidebarLeft.SLIDERITEMSIDS.archived]: archivedTab,
[AppSidebarLeft.SLIDERITEMSIDS.contacts]: contactsTab,
[AppSidebarLeft.SLIDERITEMSIDS.addMembers]: addMembersTab
[AppSidebarLeft.SLIDERITEMSIDS.contacts]: contactsTab
});
//this._selectTab(0); // make first tab as default
@ -101,7 +100,6 @@ export class AppSidebarLeft extends SidebarSlider { @@ -101,7 +100,6 @@ export class AppSidebarLeft extends SidebarSlider {
this.archivedTab = archivedTab;
this.newChannelTab = new AppNewChannelTab(this);
this.addMembersTab = addMembersTab;
this.contactsTab = contactsTab;
this.newGroupTab = new AppNewGroupTab(this);
this.settingsTab = new AppSettingsTab(this);
@ -110,6 +108,8 @@ export class AppSidebarLeft extends SidebarSlider { @@ -110,6 +108,8 @@ export class AppSidebarLeft extends SidebarSlider {
this.includedChatsTab = new AppIncludedChatsTab(this);
this.editProfileTab = new AppEditProfileTab(this);
this.generalSettingsTab = new AppGeneralSettingsTab(this);
this.privacyAndSecurityTab = new AppPrivacyAndSecurityTab(this);
this.addMembersTab = new AppAddMembersTab(this);
this.menuEl = this.toolsBtn.querySelector('.btn-menu');
this.newBtnMenu = this.sidebarEl.querySelector('#new-menu');
@ -146,8 +146,15 @@ export class AppSidebarLeft extends SidebarSlider { @@ -146,8 +146,15 @@ export class AppSidebarLeft extends SidebarSlider {
[this.newButtons.group, this.buttons.newGroup].forEach(btn => {
attachClickEvent(btn, (e) => {
this.addMembersTab.init(0, 'chat', false, (peerIds) => {
this.newGroupTab.init(peerIds);
this.addMembersTab.open({
peerId: 0,
type: 'chat',
skippable: false,
takeOut: (peerIds) => {
this.newGroupTab.open(peerIds);
},
title: 'Add Members',
placeholder: 'Add People...'
});
});
});
@ -429,6 +436,45 @@ export class AppSidebarLeft extends SidebarSlider { @@ -429,6 +436,45 @@ export class AppSidebarLeft extends SidebarSlider {
}
}
export class SettingSection {
public container: HTMLElement;
public content: HTMLElement;
public title: HTMLElement;
public caption: HTMLElement;
constructor(name: string, caption?: string) {
this.container = document.createElement('div');
this.container.classList.add('sidebar-left-section');
const hr = document.createElement('hr');
this.content = document.createElement('div');
this.content.classList.add('sidebar-left-section-content');
if(name) {
this.title = document.createElement('div');
this.title.classList.add('sidebar-left-h2', 'sidebar-left-section-name');
this.title.innerHTML = name;
this.content.append(this.title);
}
this.container.append(hr, this.content);
if(caption) {
this.caption = document.createElement('div');
this.caption.classList.add('sidebar-left-section-caption');
this.caption.innerHTML = caption;
this.container.append(this.caption);
}
}
}
export const generateSection = (appendTo: Scrollable, name: string, caption?: string) => {
const section = new SettingSection(name, caption);
appendTo.append(section.container);
return section.content;
};
const appSidebarLeft = new AppSidebarLeft();
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.appSidebarLeft = appSidebarLeft);
export default appSidebarLeft;

95
src/components/sidebarLeft/tabs/addMembers.ts

@ -1,42 +1,44 @@ @@ -1,42 +1,44 @@
import { SliderTab } from "../../slider";
import SidebarSlider, { SliderSuperTab } from "../../slider";
import AppSelectPeers from "../../appSelectPeers";
import { putPreloader } from "../../misc";
import appChatsManager from "../../../lib/appManagers/appChatsManager";
import appSidebarLeft, { AppSidebarLeft } from "..";
import Button from "../../button";
export default class AppAddMembersTab implements SliderTab {
private container = document.querySelector('.addmembers-container') as HTMLDivElement;
private contentDiv = this.container.querySelector('.sidebar-content') as HTMLDivElement;
private backBtn = this.container.querySelector('.sidebar-close-button') as HTMLButtonElement;
private nextBtn = this.contentDiv.querySelector('.btn-corner') as HTMLButtonElement;
export default class AppAddMembersTab extends SliderSuperTab {
private nextBtn: HTMLButtonElement;
private selector: AppSelectPeers;
private peerType: 'channel' | 'chat';
private peerId: number; // always positive
private takeOut: (peerIds: number[]) => void
private peerType: 'channel' | 'chat' | 'privacy';
private takeOut: (peerIds: number[]) => Promise<any> | any;
private skippable: boolean;
constructor() {
this.nextBtn.addEventListener('click', () => {
if(this.skippable) {
appSidebarLeft.closeTab(AppSidebarLeft.SLIDERITEMSIDS.addMembers);
return;
}
constructor(slider: SidebarSlider) {
super(slider);
}
protected init() {
this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow-next'});
this.content.append(this.nextBtn);
this.nextBtn.addEventListener('click', () => {
const peerIds = this.selector.getSelected();
if(peerIds.length) {
if(this.takeOut) {
this.takeOut(peerIds);
return;
}
this.nextBtn.classList.remove('tgico-arrow-next');
this.nextBtn.disabled = true;
putPreloader(this.nextBtn);
this.selector.freezed = true;
if(this.skippable) {
this.takeOut(peerIds);
this.close();
} else {
const promise = this.takeOut(peerIds);
appChatsManager.inviteToChannel(this.peerId, peerIds).then(() => {
appSidebarLeft.closeTab(AppSidebarLeft.SLIDERITEMSIDS.addMembers);
});
if(promise instanceof Promise) {
this.nextBtn.classList.remove('tgico-arrow-next');
this.nextBtn.disabled = true;
putPreloader(this.nextBtn);
this.selector.freezed = true;
promise.then(() => {
this.close();
});
} else {
this.close();
}
}
});
}
@ -48,22 +50,39 @@ export default class AppAddMembersTab implements SliderTab { @@ -48,22 +50,39 @@ export default class AppAddMembersTab implements SliderTab {
}
}
public init(id: number, type: 'channel' | 'chat', skippable: boolean, takeOut?: AppAddMembersTab['takeOut']) {
this.peerId = Math.abs(id);
this.peerType = type;
this.takeOut = takeOut;
this.skippable = skippable;
public open(options: {
title: string,
placeholder: string,
peerId?: number,
type: AppAddMembersTab['peerType'],
takeOut?: AppAddMembersTab['takeOut'],
skippable: boolean,
selectedPeerIds?: number[]
}) {
const ret = super.open();
this.title.innerHTML = options.title;
this.peerType = options.type;
this.takeOut = options.takeOut;
this.skippable = options.skippable;
this.onCloseAfterTimeout();
this.selector = new AppSelectPeers(this.contentDiv, skippable ? null : (length) => {
this.selector = new AppSelectPeers(this.content, this.skippable ? null : (length) => {
this.nextBtn.classList.toggle('is-visible', !!length);
}, ['contacts']);
this.selector.input.placeholder = options.placeholder;
if(options.selectedPeerIds) {
options.selectedPeerIds.forEach(peerId => {
this.selector.add(peerId);
});
}
this.nextBtn.classList.add('tgico-arrow-next');
this.nextBtn.innerHTML = '';
this.nextBtn.disabled = false;
this.nextBtn.classList.add('tgico-arrow-next');
this.nextBtn.classList.toggle('is-visible', skippable);
this.nextBtn.classList.toggle('is-visible', this.skippable);
appSidebarLeft.selectTab(AppSidebarLeft.SLIDERITEMSIDS.addMembers);
return ret;
}
}

11
src/components/sidebarLeft/tabs/chatFolders.ts

@ -96,7 +96,7 @@ export default class AppChatFoldersTab extends SliderSuperTab { @@ -96,7 +96,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
return div;
}
init() {
protected init() {
this.container.classList.add('chat-folders-container');
this.title.innerText = 'Chat Folders';
@ -232,13 +232,8 @@ export default class AppChatFoldersTab extends SliderSuperTab { @@ -232,13 +232,8 @@ export default class AppChatFoldersTab extends SliderSuperTab {
}
onOpen() {
if(this.init) {
this.init();
this.init = null;
} else {
if(this.animation) {
this.animation.restart();
}
if(this.animation) {
this.animation.restart();
}
}
}

11
src/components/sidebarLeft/tabs/editFolder.ts

@ -41,7 +41,7 @@ export default class AppEditFolderTab extends SliderSuperTab { @@ -41,7 +41,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
super(appSidebarLeft);
}
init() {
protected init() {
this.container.classList.add('edit-folder-container');
this.caption = document.createElement('div');
this.caption.classList.add('caption');
@ -227,13 +227,8 @@ export default class AppEditFolderTab extends SliderSuperTab { @@ -227,13 +227,8 @@ export default class AppEditFolderTab extends SliderSuperTab {
}
onOpen() {
if(this.init) {
this.init();
this.init = null;
} else {
if(this.animation) {
this.animation.restart();
}
if(this.animation) {
this.animation.restart();
}
}

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

@ -44,7 +44,7 @@ export default class AppEditProfileTab extends SliderSuperTab { @@ -44,7 +44,7 @@ export default class AppEditProfileTab extends SliderSuperTab {
super(slider);
}
public init() {
protected init() {
this.container.classList.add('edit-profile-container');
this.title.innerText = 'Edit Profile';
//this.scrollWrapper = this.container.querySelector('.scroll-wrapper');

93
src/components/sidebarLeft/tabs/generalSettings.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import { SliderSuperTab } from "../../slider"
import { AppSidebarLeft } from "..";
import { AppSidebarLeft, generateSection } from "..";
import RangeSelector from "../../rangeSelector";
import { clamp } from "../../../helpers/number";
import Button from "../../button";
@ -8,6 +8,7 @@ import RadioField from "../../radioField"; @@ -8,6 +8,7 @@ import RadioField from "../../radioField";
import appStateManager from "../../../lib/appManagers/appStateManager";
import rootScope from "../../../lib/rootScope";
import { isApple } from "../../../helpers/userAgent";
import Row from "../../row";
export class RangeSettingSelector {
public container: HTMLDivElement;
@ -59,28 +60,10 @@ export default class AppGeneralSettingsTab extends SliderSuperTab { @@ -59,28 +60,10 @@ export default class AppGeneralSettingsTab extends SliderSuperTab {
this.container.classList.add('general-settings-container');
this.title.innerText = 'General';
const generateSection = (name: string) => {
const container = document.createElement('div');
container.classList.add('sidebar-left-section');
const hr = document.createElement('hr');
const h2 = document.createElement('div');
h2.classList.add('sidebar-left-h2', 'sidebar-left-section-name');
h2.innerHTML = name;
const content = document.createElement('div');
content.classList.add('sidebar-left-section-content');
content.append(h2);
container.append(hr, content);
this.scrollable.append(container);
return content;
};
const section = generateSection.bind(null, this.scrollable);
{
const container = generateSection('Settings');
const container = section('Settings');
const range = new RangeSettingSelector('Message Text Size', 1, rootScope.settings.messagesTextSize, 12, 20);
range.onChange = (value) => {
@ -95,67 +78,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTab { @@ -95,67 +78,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTab {
}
{
const container = generateSection('Keyboard');
class Row {
public container: HTMLElement;
public title: HTMLDivElement;
public subtitle: HTMLElement;
public checkboxField: ReturnType<typeof CheckboxField>;
public radioField: ReturnType<typeof RadioField>;
constructor(options: Partial<{
icon: string,
subtitle: string,
radioField: Row['radioField'],
checkboxField: Row['checkboxField'],
title: string,
}> = {}) {
this.container = document.createElement('div');
this.container.classList.add('row');
this.subtitle = document.createElement('div');
this.subtitle.classList.add('row-subtitle');
if(options.subtitle) {
this.subtitle.innerHTML = options.subtitle;
}
let havePadding = false;
if(options.radioField || options.checkboxField) {
havePadding = true;
if(options.radioField) {
this.radioField = options.radioField;
this.container.append(this.radioField.label);
}
if(options.checkboxField) {
this.checkboxField = options.checkboxField;
this.container.append(this.checkboxField.label);
}
} else {
if(options.icon) {
havePadding = true;
this.container.classList.add('tgico-', options.icon);
}
if(options.title) {
this.title = document.createElement('div');
this.title.classList.add('row-title');
this.title.innerHTML = options.title;
this.container.append(this.title);
}
}
if(havePadding) {
this.container.classList.add('row-with-padding');
}
this.container.append(this.subtitle);
}
}
const container = section('Keyboard');
const form = document.createElement('form');
@ -174,7 +97,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTab { @@ -174,7 +97,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTab {
}
{
const container = generateSection('Auto-Download Media');
const container = section('Auto-Download Media');
const contactsCheckboxField = CheckboxField('Contacts', 'contacts', false, 'settings.autoDownload.contacts');
const privateCheckboxField = CheckboxField('Private Chats', 'private', false, 'settings.autoDownload.private');
@ -185,7 +108,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTab { @@ -185,7 +108,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTab {
}
{
const container = generateSection('Auto-Play Media');
const container = section('Auto-Play Media');
const gifsCheckboxField = CheckboxField('GIFs', 'gifs', false, 'settings.autoPlay.gifs');
const videosCheckboxField = CheckboxField('Videos', 'videos', false, 'settings.autoPlay.videos');
@ -194,7 +117,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTab { @@ -194,7 +117,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTab {
}
{
const container = generateSection('Stickers');
const container = section('Stickers');
const suggestCheckboxField = CheckboxField('Suggest Stickers by Emoji', 'suggest', false, 'settings.stickers.suggest');
const loopCheckboxField = CheckboxField('Loop Animated Stickers', 'loop', false, 'settings.stickers.loop');

24
src/components/sidebarLeft/tabs/newChannel.ts

@ -3,12 +3,10 @@ import { InputFile } from "../../../layer"; @@ -3,12 +3,10 @@ import { InputFile } from "../../../layer";
import appChatsManager from "../../../lib/appManagers/appChatsManager";
import Button from "../../button";
import InputField from "../../inputField";
import PopupAvatar from "../../popups/avatar";
import { SliderTab, SliderSuperTab } from "../../slider";
import { SliderSuperTab } from "../../slider";
import AvatarEdit from "../../avatarEdit";
export default class AppNewChannelTab extends SliderSuperTab {
private canvas = this.container.querySelector('.avatar-edit-canvas') as HTMLCanvasElement;
private uploadAvatar: () => Promise<InputFile> = null;
private channelNameInputField: InputField;
@ -20,7 +18,7 @@ export default class AppNewChannelTab extends SliderSuperTab { @@ -20,7 +18,7 @@ export default class AppNewChannelTab extends SliderSuperTab {
super(appSidebarLeft);
}
private init() {
protected init() {
this.container.classList.add('new-channel-container');
this.title.innerText = 'New Channel';
@ -71,7 +69,16 @@ export default class AppNewChannelTab extends SliderSuperTab { @@ -71,7 +69,16 @@ export default class AppNewChannelTab extends SliderSuperTab {
}
appSidebarLeft.removeTabFromHistory(this.id);
appSidebarLeft.addMembersTab.init(channelId, 'channel', true);
appSidebarLeft.addMembersTab.open({
peerId: channelId,
type: 'channel',
skippable: true,
title: 'Add Members',
placeholder: 'Add People...',
takeOut: (peerIds) => {
return appChatsManager.inviteToChannel(Math.abs(channelId), peerIds);
}
});
});
});
@ -86,11 +93,4 @@ export default class AppNewChannelTab extends SliderSuperTab { @@ -86,11 +93,4 @@ export default class AppNewChannelTab extends SliderSuperTab {
this.channelDescriptionInputField.value = '';
this.nextBtn.disabled = false;
}
onOpen() {
if(this.init) {
this.init();
this.init = null;
}
}
}

64
src/components/sidebarLeft/tabs/newGroup.ts

@ -6,13 +6,10 @@ import appUsersManager from "../../../lib/appManagers/appUsersManager"; @@ -6,13 +6,10 @@ import appUsersManager from "../../../lib/appManagers/appUsersManager";
import { SearchGroup } from "../../appSearch";
import Button from "../../button";
import InputField from "../../inputField";
import PopupAvatar from "../../popups/avatar";
import Scrollable from "../../scrollable";
import { SliderSuperTab } from "../../slider";
import AvatarEdit from "../../avatarEdit";
export default class AppNewGroupTab extends SliderSuperTab {
private canvas = this.container.querySelector('.avatar-edit-canvas') as HTMLCanvasElement;
private searchGroup = new SearchGroup(' ', 'contacts', true, 'new-group-members disable-hover', false);
private avatarEdit: AvatarEdit;
private uploadAvatar: () => Promise<InputFile> = null;
@ -24,7 +21,7 @@ export default class AppNewGroupTab extends SliderSuperTab { @@ -24,7 +21,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
super(appSidebarLeft);
}
private construct() {
protected init() {
this.container.classList.add('new-group-container');
this.title.innerText = 'New Group';
@ -73,10 +70,6 @@ export default class AppNewGroupTab extends SliderSuperTab { @@ -73,10 +70,6 @@ export default class AppNewGroupTab extends SliderSuperTab {
this.scrollable.append(this.avatarEdit.container, inputWrapper, chatsContainer);
}
public onClose() {
}
public onCloseAfterTimeout() {
this.searchGroup.clear();
this.avatarEdit.clear();
@ -85,36 +78,35 @@ export default class AppNewGroupTab extends SliderSuperTab { @@ -85,36 +78,35 @@ export default class AppNewGroupTab extends SliderSuperTab {
this.nextBtn.disabled = false;
}
public init(userIds: number[]) {
if(this.construct) {
this.construct();
this.construct = null;
}
this.userIds = userIds;
this.open();
this.userIds.forEach(userId => {
let {dom} = appDialogsManager.addDialogNew({
dialog: userId,
container: this.searchGroup.list,
drawStatus: false,
rippleEnabled: false,
avatarSize: 48
});
public open(userIds: number[]) {
const result = super.open();
result.then(() => {
this.userIds = userIds;
this.userIds.forEach(userId => {
let {dom} = appDialogsManager.addDialogNew({
dialog: userId,
container: this.searchGroup.list,
drawStatus: false,
rippleEnabled: false,
avatarSize: 48
});
let subtitle = '';
subtitle = appUsersManager.getUserStatusString(userId);
if(subtitle == 'online') {
subtitle = `<i>${subtitle}</i>`;
}
let subtitle = '';
subtitle = appUsersManager.getUserStatusString(userId);
if(subtitle == 'online') {
subtitle = `<i>${subtitle}</i>`;
}
if(subtitle) {
dom.lastMessageSpan.innerHTML = subtitle;
}
});
if(subtitle) {
dom.lastMessageSpan.innerHTML = subtitle;
}
this.searchGroup.nameEl.innerText = this.userIds.length + ' members';
this.searchGroup.setActive();
});
this.searchGroup.nameEl.innerText = this.userIds.length + ' members';
this.searchGroup.setActive();
return result;
}
}

98
src/components/sidebarLeft/tabs/privacyAndSecurity.ts

@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
import SidebarSlider, { SliderSuperTab } from "../../slider";
import { generateSection } from "..";
import Row from "../../row";
import { InputPrivacyKey, PrivacyRule } from "../../../layer";
import appPrivacyManager from "../../../lib/appManagers/appPrivacyManager";
import AppPrivacyPhoneNumberTab from "./privacy/phoneNumber";
export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
constructor(slider: SidebarSlider) {
super(slider);
}
protected init() {
this.container.classList.add('privacy-container');
this.title.innerText = 'Privacy and Security';
const section = generateSection.bind(null, this.scrollable);
{
const container = section('');
const blockedUsersRow = new Row({
icon: 'deleteuser',
title: 'Blocked Users',
subtitle: '6 users',
clickable: true
});
const twoFactorRow = new Row({
icon: 'lock',
title: 'Two-Step Verification',
subtitle: 'Off',
clickable: true
});
const activeSessionRow = new Row({
icon: 'activesessions',
title: 'Active Sessions',
subtitle: '3 devices',
clickable: true
});
container.append(blockedUsersRow.container, twoFactorRow.container, activeSessionRow.container);
}
{
const container = section('Privacy');
const rowsByKeys: Partial<{
[key in InputPrivacyKey['_']]: Row
}> = {};
const numberVisibilityRow = rowsByKeys['inputPrivacyKeyPhoneNumber'] = new Row({
title: 'Who can see my phone number?',
subtitle: 'My Contacts',
navigationTab: new AppPrivacyPhoneNumberTab(this.slider)
});
const lastSeenTimeRow = rowsByKeys['inputPrivacyKeyStatusTimestamp'] = new Row({
title: 'Who can see your Last Seen time?',
subtitle: 'Everybody',
clickable: true
});
const photoVisibilityRow = rowsByKeys['inputPrivacyKeyProfilePhoto'] = new Row({
title: 'Who can see my profile photo?',
subtitle: 'Everybody',
clickable: true
});
const linkAccountRow = rowsByKeys['inputPrivacyKeyForwards'] = new Row({
title: 'Who can add a link to my account when forwarding my messages?',
subtitle: 'Everybody',
clickable: true
});
const groupChatsAddRow = rowsByKeys['inputPrivacyKeyChatInvite'] = new Row({
title: 'Who can add me to group chats?',
subtitle: 'Everybody',
clickable: true
});
for(const key in rowsByKeys) {
const row = rowsByKeys[key as keyof typeof rowsByKeys];
appPrivacyManager.getPrivacy(key as keyof typeof rowsByKeys).then(rules => {
const details = appPrivacyManager.getPrivacyRulesDetails(rules);
const type = details.type === 2 ? 'Everybody' : (details.type === 1 ? 'My Contacts' : 'Nobody');
const disallowLength = details.disallowLengths.users + details.disallowLengths.chats;
const allowLength = details.allowLengths.users + details.allowLengths.chats;
const str = type + (disallowLength || allowLength ? ` (${[-disallowLength, allowLength ? '+' + allowLength : 0].filter(Boolean).join(', ')})` : '');
row.subtitle.innerHTML = str;
});
}
container.append(numberVisibilityRow.container, lastSeenTimeRow.container, photoVisibilityRow.container, linkAccountRow.container, groupChatsAddRow.container);
}
}
}

6
src/components/sidebarLeft/tabs/settings.ts

@ -56,7 +56,7 @@ export default class AppSettingsTab extends SliderSuperTab { @@ -56,7 +56,7 @@ export default class AppSettingsTab extends SliderSuperTab {
buttonsDiv.append(this.buttons.folders = Button(className, {icon: 'folder', rippleSquare: true, text: 'Chat Folders'}));
buttonsDiv.append(this.buttons.general = Button(className, {icon: 'settings', rippleSquare: true, text: 'General Settings'}));
buttonsDiv.append(this.buttons.notifications = Button(className + ' btn-disabled', {icon: 'unmute', rippleSquare: true, text: 'Notifications'}));
buttonsDiv.append(this.buttons.privacy = Button(className + ' btn-disabled', {icon: 'lock', rippleSquare: true, text: 'Privacy and Security'}));
buttonsDiv.append(this.buttons.privacy = Button(className, {icon: 'lock', rippleSquare: true, text: 'Privacy and Security'}));
buttonsDiv.append(this.buttons.language = Button(className + ' btn-disabled', {icon: 'language', rippleSquare: true, text: 'Language'}));
this.scrollable.append(this.avatarElem, this.nameDiv, this.phoneDiv, buttonsDiv);
@ -78,6 +78,10 @@ export default class AppSettingsTab extends SliderSuperTab { @@ -78,6 +78,10 @@ export default class AppSettingsTab extends SliderSuperTab {
this.buttons.general.addEventListener('click', () => {
appSidebarLeft.generalSettingsTab.open();
});
this.buttons.privacy.addEventListener('click', () => {
appSidebarLeft.privacyAndSecurityTab.open();
});
}
public fillElements() {

24
src/components/slider.ts

@ -2,7 +2,6 @@ import { attachClickEvent } from "../helpers/dom"; @@ -2,7 +2,6 @@ import { attachClickEvent } from "../helpers/dom";
import { horizontalMenu } from "./horizontalMenu";
import ButtonIcon from "./buttonIcon";
import Scrollable from "./scrollable";
import { p } from "../mock/srp";
export interface SliderTab {
onOpen?: () => void,
@ -23,7 +22,7 @@ export class SliderSuperTab implements SliderTab { @@ -23,7 +22,7 @@ export class SliderSuperTab implements SliderTab {
public id: number;
constructor(protected slider: SidebarSlider) {
constructor(protected slider: SidebarSlider, protected destroyable = false) {
this.container = document.createElement('div');
this.container.classList.add('sidebar-slider-item');
@ -51,20 +50,37 @@ export class SliderSuperTab implements SliderTab { @@ -51,20 +50,37 @@ export class SliderSuperTab implements SliderTab {
return this.slider.closeTab(this.id);
}
public open() {
public async open(...args: any[]) {
if(this.init) {
const result = this.init();
this.init = null;
await (result instanceof Promise ? result : Promise.resolve());
}
return this.slider.selectTab(this);
}
protected init(): Promise<any> | any {
}
// * fix incompability
public onOpen() {
}
/* public onCloseAfterTimeout() {
if(this.destroyable) { // ! WARNING, пока что это будет работать только с самой последней внутренней вкладкой !
delete this.slider.tabs[this.id];
this.container.remove();
}
} */
}
const TRANSITION_TIME = 250;
export default class SidebarSlider {
protected _selectTab: (id: number) => void;
protected _selectTab: ReturnType<typeof horizontalMenu>;
public historyTabIds: number[] = [];
public tabsContainer: HTMLElement;

4
src/helpers/dom.ts

@ -674,7 +674,7 @@ export async function getFilesFromEvent(e: ClipboardEvent | DragEvent, onlyTypes @@ -674,7 +674,7 @@ export async function getFilesFromEvent(e: ClipboardEvent | DragEvent, onlyTypes
return files;
}
export function radiosHandleChange(inputs: HTMLInputElement[], onChange: (value: string) => void) {
/* export function radiosHandleChange(inputs: HTMLInputElement[], onChange: (value: string) => void) {
inputs.forEach(input => {
input.addEventListener('change', () => {
if(input.checked) {
@ -682,7 +682,7 @@ export function radiosHandleChange(inputs: HTMLInputElement[], onChange: (value: @@ -682,7 +682,7 @@ export function radiosHandleChange(inputs: HTMLInputElement[], onChange: (value:
}
});
});
}
} */
export function isSendShortcutPressed(e: KeyboardEvent) {
if(e.key == 'Enter' && !isTouchSupported) {

4
src/lib/appManagers/appDialogsManager.ts

@ -8,7 +8,7 @@ import Scrollable, { ScrollableX, SliceSides } from "../../components/scrollable @@ -8,7 +8,7 @@ import Scrollable, { ScrollableX, SliceSides } from "../../components/scrollable
import appSidebarLeft from "../../components/sidebarLeft";
import { formatDateAccordingToToday } from "../../helpers/date";
import { escapeRegExp } from "../../helpers/string";
import { isApple } from "../../helpers/userAgent";
import { isSafari } from "../../helpers/userAgent";
import { logger, LogLevels } from "../logger";
import { RichTextProcessor } from "../richtextprocessor";
import rootScope from "../rootScope";
@ -745,7 +745,7 @@ export class AppDialogsManager { @@ -745,7 +745,7 @@ export class AppDialogsManager {
const saveLength = 10;
const sliceFromStart = isApple ? [] : children.slice(0, Math.max(0, firstIndex - saveLength));
const sliceFromStart = isSafari ? [] : children.slice(0, Math.max(0, firstIndex - saveLength));
const sliceFromEnd = children.slice(lastIndex + saveLength);
/* if(sliceFromStart.length != sliceFromEnd.length) {

66
src/lib/appManagers/appPrivacyManager.ts

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
import { MOUNT_CLASS_TO } from "../mtproto/mtproto_config";
import { InputPrivacyKey, PrivacyRule } from "../../layer";
import apiManager from "../mtproto/mtprotoworker";
import appChatsManager from "./appChatsManager";
import appUsersManager from "./appUsersManager";
export class AppPrivacyManager {
constructor() {
}
public getPrivacy(inputKey: InputPrivacyKey['_']) {
return apiManager.invokeApi('account.getPrivacy', {
key: {
_: inputKey
}
}).then(privacyRules => {
appUsersManager.saveApiUsers(privacyRules.users);
appChatsManager.saveApiChats(privacyRules.chats);
console.log('privacy rules', inputKey, privacyRules, privacyRules.rules);
return privacyRules.rules;
});
}
public getPrivacyRulesDetails(rules: PrivacyRule[]) {
const types: number[] = [];
let allowLengths = {users: 0, chats: 0}, disallowLengths = {users: 0, chats: 0};
rules.forEach(rule => {
switch(rule._) {
case 'privacyValueAllowAll':
types.push(2);
break;
case 'privacyValueDisallowAll':
types.push(0);
break;
case 'privacyValueAllowContacts':
types.push(1);
break;
/* case 'privacyValueDisallowContacts':
types.push('Except My Contacts');
break; */
case 'privacyValueAllowChatParticipants':
allowLengths.chats += rule.chats.length;
break;
case 'privacyValueAllowUsers':
allowLengths.users += rule.users.length;
break;
case 'privacyValueDisallowChatParticipants':
disallowLengths.chats += rule.chats.length;
break;
case 'privacyValueDisallowUsers':
disallowLengths.users += rule.users.length;
break;
}
});
return {type: types[0], disallowLengths, allowLengths};
}
}
const appPrivacyManager = new AppPrivacyManager();
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.appPrivacyManager = appPrivacyManager);
export default appPrivacyManager;

13
src/scss/partials/_leftSidebar.scss

@ -853,8 +853,8 @@ @@ -853,8 +853,8 @@
&-section {
padding: .5rem 0 1rem;
&-content {
margin: 0 0.125rem;
&-content, &-caption {
margin: 0 .125rem;
@include respond-to(not-handhelds) {
margin: 0 .625rem;
@ -865,6 +865,13 @@ @@ -865,6 +865,13 @@
padding: 1rem .875rem;
}
&-caption {
margin-top: 1rem;
font-size: 0.875rem;
color: #707579;
line-height: 1.2;
}
.btn-primary, .checkbox-field, .radio-field {
margin: 0;
}
@ -884,7 +891,7 @@ @@ -884,7 +891,7 @@
height: 54px;
}
.checkbox-field {
.checkbox-field, &-caption {
padding: 0 .875rem;
}
}

33
src/scss/style.scss

@ -1117,9 +1117,30 @@ middle-ellipsis-element { @@ -1117,9 +1117,30 @@ middle-ellipsis-element {
}
.row {
padding-left: 4.375rem;
min-height: 3.375rem;
margin-top: 1rem;
margin-top: .5rem;
position: relative;
padding: .5rem .75rem;
display: flex;
flex-direction: column;
justify-content: center;
&-with-padding {
padding-left: 4.375rem;
.row-title.tgico:before {
position: absolute;
left: .75rem;
font-size: 1.5rem;
color: #707579;
}
}
&-clickable {
cursor: pointer;
border-radius: $border-radius-medium;
overflow: hidden;
}
.radio-field {
&-main {
@ -1131,6 +1152,12 @@ middle-ellipsis-element { @@ -1131,6 +1152,12 @@ middle-ellipsis-element {
&-subtitle {
color: #707579 !important;
font-size: 14px !important;
line-height: 1.6;
}
}
.hover-effect {
html.no-touch &:hover {
transition: .2s background-color;
background-color: var(--color-gray-hover);
}
}

Loading…
Cancel
Save