Privacy alpha version
This commit is contained in:
parent
521ec5690d
commit
f60d0f82ce
@ -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
Normal file
15
src/components/radioForm.ts
Normal file
@ -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
Normal file
83
src/components/row.ts
Normal file
@ -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);
|
||||
};
|
@ -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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
||||
[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 {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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() {
|
||||
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', () => {
|
||||
if(this.skippable) {
|
||||
appSidebarLeft.closeTab(AppSidebarLeft.SLIDERITEMSIDS.addMembers);
|
||||
return;
|
||||
}
|
||||
|
||||
const peerIds = this.selector.getSelected();
|
||||
if(peerIds.length) {
|
||||
if(this.takeOut) {
|
||||
this.takeOut(peerIds);
|
||||
return;
|
||||
|
||||
if(this.skippable) {
|
||||
this.takeOut(peerIds);
|
||||
this.close();
|
||||
} else {
|
||||
const promise = this.takeOut(peerIds);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
this.nextBtn.classList.remove('tgico-arrow-next');
|
||||
this.nextBtn.disabled = true;
|
||||
putPreloader(this.nextBtn);
|
||||
this.selector.freezed = true;
|
||||
|
||||
appChatsManager.inviteToChannel(this.peerId, peerIds).then(() => {
|
||||
appSidebarLeft.closeTab(AppSidebarLeft.SLIDERITEMSIDS.addMembers);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
if(this.init) {
|
||||
this.init();
|
||||
this.init = null;
|
||||
} else {
|
||||
if(this.animation) {
|
||||
this.animation.restart();
|
||||
}
|
||||
if(this.animation) {
|
||||
this.animation.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
if(this.init) {
|
||||
this.init();
|
||||
this.init = null;
|
||||
} else {
|
||||
if(this.animation) {
|
||||
this.animation.restart();
|
||||
}
|
||||
if(this.animation) {
|
||||
this.animation.restart();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
|
@ -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";
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
{
|
||||
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 {
|
||||
}
|
||||
|
||||
{
|
||||
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 {
|
||||
}
|
||||
|
||||
{
|
||||
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 {
|
||||
}
|
||||
|
||||
{
|
||||
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');
|
||||
|
@ -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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
this.channelDescriptionInputField.value = '';
|
||||
this.nextBtn.disabled = false;
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
if(this.init) {
|
||||
this.init();
|
||||
this.init = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
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 {
|
||||
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 {
|
||||
this.nextBtn.disabled = false;
|
||||
}
|
||||
|
||||
public init(userIds: number[]) {
|
||||
if(this.construct) {
|
||||
this.construct();
|
||||
this.construct = null;
|
||||
}
|
||||
public open(userIds: number[]) {
|
||||
const result = super.open();
|
||||
result.then(() => {
|
||||
this.userIds = userIds;
|
||||
|
||||
this.userIds = userIds;
|
||||
this.userIds.forEach(userId => {
|
||||
let {dom} = appDialogsManager.addDialogNew({
|
||||
dialog: userId,
|
||||
container: this.searchGroup.list,
|
||||
drawStatus: false,
|
||||
rippleEnabled: false,
|
||||
avatarSize: 48
|
||||
});
|
||||
|
||||
this.open();
|
||||
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>`;
|
||||
}
|
||||
|
||||
if(subtitle) {
|
||||
dom.lastMessageSpan.innerHTML = subtitle;
|
||||
}
|
||||
});
|
||||
|
||||
let subtitle = '';
|
||||
subtitle = appUsersManager.getUserStatusString(userId);
|
||||
if(subtitle == 'online') {
|
||||
subtitle = `<i>${subtitle}</i>`;
|
||||
}
|
||||
|
||||
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
Normal file
98
src/components/sidebarLeft/tabs/privacyAndSecurity.ts
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
this.buttons.general.addEventListener('click', () => {
|
||||
appSidebarLeft.generalSettingsTab.open();
|
||||
});
|
||||
|
||||
this.buttons.privacy.addEventListener('click', () => {
|
||||
appSidebarLeft.privacyAndSecurityTab.open();
|
||||
});
|
||||
}
|
||||
|
||||
public fillElements() {
|
||||
|
@ -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 {
|
||||
|
||||
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 {
|
||||
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;
|
||||
|
||||
|
@ -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:
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} */
|
||||
|
||||
export function isSendShortcutPressed(e: KeyboardEvent) {
|
||||
if(e.key == 'Enter' && !isTouchSupported) {
|
||||
|
@ -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 {
|
||||
|
||||
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
Normal file
66
src/lib/appManagers/appPrivacyManager.ts
Normal file
@ -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;
|
@ -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 @@
|
||||
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 @@
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
.checkbox-field {
|
||||
.checkbox-field, &-caption {
|
||||
padding: 0 .875rem;
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
&-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…
x
Reference in New Issue
Block a user