Browse Source

Privacy updates

master
morethanwords 4 years ago
parent
commit
b8f98e76c2
  1. 4
      src/components/privacySection.ts
  2. 7
      src/components/scrollable.ts
  3. 21
      src/components/sidebarLeft/tabs/activeSessions.ts
  4. 27
      src/components/sidebarLeft/tabs/blockedUsers.ts
  5. 90
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  6. 53
      src/lib/appManagers/appPrivacyManager.ts
  7. 6
      src/lib/rootScope.ts

4
src/components/privacySection.ts

@ -122,9 +122,9 @@ export default class PrivacySection {
}); });
} }
setTimeout(() => { /* setTimeout(() => {
this.setRadio(PrivacyType.Contacts); this.setRadio(PrivacyType.Contacts);
}, 0); }, 0); */
const promise = appPrivacyManager.getPrivacy(options.inputKey).then(rules => { const promise = appPrivacyManager.getPrivacy(options.inputKey).then(rules => {
const details = appPrivacyManager.getPrivacyRulesDetails(rules); const details = appPrivacyManager.getPrivacyRulesDetails(rules);

7
src/components/scrollable.ts

@ -177,7 +177,12 @@ export default class Scrollable extends ScrollableBase {
}; };
public checkForTriggers = () => { public checkForTriggers = () => {
if((!this.onScrolledTop && !this.onScrolledBottom) || this.isHeavyAnimationInProgress) return; if((!this.onScrolledTop && !this.onScrolledBottom)) return;
if(this.isHeavyAnimationInProgress) {
this.onScroll();
return;
}
const scrollHeight = this.container.scrollHeight; const scrollHeight = this.container.scrollHeight;
if(!scrollHeight) { // незачем вызывать триггеры если блок пустой или не виден if(!scrollHeight) { // незачем вызывать триггеры если блок пустой или не виден

21
src/components/sidebarLeft/tabs/activeSessions.ts

@ -10,8 +10,10 @@ import ButtonMenu from "../../buttonMenu";
import PopupConfirmAction from "../../popups/confirmAction"; import PopupConfirmAction from "../../popups/confirmAction";
import apiManager from "../../../lib/mtproto/mtprotoworker"; import apiManager from "../../../lib/mtproto/mtprotoworker";
import { toast } from "../../toast"; import { toast } from "../../toast";
import AppPrivacyAndSecurityTab from "./privacyAndSecurity";
export default class AppActiveSessionsTab extends SliderSuperTab { export default class AppActiveSessionsTab extends SliderSuperTab {
public privacyTab: AppPrivacyAndSecurityTab;
public authorizations: Authorization.authorization[]; public authorizations: Authorization.authorization[];
private menuElement: HTMLElement; private menuElement: HTMLElement;
@ -57,11 +59,15 @@ export default class AppActiveSessionsTab extends SliderSuperTab {
text: 'TERMINATE', text: 'TERMINATE',
isDanger: true, isDanger: true,
callback: () => { callback: () => {
toggleDisability([btnTerminate], true); const b = [btnTerminate];
toggleDisability(b, true);
apiManager.invokeApi('auth.resetAuthorizations').then(value => { apiManager.invokeApi('auth.resetAuthorizations').then(value => {
//toggleDisability([btnTerminate], false); //toggleDisability([btnTerminate], false);
btnTerminate.remove(); btnTerminate.remove();
otherSection.container.remove(); otherSection.container.remove();
this.privacyTab.updateActiveSessions();
}, onError).finally(() => {
toggleDisability(b, false);
}); });
} }
}], { }], {
@ -90,6 +96,12 @@ export default class AppActiveSessionsTab extends SliderSuperTab {
this.scrollable.append(otherSection.container); this.scrollable.append(otherSection.container);
const onError = (err: any) => {
if(err.type === 'FRESH_RESET_AUTHORISATION_FORBIDDEN') {
toast('For security reasons, you can\'t terminate older sessions from a device that you\'ve just connected. Please use an earlier connection or wait for a few hours.');
}
};
let target: HTMLElement; let target: HTMLElement;
const onTerminateClick = () => { const onTerminateClick = () => {
const hash = target.dataset.hash; const hash = target.dataset.hash;
@ -102,12 +114,9 @@ export default class AppActiveSessionsTab extends SliderSuperTab {
.then(value => { .then(value => {
if(value) { if(value) {
target.remove(); target.remove();
this.privacyTab.updateActiveSessions();
} }
}, (err) => { }, onError);
if(err.type === 'FRESH_RESET_AUTHORISATION_FORBIDDEN') {
toast('For security reasons, you can\'t terminate older sessions from a device that you\'ve just connected. Please use an earlier connection or wait for a few hours.');
}
});
} }
}], { }], {
title: 'Terminate Session', title: 'Terminate Session',

27
src/components/sidebarLeft/tabs/blockedUsers.ts

@ -103,6 +103,33 @@ export default class AppBlockedUsersTab extends SliderSuperTab {
} }
} }
}); });
const LOAD_COUNT = 50;
let loading = false;
this.scrollable.onScrolledBottom = () => {
if(loading) {
return;
}
loading = true;
appUsersManager.getBlocked(list.childElementCount, LOAD_COUNT).then(res => {
for(const peerId of res.peerIds) {
add(peerId, true);
}
if(res.peerIds.length < LOAD_COUNT) {
this.scrollable.onScrolledBottom = null;
}
this.scrollable.checkForTriggers();
}).finally(() => {
loading = false;
});
};
}
onOpenAfterTimeout() {
this.scrollable.onScroll();
} }
onCloseAfterTimeout() { onCloseAfterTimeout() {

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

@ -1,7 +1,7 @@
import { SliderSuperTab } from "../../slider"; import { SliderSuperTab } from "../../slider";
import { generateSection, SettingSection } from ".."; import { generateSection, SettingSection } from "..";
import Row from "../../row"; import Row from "../../row";
import { AccountPassword, Authorization, InputPrivacyKey, PrivacyRule } from "../../../layer"; import { AccountPassword, Authorization, InputPrivacyKey } from "../../../layer";
import appPrivacyManager, { PrivacyType } from "../../../lib/appManagers/appPrivacyManager"; import appPrivacyManager, { PrivacyType } from "../../../lib/appManagers/appPrivacyManager";
import AppPrivacyPhoneNumberTab from "./privacy/phoneNumber"; import AppPrivacyPhoneNumberTab from "./privacy/phoneNumber";
import AppTwoStepVerificationTab from "./2fa"; import AppTwoStepVerificationTab from "./2fa";
@ -17,14 +17,20 @@ import AppActiveSessionsTab from "./activeSessions";
import apiManager from "../../../lib/mtproto/mtprotoworker"; import apiManager from "../../../lib/mtproto/mtprotoworker";
import AppBlockedUsersTab from "./blockedUsers"; import AppBlockedUsersTab from "./blockedUsers";
import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager";
import rootScope from "../../../lib/rootScope";
export default class AppPrivacyAndSecurityTab extends SliderSuperTab { export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
private activeSessionsRow: Row;
private authorizations: Authorization.authorization[];
protected init() { protected init() {
this.container.classList.add('privacy-container'); this.container.classList.add('privacy-container');
this.title.innerText = 'Privacy and Security'; this.title.innerText = 'Privacy and Security';
const section = generateSection.bind(null, this.scrollable); const section = generateSection.bind(null, this.scrollable);
const SUBTITLE = 'Loading...';
{ {
const section = new SettingSection({noDelimiter: true}); const section = new SettingSection({noDelimiter: true});
@ -32,7 +38,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const blockedUsersRow = new Row({ const blockedUsersRow = new Row({
icon: 'deleteuser', icon: 'deleteuser',
title: 'Blocked Users', title: 'Blocked Users',
subtitle: 'Loading...', subtitle: SUBTITLE,
clickable: () => { clickable: () => {
const tab = new AppBlockedUsersTab(this.slider); const tab = new AppBlockedUsersTab(this.slider);
tab.peerIds = blockedPeerIds; tab.peerIds = blockedPeerIds;
@ -45,7 +51,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const twoFactorRowOptions = { const twoFactorRowOptions = {
icon: 'lock', icon: 'lock',
title: 'Two-Step Verification', title: 'Two-Step Verification',
subtitle: 'Loading...', subtitle: SUBTITLE,
clickable: (e: Event) => { clickable: (e: Event) => {
let tab: AppTwoStepVerificationTab | AppTwoStepVerificationEnterPasswordTab | AppTwoStepVerificationEmailConfirmationTab; let tab: AppTwoStepVerificationTab | AppTwoStepVerificationEnterPasswordTab | AppTwoStepVerificationEmailConfirmationTab;
if(passwordState.pFlags.has_password) { if(passwordState.pFlags.has_password) {
@ -68,24 +74,39 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const twoFactorRow = new Row(twoFactorRowOptions); const twoFactorRow = new Row(twoFactorRowOptions);
twoFactorRow.freezed = true; twoFactorRow.freezed = true;
const activeSessionRow = new Row({ const activeSessionsRow = this.activeSessionsRow = new Row({
icon: 'activesessions', icon: 'activesessions',
title: 'Active Sessions', title: 'Active Sessions',
subtitle: 'Loading...', subtitle: SUBTITLE,
clickable: () => { clickable: () => {
const tab = new AppActiveSessionsTab(this.slider); const tab = new AppActiveSessionsTab(this.slider);
tab.authorizations = authorizations; tab.privacyTab = this;
tab.authorizations = this.authorizations;
tab.open(); tab.open();
} }
}); });
activeSessionRow.freezed = true; activeSessionsRow.freezed = true;
section.content.append(blockedUsersRow.container, twoFactorRow.container, activeSessionRow.container); section.content.append(blockedUsersRow.container, twoFactorRow.container, activeSessionsRow.container);
this.scrollable.append(section.container); this.scrollable.append(section.container);
let blockedCount: number;
const setBlockedCount = (count: number) => {
blockedCount = count;
blockedUsersRow.subtitle.innerText = count + ' ' + (count !== 1 ? 'users' : 'user');
};
this.listenerSetter.add(rootScope, 'peer_block', (update) => {
const {blocked, peerId} = update;
if(!blocked) blockedPeerIds.findAndSplice(p => p === peerId);
else blockedPeerIds.unshift(peerId);
blockedCount += blocked ? 1 : -1;
setBlockedCount(blockedCount);
});
appUsersManager.getBlocked().then(res => { appUsersManager.getBlocked().then(res => {
blockedUsersRow.freezed = false; blockedUsersRow.freezed = false;
blockedUsersRow.subtitle.innerText = res.count + ' ' + (res.count !== 1 ? 'users' : 'user'); setBlockedCount(res.count);
blockedPeerIds = res.peerIds; blockedPeerIds = res.peerIds;
}); });
@ -97,13 +118,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
//console.log('password state', state); //console.log('password state', state);
}); });
let authorizations: Authorization.authorization[]; this.updateActiveSessions();
apiManager.invokeApi('account.getAuthorizations').then(auths => {
activeSessionRow.freezed = false;
authorizations = auths.authorizations;
activeSessionRow.subtitle.innerText = authorizations.length + ' ' + (authorizations.length !== 1 ? 'devices' : 'device');
console.log('auths', auths);
});
} }
{ {
@ -117,7 +132,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const numberVisibilityRow = rowsByKeys['inputPrivacyKeyPhoneNumber'] = new Row({ const numberVisibilityRow = rowsByKeys['inputPrivacyKeyPhoneNumber'] = new Row({
title: 'Who can see my phone number?', title: 'Who can see my phone number?',
subtitle: 'My Contacts', subtitle: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyPhoneNumberTab(this.slider).open() new AppPrivacyPhoneNumberTab(this.slider).open()
} }
@ -125,7 +140,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const lastSeenTimeRow = rowsByKeys['inputPrivacyKeyStatusTimestamp'] = new Row({ const lastSeenTimeRow = rowsByKeys['inputPrivacyKeyStatusTimestamp'] = new Row({
title: 'Who can see your Last Seen time?', title: 'Who can see your Last Seen time?',
subtitle: 'Everybody', subtitle: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyLastSeenTab(this.slider).open() new AppPrivacyLastSeenTab(this.slider).open()
} }
@ -133,7 +148,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const photoVisibilityRow = rowsByKeys['inputPrivacyKeyProfilePhoto'] = new Row({ const photoVisibilityRow = rowsByKeys['inputPrivacyKeyProfilePhoto'] = new Row({
title: 'Who can see my profile photo?', title: 'Who can see my profile photo?',
subtitle: 'Everybody', subtitle: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyProfilePhotoTab(this.slider).open(); new AppPrivacyProfilePhotoTab(this.slider).open();
} }
@ -141,7 +156,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const callRow = rowsByKeys['inputPrivacyKeyPhoneCall'] = new Row({ const callRow = rowsByKeys['inputPrivacyKeyPhoneCall'] = new Row({
title: 'Who can call me?', title: 'Who can call me?',
subtitle: 'Everybody', subtitle: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyCallsTab(this.slider).open(); new AppPrivacyCallsTab(this.slider).open();
} }
@ -149,7 +164,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const linkAccountRow = rowsByKeys['inputPrivacyKeyForwards'] = new Row({ const linkAccountRow = rowsByKeys['inputPrivacyKeyForwards'] = new Row({
title: 'Who can add a link to my account when forwarding my messages?', title: 'Who can add a link to my account when forwarding my messages?',
subtitle: 'Everybody', subtitle: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyForwardMessagesTab(this.slider).open(); new AppPrivacyForwardMessagesTab(this.slider).open();
} }
@ -157,15 +172,19 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const groupChatsAddRow = rowsByKeys['inputPrivacyKeyChatInvite'] = new Row({ const groupChatsAddRow = rowsByKeys['inputPrivacyKeyChatInvite'] = new Row({
title: 'Who can add me to group chats?', title: 'Who can add me to group chats?',
subtitle: 'Everybody', subtitle: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyAddToGroupsTab(this.slider).open(); new AppPrivacyAddToGroupsTab(this.slider).open();
} }
}); });
for(const key in rowsByKeys) { const updatePrivacyRow = (key: InputPrivacyKey['_']) => {
const row = rowsByKeys[key as keyof typeof rowsByKeys]; const row = rowsByKeys[key];
appPrivacyManager.getPrivacy(key as keyof typeof rowsByKeys).then(rules => { if(!row) {
return;
}
appPrivacyManager.getPrivacy(key).then(rules => {
const details = appPrivacyManager.getPrivacyRulesDetails(rules); const details = appPrivacyManager.getPrivacyRulesDetails(rules);
const type = details.type === PrivacyType.Everybody ? 'Everybody' : (details.type === PrivacyType.Contacts ? 'My Contacts' : 'Nobody'); const type = details.type === PrivacyType.Everybody ? 'Everybody' : (details.type === PrivacyType.Contacts ? 'My Contacts' : 'Nobody');
const disallowLength = details.disallowPeers.users.length + details.disallowPeers.chats.length; const disallowLength = details.disallowPeers.users.length + details.disallowPeers.chats.length;
@ -173,9 +192,30 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const str = type + (disallowLength || allowLength ? ` (${[-disallowLength, allowLength ? '+' + allowLength : 0].filter(Boolean).join(', ')})` : ''); const str = type + (disallowLength || allowLength ? ` (${[-disallowLength, allowLength ? '+' + allowLength : 0].filter(Boolean).join(', ')})` : '');
row.subtitle.innerHTML = str; row.subtitle.innerHTML = str;
}); });
};
for(const key in rowsByKeys) {
updatePrivacyRow(key as keyof typeof rowsByKeys);
} }
rootScope.on('privacy_update', (update) => {
let key: string = update.key._;
key = key[0].toUpperCase() + key.slice(1);
key = 'input' + key;
updatePrivacyRow(key as any);
});
container.append(numberVisibilityRow.container, lastSeenTimeRow.container, photoVisibilityRow.container, callRow.container, linkAccountRow.container, groupChatsAddRow.container); container.append(numberVisibilityRow.container, lastSeenTimeRow.container, photoVisibilityRow.container, callRow.container, linkAccountRow.container, groupChatsAddRow.container);
} }
} }
public updateActiveSessions() {
apiManager.invokeApi('account.getAuthorizations').then(auths => {
this.activeSessionsRow.freezed = false;
this.authorizations = auths.authorizations;
this.activeSessionsRow.subtitle.innerText = this.authorizations.length + ' ' + (this.authorizations.length !== 1 ? 'devices' : 'device');
//console.log('auths', auths);
});
}
} }

53
src/lib/appManagers/appPrivacyManager.ts

@ -1,8 +1,10 @@
import { MOUNT_CLASS_TO } from "../../config/debug"; import { MOUNT_CLASS_TO } from "../../config/debug";
import { InputPrivacyKey, InputPrivacyRule, PrivacyRule } from "../../layer"; import { InputPrivacyKey, InputPrivacyRule, PrivacyRule, Update, PrivacyKey } from "../../layer";
import apiManager from "../mtproto/mtprotoworker"; import apiManager from "../mtproto/mtprotoworker";
import appChatsManager from "./appChatsManager"; import appChatsManager from "./appChatsManager";
import appUsersManager from "./appUsersManager"; import appUsersManager from "./appUsersManager";
import apiUpdatesManager from "./apiUpdatesManager";
import rootScope from "../rootScope";
export enum PrivacyType { export enum PrivacyType {
Everybody = 2, Everybody = 2,
@ -11,8 +13,27 @@ export enum PrivacyType {
} }
export class AppPrivacyManager { export class AppPrivacyManager {
private privacy: Partial<{
[key in PrivacyKey['_']]: PrivacyRule[] | Promise<PrivacyRule[]>
}> = {};
constructor() { constructor() {
rootScope.on('apiUpdate', (e) => {
const update = e as Update;
switch(update._) {
case 'updatePrivacy':
const key = update.key._;
this.privacy[key] = update.rules;
rootScope.broadcast('privacy_update', update);
break;
}
});
}
public convertInputKeyToKey(inputKey: string) {
let str = inputKey.replace('input', '');
return (str[0].toLowerCase() + str.slice(1)) as string;
} }
public setPrivacy(inputKey: InputPrivacyKey['_'], rules: InputPrivacyRule[]) { public setPrivacy(inputKey: InputPrivacyKey['_'], rules: InputPrivacyRule[]) {
@ -22,17 +43,39 @@ export class AppPrivacyManager {
}, },
rules rules
}).then(privacyRules => { }).then(privacyRules => {
/* appUsersManager.saveApiUsers(privacyRules.users); appUsersManager.saveApiUsers(privacyRules.users);
appChatsManager.saveApiChats(privacyRules.chats); appChatsManager.saveApiChats(privacyRules.chats);
console.log('privacy rules', inputKey, privacyRules, privacyRules.rules); */ apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updatePrivacy',
key: {
_: this.convertInputKeyToKey(inputKey)
},
rules: rules.map(inputRule => {
const rule: PrivacyRule = {} as any;
Object.assign(rule, inputRule);
rule._ = this.convertInputKeyToKey(rule._) as any;
return rule;
})
} as Update.updatePrivacy
});
//console.log('privacy rules', inputKey, privacyRules, privacyRules.rules);
return privacyRules.rules; return privacyRules.rules;
}); });
} }
public getPrivacy(inputKey: InputPrivacyKey['_']) { public getPrivacy(inputKey: InputPrivacyKey['_']) {
return apiManager.invokeApi('account.getPrivacy', { const privacyKey: PrivacyKey['_'] = this.convertInputKeyToKey(inputKey) as any;
const rules = this.privacy[privacyKey];
if(rules) {
return Promise.resolve(rules);
}
return this.privacy[privacyKey] = apiManager.invokeApi('account.getPrivacy', {
key: { key: {
_: inputKey _: inputKey
} }
@ -42,7 +85,7 @@ export class AppPrivacyManager {
//console.log('privacy rules', inputKey, privacyRules, privacyRules.rules); //console.log('privacy rules', inputKey, privacyRules, privacyRules.rules);
return privacyRules.rules; return this.privacy[privacyKey] = privacyRules.rules;
}); });
} }

6
src/lib/rootScope.ts

@ -92,6 +92,8 @@ type BroadcastEvents = {
'overlay_toggle': boolean, 'overlay_toggle': boolean,
'background_change': void, 'background_change': void,
'privacy_update': Update.updatePrivacy
}; };
class RootScope extends EventListenerBase<any> { class RootScope extends EventListenerBase<any> {
@ -126,11 +128,11 @@ class RootScope extends EventListenerBase<any> {
} }
public broadcast = <T extends keyof BroadcastEvents>(name: T, detail?: BroadcastEvents[T]) => { public broadcast = <T extends keyof BroadcastEvents>(name: T, detail?: BroadcastEvents[T]) => {
//if(DEBUG) { /* //if(DEBUG) {
if(name !== 'user_update') { if(name !== 'user_update') {
console.debug('Broadcasting ' + name + ' event, with args:', detail); console.debug('Broadcasting ' + name + ' event, with args:', detail);
} }
//} //} */
this.setListenerResult(name, detail); this.setListenerResult(name, detail);
}; };

Loading…
Cancel
Save