morethanwords
4 years ago
19 changed files with 498 additions and 212 deletions
@ -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; |
||||||
|
} |
@ -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); |
||||||
|
}; |
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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; |
Loading…
Reference in new issue