Browse Source

Better auto download settings

master
Eduard Kuzmenko 3 years ago
parent
commit
7b2f1a015e
  1. 2
      src/components/appSearchSuper..ts
  2. 8
      src/components/audio.ts
  3. 16
      src/components/chat/bubbles.ts
  4. 27
      src/components/chat/chat.ts
  5. 53
      src/components/sidebarLeft/tabs/autoDownload/file.ts
  6. 59
      src/components/sidebarLeft/tabs/autoDownload/photo.ts
  7. 18
      src/components/sidebarLeft/tabs/autoDownload/video.ts
  8. 196
      src/components/sidebarLeft/tabs/dataAndStorage.ts
  9. 80
      src/components/sidebarLeft/tabs/generalSettings.ts
  10. 14
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  11. 21
      src/components/sidebarLeft/tabs/settings.ts
  12. 4
      src/components/sliderTab.ts
  13. 52
      src/components/wrappers.ts
  14. 44
      src/helpers/autoDownload.ts
  15. 19
      src/helpers/formatBytes.ts
  16. 12
      src/helpers/number.ts
  17. 37
      src/lang.ts
  18. 50
      src/lib/appManagers/appStateManager.ts
  19. 2
      src/lib/langPack.ts
  20. 4
      src/scss/partials/_button.scss
  21. 9
      src/scss/partials/_row.scss

2
src/components/appSearchSuper..ts

@ -734,7 +734,7 @@ export default class AppSearchSuper {
showSender, showSender,
searchContext: this.copySearchContext(inputFilter), searchContext: this.copySearchContext(inputFilter),
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
noAutoDownload: true autoDownloadSize: 0
}); });
if((['audio', 'voice', 'round'] as MyDocument['type'][]).includes(document.type)) { if((['audio', 'voice', 'round'] as MyDocument['type'][]).includes(document.type)) {

8
src/components/audio.ts

@ -26,9 +26,10 @@ import { joinElementsWith } from "../lib/langPack";
import { MiddleEllipsisElement } from "./middleEllipsis"; import { MiddleEllipsisElement } from "./middleEllipsis";
import htmlToSpan from "../helpers/dom/htmlToSpan"; import htmlToSpan from "../helpers/dom/htmlToSpan";
import { formatFullSentTime } from "../helpers/date"; import { formatFullSentTime } from "../helpers/date";
import { clamp, formatBytes } from "../helpers/number";
import throttleWithRaf from "../helpers/schedulers/throttleWithRaf"; import throttleWithRaf from "../helpers/schedulers/throttleWithRaf";
import { NULL_PEER_ID } from "../lib/mtproto/mtproto_config"; import { NULL_PEER_ID } from "../lib/mtproto/mtproto_config";
import formatBytes from "../helpers/formatBytes";
import { clamp } from "../helpers/number";
rootScope.addEventListener('messages_media_read', ({mids, peerId}) => { rootScope.addEventListener('messages_media_read', ({mids, peerId}) => {
mids.forEach(mid => { mids.forEach(mid => {
@ -523,7 +524,8 @@ export default class AudioElement extends HTMLElement {
if(!isOutgoing) { if(!isOutgoing) {
let preloader: ProgressivePreloader = this.preloader; let preloader: ProgressivePreloader = this.preloader;
onLoad(doc.type !== 'audio' && !this.noAutoDownload); const autoDownload = doc.type !== 'audio'/* || !this.noAutoDownload */;
onLoad(autoDownload);
const r = (shouldPlay: boolean) => { const r = (shouldPlay: boolean) => {
if(this.audio.src) { if(this.audio.src) {
@ -634,7 +636,7 @@ export default class AudioElement extends HTMLElement {
}; };
if(!this.audio?.src) { if(!this.audio?.src) {
if(doc.type !== 'audio' && !this.noAutoDownload) { if(autoDownload) {
r(false); r(false);
} else { } else {
attachClickEvent(toggle, () => { attachClickEvent(toggle, () => {

16
src/components/chat/bubbles.ts

@ -3102,7 +3102,7 @@ export default class ChatBubbles {
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
chat: this.chat, chat: this.chat,
loadPromises, loadPromises,
noAutoDownload: this.chat.noAutoDownloadMedia, autoDownload: this.chat.autoDownload,
}); });
break; break;
@ -3119,7 +3119,7 @@ export default class ChatBubbles {
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
middleware: this.getMiddleware(), middleware: this.getMiddleware(),
loadPromises, loadPromises,
noAutoDownload: this.chat.noAutoDownloadMedia, autoDownloadSize: this.chat.autoDownload.photo,
}); });
break; break;
@ -3174,13 +3174,13 @@ export default class ChatBubbles {
isOut, isOut,
group: CHAT_ANIMATION_GROUP, group: CHAT_ANIMATION_GROUP,
loadPromises, loadPromises,
noAutoDownload: this.chat.noAutoDownloadMedia, autoDownloadSize: this.chat.autoDownload.video,
}); });
//} //}
} else { } else {
const docDiv = wrapDocument({ const docDiv = wrapDocument({
message, message,
noAutoDownload: this.chat.noAutoDownloadMedia, autoDownloadSize: this.chat.autoDownload.file,
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
loadPromises loadPromises
}); });
@ -3265,7 +3265,7 @@ export default class ChatBubbles {
middleware: this.getMiddleware(), middleware: this.getMiddleware(),
loadPromises, loadPromises,
withoutPreloader: isSquare, withoutPreloader: isSquare,
noAutoDownload: this.chat.noAutoDownloadMedia, autoDownloadSize: this.chat.autoDownload.photo,
}); });
} }
@ -3346,7 +3346,7 @@ export default class ChatBubbles {
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
chat: this.chat, chat: this.chat,
loadPromises, loadPromises,
noAutoDownload: this.chat.noAutoDownloadMedia, autoDownload: this.chat.autoDownload,
}); });
} else { } else {
const withTail = !IS_ANDROID && !IS_APPLE && !isRound && canHaveTail && !withReplies && USE_MEDIA_TAILS; const withTail = !IS_ANDROID && !IS_APPLE && !isRound && canHaveTail && !withReplies && USE_MEDIA_TAILS;
@ -3363,7 +3363,7 @@ export default class ChatBubbles {
middleware: this.getMiddleware(), middleware: this.getMiddleware(),
group: CHAT_ANIMATION_GROUP, group: CHAT_ANIMATION_GROUP,
loadPromises, loadPromises,
noAutoDownload: this.chat.noAutoDownloadMedia, autoDownloadSize: this.chat.autoDownload.video,
searchContext: isRound ? { searchContext: isRound ? {
peerId: this.peerId, peerId: this.peerId,
inputFilter: {_: 'inputMessagesFilterRoundVoice'}, inputFilter: {_: 'inputMessagesFilterRoundVoice'},
@ -3382,7 +3382,7 @@ export default class ChatBubbles {
messageDiv, messageDiv,
chat: this.chat, chat: this.chat,
loadPromises, loadPromises,
noAutoDownload: this.chat.noAutoDownloadMedia, autoDownloadSize: this.chat.autoDownload.file,
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
searchContext: doc.type === 'voice' || doc.type === 'audio' ? { searchContext: doc.type === 'voice' || doc.type === 'audio' ? {
peerId: this.peerId, peerId: this.peerId,

27
src/components/chat/chat.ts

@ -42,6 +42,7 @@ import AppPrivateSearchTab from "../sidebarRight/tabs/search";
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl"; import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
import mediaSizes from "../../helpers/mediaSizes"; import mediaSizes from "../../helpers/mediaSizes";
import ChatSearch from "./search"; import ChatSearch from "./search";
import getAutoDownloadSettingsByPeerId, { ChatAutoDownloadSettings } from "../../helpers/autoDownload";
export type ChatType = 'chat' | 'pinned' | 'replies' | 'discussion' | 'scheduled'; export type ChatType = 'chat' | 'pinned' | 'replies' | 'discussion' | 'scheduled';
@ -69,11 +70,12 @@ export default class Chat extends EventListenerBase<{
public type: ChatType; public type: ChatType;
public noAutoDownloadMedia: boolean;
public noForwards: boolean; public noForwards: boolean;
public inited: boolean; public inited: boolean;
public autoDownload: ChatAutoDownloadSettings;
constructor(public appImManager: AppImManager, constructor(public appImManager: AppImManager,
public appChatsManager: AppChatsManager, public appChatsManager: AppChatsManager,
public appDocsManager: AppDocsManager, public appDocsManager: AppDocsManager,
@ -346,28 +348,7 @@ export default class Chat extends EventListenerBase<{
} }
public setAutoDownloadMedia() { public setAutoDownloadMedia() {
const peerId = this.peerId; this.autoDownload = getAutoDownloadSettingsByPeerId(this.peerId);
if(!peerId) {
return;
}
let type: keyof State['settings']['autoDownload'];
if(!peerId.isUser()) {
if(peerId.isBroadcast()) {
type = 'channels';
} else {
type = 'groups';
}
} else {
if(peerId.isContact()) {
type = 'contacts';
} else {
type = 'private';
}
}
this.noAutoDownloadMedia = !rootScope.settings.autoDownload[type];
} }
public setMessageId(messageId?: number) { public setMessageId(messageId?: number) {

53
src/components/sidebarLeft/tabs/autoDownload/file.ts

@ -0,0 +1,53 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import formatBytes from "../../../../helpers/formatBytes";
import debounce from "../../../../helpers/schedulers/debounce";
import appStateManager from "../../../../lib/appManagers/appStateManager";
import I18n from "../../../../lib/langPack";
import rootScope from "../../../../lib/rootScope";
import { SliderSuperTabEventable } from "../../../sliderTab";
import { RangeSettingSelector } from "../generalSettings";
import { autoDownloadPeerTypeSection } from "./photo";
export default class AppAutoDownloadFileTab extends SliderSuperTabEventable {
protected init() {
this.header.classList.add('with-border');
this.setTitle('AutoDownloadFiles');
const debouncedSave = debounce((sizeMax: number) => {
appStateManager.setByKey('settings.autoDownloadNew.file_size_max', sizeMax);
}, 200, false, true);
const section = autoDownloadPeerTypeSection('file', 'AutoDownloadFilesTitle');
const MIN = 512 * 1024;
// const MAX = 2 * 1024 * 1024 * 1024;
const MAX = 20 * 1024 * 1024;
const MAX_RANGE = MAX - MIN;
const sizeMax = rootScope.settings.autoDownloadNew.file_size_max;
const value = Math.sqrt(Math.sqrt((sizeMax - MIN) / MAX_RANGE));
const upTo = new I18n.IntlElement({
key: 'AutodownloadSizeLimitUpTo',
args: [formatBytes(sizeMax)]
});
const range = new RangeSettingSelector('AutoDownloadMaxFileSize', 0.01, value, 0, 1, false);
range.onChange = (value) => {
const sizeMax = (value ** 4 * MAX_RANGE + MIN) | 0;
upTo.compareAndUpdate({args: [formatBytes(sizeMax)]});
debouncedSave(sizeMax);
};
range.valueContainer.append(upTo.element);
section.content.append(range.container);
this.scrollable.append(section.container);
}
}

59
src/components/sidebarLeft/tabs/autoDownload/photo.ts

@ -0,0 +1,59 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { SettingSection } from "../..";
import { LangPackKey } from "../../../../lib/langPack";
import CheckboxField from "../../../checkboxField";
import { SliderSuperTabEventable } from "../../../sliderTab";
export function autoDownloadPeerTypeSection(type: 'photo' | 'video' | 'file', title: LangPackKey) {
const section = new SettingSection({name: title});
const key = 'settings.autoDownload.' + type + '.';
const contactsCheckboxField = new CheckboxField({
text: 'AutodownloadContacts',
name: 'contacts',
stateKey: key + 'contacts',
withRipple: true
});
const privateCheckboxField = new CheckboxField({
text: 'AutodownloadPrivateChats',
name: 'private',
stateKey: key + 'private',
withRipple: true
});
const groupsCheckboxField = new CheckboxField({
text: 'AutodownloadGroupChats',
name: 'groups',
stateKey: key + 'groups',
withRipple: true
});
const channelsCheckboxField = new CheckboxField({
text: 'AutodownloadChannels',
name: 'channels',
stateKey: key + 'channels',
withRipple: true
});
section.content.append(
contactsCheckboxField.label,
privateCheckboxField.label,
groupsCheckboxField.label,
channelsCheckboxField.label
);
return section;
}
export default class AppAutoDownloadPhotoTab extends SliderSuperTabEventable {
protected init() {
this.header.classList.add('with-border');
this.setTitle('AutoDownloadPhotos');
const section = autoDownloadPeerTypeSection('photo', 'AutoDownloadPhotosTitle');
this.scrollable.append(section.container);
}
}

18
src/components/sidebarLeft/tabs/autoDownload/video.ts

@ -0,0 +1,18 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { SliderSuperTabEventable } from "../../../sliderTab";
import { autoDownloadPeerTypeSection } from "./photo";
export default class AppAutoDownloadVideoTab extends SliderSuperTabEventable {
protected init() {
this.header.classList.add('with-border');
this.setTitle('AutoDownloadVideos');
const section = autoDownloadPeerTypeSection('video', 'AutoDownloadVideosTitle');
this.scrollable.append(section.container);
}
}

196
src/components/sidebarLeft/tabs/dataAndStorage.ts

@ -0,0 +1,196 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { SettingSection } from "..";
import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import replaceContent from "../../../helpers/dom/replaceContent";
import toggleDisability from "../../../helpers/dom/toggleDisability";
import formatBytes from "../../../helpers/formatBytes";
import { copy, deepEqual } from "../../../helpers/object";
import appStateManager, { AutoDownloadPeerTypeSettings, STATE_INIT } from "../../../lib/appManagers/appStateManager";
import { FormatterArguments, i18n, join, LangPackKey } from "../../../lib/langPack";
import rootScope from "../../../lib/rootScope";
import Button from "../../button";
import CheckboxField from "../../checkboxField";
import confirmationPopup from "../../confirmationPopup";
import Row from "../../row";
import { SliderSuperTabEventable, SliderSuperTabEventableConstructable } from "../../sliderTab";
import AppAutoDownloadFileTab from "./autoDownload/file";
import AppAutoDownloadPhotoTab from "./autoDownload/photo";
import AppAutoDownloadVideoTab from "./autoDownload/video";
const AUTO_DOWNLOAD_FOR_KEYS: {[k in keyof AutoDownloadPeerTypeSettings]: LangPackKey} = {
contacts: 'AutoDownloadContacts',
private: 'AutoDownloadPm',
groups: 'AutoDownloadGroups',
channels: 'AutoDownloadChannels'
};
export default class AppDataAndStorageTab extends SliderSuperTabEventable {
protected async init() {
this.header.classList.add('with-border');
this.setTitle('DataSettings');
{
const section = new SettingSection({name: 'AutomaticMediaDownload', caption: 'AutoDownloadAudioInfo'});
const state = await appStateManager.getState();
const autoCheckboxField = new CheckboxField({
text: 'AutoDownloadMedia',
name: 'auto',
checked: !state.settings.autoDownloadNew.pFlags.disabled,
withRipple: true
});
const onChange = () => {
toggleDisability([resetButton],
deepEqual(state.settings.autoDownload, STATE_INIT.settings.autoDownload) &&
deepEqual(state.settings.autoDownloadNew, STATE_INIT.settings.autoDownloadNew));
};
const setSubtitles = () => {
this.setAutoDownloadSubtitle(photoRow, state.settings.autoDownload.photo, /* state.settings.autoDownloadNew.photo_size_max */);
this.setAutoDownloadSubtitle(videoRow, state.settings.autoDownload.video/* , state.settings.autoDownloadNew.video_size_max */);
this.setAutoDownloadSubtitle(fileRow, state.settings.autoDownload.file, state.settings.autoDownloadNew.file_size_max);
};
const openTab = (tabConstructor: SliderSuperTabEventableConstructable) => {
const tab = new tabConstructor(this.slider, true);
tab.open();
this.listenerSetter.add(tab.eventListener)('destroy', () => {
setSubtitles();
onChange();
}, {once: true});
};
const photoRow = new Row({
titleLangKey: 'AutoDownloadPhotos',
subtitle: '',
clickable: () => {
openTab(AppAutoDownloadPhotoTab);
}
});
const videoRow = new Row({
titleLangKey: 'AutoDownloadVideos',
subtitle: '',
clickable: () => {
openTab(AppAutoDownloadVideoTab);
}
});
const fileRow = new Row({
titleLangKey: 'AutoDownloadFiles',
subtitle: '',
clickable: () => {
openTab(AppAutoDownloadFileTab);
}
});
const resetButton = Button('btn-primary btn-transparent primary', {icon: 'delete', text: 'ResetAutomaticMediaDownload'});
attachClickEvent(resetButton, () => {
confirmationPopup({
titleLangKey: 'ResetAutomaticMediaDownloadAlertTitle',
descriptionLangKey: 'ResetAutomaticMediaDownloadAlert',
button: {
langKey: 'Reset'
}
}).then(() => {
rootScope.settings.autoDownloadNew = copy(STATE_INIT.settings.autoDownloadNew);
rootScope.settings.autoDownload = copy(STATE_INIT.settings.autoDownload);
appStateManager.pushToState('settings', rootScope.settings);
rootScope.dispatchEvent('settings_updated', {key: 'settings', value: rootScope.settings});
setSubtitles();
autoCheckboxField.checked = !state.settings.autoDownloadNew.pFlags.disabled;
});
});
const onDisabledChange = () => {
const disabled = !autoCheckboxField.checked;
const settings = rootScope.settings;
if(disabled) {
settings.autoDownloadNew.pFlags.disabled = true;
} else {
delete settings.autoDownloadNew.pFlags.disabled;
}
[photoRow, videoRow, fileRow].forEach(row => {
row.container.classList.toggle('is-disabled', disabled);
});
appStateManager.pushToState('settings', settings);
rootScope.dispatchEvent('settings_updated', {key: 'settings', value: settings});
onChange();
};
autoCheckboxField.input.addEventListener('change', onDisabledChange);
onDisabledChange();
setSubtitles();
section.content.append(
autoCheckboxField.label,
photoRow.container,
videoRow.container,
fileRow.container,
resetButton
);
this.scrollable.append(section.container);
}
{
const section = new SettingSection({name: 'AutoplayMedia'});
const gifsCheckboxField = new CheckboxField({
text: 'AutoplayGIF',
name: 'gifs',
stateKey: 'settings.autoPlay.gifs',
withRipple: true
});
const videosCheckboxField = new CheckboxField({
text: 'AutoplayVideo',
name: 'videos',
stateKey: 'settings.autoPlay.videos',
withRipple: true
});
section.content.append(gifsCheckboxField.label, videosCheckboxField.label);
this.scrollable.append(section.container);
}
}
private setAutoDownloadSubtitle(row: Row, settings: AutoDownloadPeerTypeSettings, sizeMax?: number) {
let key: LangPackKey, args: FormatterArguments = [];
const peerKeys = Object.keys(settings) as (keyof typeof AUTO_DOWNLOAD_FOR_KEYS)[];
const enabledKeys = peerKeys.map(key => settings[key] ? AUTO_DOWNLOAD_FOR_KEYS[key] : undefined).filter(Boolean);
if(!enabledKeys.length || sizeMax === 0) {
key = 'AutoDownloadOff';
} else {
const isAll = enabledKeys.length === peerKeys.length;
if(sizeMax !== undefined) {
key = isAll ? 'AutoDownloadUpToOnAllChats' : 'AutoDownloadOnUpToFor';
args.push(formatBytes(sizeMax));
} else {
key = isAll ? 'AutoDownloadOnAllChats' : 'AutoDownloadOnFor';
}
if(!isAll) {
const fragment = document.createElement('span');
fragment.append(...join(enabledKeys.map(key => i18n(key)), true, false));
args.push(fragment);
}
}
replaceContent(row.subtitle, i18n(key, args));
}
}

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

@ -4,7 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import { generateSection } from ".."; import { generateSection, SettingSection } from "..";
import RangeSelector from "../../rangeSelector"; import RangeSelector from "../../rangeSelector";
import Button from "../../button"; import Button from "../../button";
import CheckboxField from "../../checkboxField"; import CheckboxField from "../../checkboxField";
@ -31,11 +31,19 @@ import AppQuickReactionTab from "./quickReaction";
export class RangeSettingSelector { export class RangeSettingSelector {
public container: HTMLDivElement; public container: HTMLDivElement;
public valueContainer: HTMLElement;
private range: RangeSelector; private range: RangeSelector;
public onChange: (value: number) => void; public onChange: (value: number) => void;
constructor(name: LangPackKey, step: number, initialValue: number, minValue: number, maxValue: number) { constructor(
name: LangPackKey,
step: number,
initialValue: number,
minValue: number,
maxValue: number,
writeValue = true
) {
const BASE_CLASS = 'range-setting-selector'; const BASE_CLASS = 'range-setting-selector';
this.container = document.createElement('div'); this.container = document.createElement('div');
this.container.classList.add(BASE_CLASS); this.container.classList.add(BASE_CLASS);
@ -47,9 +55,12 @@ export class RangeSettingSelector {
nameDiv.classList.add(BASE_CLASS + '-name'); nameDiv.classList.add(BASE_CLASS + '-name');
_i18n(nameDiv, name); _i18n(nameDiv, name);
const valueDiv = document.createElement('div'); const valueDiv = this.valueContainer = document.createElement('div');
valueDiv.classList.add(BASE_CLASS + '-value'); valueDiv.classList.add(BASE_CLASS + '-value');
if(writeValue) {
valueDiv.innerHTML = '' + initialValue; valueDiv.innerHTML = '' + initialValue;
}
details.append(nameDiv, valueDiv); details.append(nameDiv, valueDiv);
@ -65,9 +76,11 @@ export class RangeSettingSelector {
this.onChange(value); this.onChange(value);
} }
if(writeValue) {
//console.log('font size scrub:', value); //console.log('font size scrub:', value);
valueDiv.innerText = '' + value; valueDiv.innerText = '' + value;
} }
}
}); });
this.container.append(details, this.range.container); this.container.append(details, this.range.container);
@ -213,58 +226,6 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
container.append(form); container.append(form);
} }
{
const container = section('AutoDownloadMedia');
//container.classList.add('sidebar-left-section-disabled');
const contactsCheckboxField = new CheckboxField({
text: 'AutodownloadContacts',
name: 'contacts',
stateKey: 'settings.autoDownload.contacts',
withRipple: true
});
const privateCheckboxField = new CheckboxField({
text: 'AutodownloadPrivateChats',
name: 'private',
stateKey: 'settings.autoDownload.private',
withRipple: true
});
const groupsCheckboxField = new CheckboxField({
text: 'AutodownloadGroupChats',
name: 'groups',
stateKey: 'settings.autoDownload.groups',
withRipple: true
});
const channelsCheckboxField = new CheckboxField({
text: 'AutodownloadChannels',
name: 'channels',
stateKey: 'settings.autoDownload.channels',
withRipple: true
});
container.append(contactsCheckboxField.label, privateCheckboxField.label, groupsCheckboxField.label, channelsCheckboxField.label);
}
{
const container = section('General.AutoplayMedia');
//container.classList.add('sidebar-left-section-disabled');
const gifsCheckboxField = new CheckboxField({
text: 'AutoplayGIF',
name: 'gifs',
stateKey: 'settings.autoPlay.gifs',
withRipple: true
});
const videosCheckboxField = new CheckboxField({
text: 'AutoplayVideo',
name: 'videos',
stateKey: 'settings.autoPlay.videos',
withRipple: true
});
container.append(gifsCheckboxField.label, videosCheckboxField.label);
}
{ {
const container = section('Emoji'); const container = section('Emoji');
@ -285,7 +246,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
} }
{ {
const container = section('Telegram.InstalledStickerPacksController'); const section = new SettingSection({name: 'Telegram.InstalledStickerPacksController', caption: 'StickersBotInfo'});
const reactionsRow = new Row({ const reactionsRow = new Row({
titleLangKey: 'DoubleTapSetting', titleLangKey: 'DoubleTapSetting',
@ -324,6 +285,8 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
const stickerSets: {[id: string]: Row} = {}; const stickerSets: {[id: string]: Row} = {};
const stickersContent = section.generateContentElement();
const lazyLoadQueue = new LazyLoadQueue(); const lazyLoadQueue = new LazyLoadQueue();
const renderStickerSet = (stickerSet: StickerSet.stickerSet, method: 'append' | 'prepend' = 'append') => { const renderStickerSet = (stickerSet: StickerSet.stickerSet, method: 'append' | 'prepend' = 'append') => {
const row = new Row({ const row = new Row({
@ -353,7 +316,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
row.container.append(div); row.container.append(div);
container[method](row.container); stickersContent[method](row.container);
}; };
appStickersManager.getAllStickers().then(allStickers => { appStickersManager.getAllStickers().then(allStickers => {
@ -380,7 +343,8 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
} }
}); });
container.append(reactionsRow.container, suggestCheckboxField.label, loopCheckboxField.label); section.content.append(reactionsRow.container, suggestCheckboxField.label, loopCheckboxField.label);
this.scrollable.append(section.container);
} }
} }

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

@ -38,13 +38,14 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
private authorizations: Authorization.authorization[]; private authorizations: Authorization.authorization[];
protected init() { protected init() {
this.header.classList.add('with-border');
this.container.classList.add('dont-u-dare-block-me'); this.container.classList.add('dont-u-dare-block-me');
this.setTitle('PrivacySettings'); this.setTitle('PrivacySettings');
const SUBTITLE: LangPackKey = 'Loading'; const SUBTITLE: LangPackKey = 'Loading';
{ {
const section = new SettingSection({noDelimiter: true}); const section = new SettingSection({noDelimiter: true, caption: 'SessionsInfo'});
let blockedPeerIds: PeerId[]; let blockedPeerIds: PeerId[];
const blockedUsersRow = new Row({ const blockedUsersRow = new Row({
@ -141,7 +142,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
} }
{ {
const section = new SettingSection({name: 'PrivacyTitle'}); const section = new SettingSection({name: 'PrivacyTitle', caption: 'GroupsAndChannelsHelp'});
section.content.classList.add('privacy-navigation-container'); section.content.classList.add('privacy-navigation-container');
@ -218,7 +219,14 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
}); });
}; };
section.content.append(numberVisibilityRow.container, lastSeenTimeRow.container, photoVisibilityRow.container, callRow.container, linkAccountRow.container, groupChatsAddRow.container); section.content.append(
numberVisibilityRow.container,
lastSeenTimeRow.container,
photoVisibilityRow.container,
callRow.container,
linkAccountRow.container,
groupChatsAddRow.container
);
this.scrollable.append(section.container); this.scrollable.append(section.container);
for(const key in rowsByKeys) { for(const key in rowsByKeys) {

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

@ -19,6 +19,7 @@ import PeerTitle from "../../peerTitle";
import AppLanguageTab from "./language"; import AppLanguageTab from "./language";
import lottieLoader from "../../../lib/rlottie/lottieLoader"; import lottieLoader from "../../../lib/rlottie/lottieLoader";
import PopupPeer from "../../popups/peer"; import PopupPeer from "../../popups/peer";
import AppDataAndStorageTab from "./dataAndStorage";
//import AppMediaViewer from "../../appMediaViewerNew"; //import AppMediaViewer from "../../appMediaViewerNew";
export default class AppSettingsTab extends SliderSuperTab { export default class AppSettingsTab extends SliderSuperTab {
@ -31,6 +32,7 @@ export default class AppSettingsTab extends SliderSuperTab {
folders: HTMLButtonElement, folders: HTMLButtonElement,
general: HTMLButtonElement, general: HTMLButtonElement,
notifications: HTMLButtonElement, notifications: HTMLButtonElement,
storage: HTMLButtonElement,
privacy: HTMLButtonElement, privacy: HTMLButtonElement,
language: HTMLButtonElement language: HTMLButtonElement
} = {} as any; } = {} as any;
@ -115,12 +117,15 @@ export default class AppSettingsTab extends SliderSuperTab {
buttonsDiv.classList.add('profile-buttons'); buttonsDiv.classList.add('profile-buttons');
const className = 'profile-button btn-primary btn-transparent'; const className = 'profile-button btn-primary btn-transparent';
buttonsDiv.append(this.buttons.edit = Button(className, {icon: 'edit', text: 'EditAccount.Title'})); buttonsDiv.append(
buttonsDiv.append(this.buttons.folders = Button(className, {icon: 'folder', text: 'AccountSettings.Filters'})); this.buttons.edit = Button(className, {icon: 'edit', text: 'EditAccount.Title'}),
buttonsDiv.append(this.buttons.general = Button(className, {icon: 'settings', text: 'Telegram.GeneralSettingsViewController'})); this.buttons.folders = Button(className, {icon: 'folder', text: 'AccountSettings.Filters'}),
buttonsDiv.append(this.buttons.notifications = Button(className, {icon: 'unmute', text: 'AccountSettings.Notifications'})); this.buttons.general = Button(className, {icon: 'settings', text: 'Telegram.GeneralSettingsViewController'}),
buttonsDiv.append(this.buttons.privacy = Button(className, {icon: 'lock', text: 'AccountSettings.PrivacyAndSecurity'})); this.buttons.storage = Button(className, {icon: 'data', text: 'DataSettings'}),
buttonsDiv.append(this.buttons.language = Button(className, {icon: 'language', text: 'AccountSettings.Language'})); this.buttons.notifications = Button(className, {icon: 'unmute', text: 'AccountSettings.Notifications'}),
this.buttons.privacy = Button(className, {icon: 'lock', text: 'AccountSettings.PrivacyAndSecurity'}),
this.buttons.language = Button(className, {icon: 'language', text: 'AccountSettings.Language'})
);
this.scrollable.append(this.avatarElem, this.nameDiv, this.phoneDiv, buttonsDiv); this.scrollable.append(this.avatarElem, this.nameDiv, this.phoneDiv, buttonsDiv);
this.scrollable.container.classList.add('profile-content-wrapper'); this.scrollable.container.classList.add('profile-content-wrapper');
@ -142,6 +147,10 @@ export default class AppSettingsTab extends SliderSuperTab {
new AppGeneralSettingsTab(this.slider).open(); new AppGeneralSettingsTab(this.slider).open();
}); });
this.buttons.storage.addEventListener('click', () => {
new AppDataAndStorageTab(this.slider).open();
});
this.buttons.notifications.addEventListener('click', () => { this.buttons.notifications.addEventListener('click', () => {
new AppNotificationsTab(this.slider).open(); new AppNotificationsTab(this.slider).open();
}); });

4
src/components/sliderTab.ts

@ -22,6 +22,10 @@ export interface SliderSuperTabConstructable {
new(slider: SidebarSlider, destroyable: boolean): SliderSuperTab; new(slider: SidebarSlider, destroyable: boolean): SliderSuperTab;
} }
export interface SliderSuperTabEventableConstructable {
new(slider: SidebarSlider, destroyable: boolean): SliderSuperTabEventable;
}
export default class SliderSuperTab implements SliderTab { export default class SliderSuperTab implements SliderTab {
public container: HTMLElement; public container: HTMLElement;

52
src/components/wrappers.ts

@ -9,7 +9,6 @@ import { getEmojiToneIndex } from '../vendor/emoji';
import { deferredPromise } from '../helpers/cancellablePromise'; import { deferredPromise } from '../helpers/cancellablePromise';
import { formatFullSentTime } from '../helpers/date'; import { formatFullSentTime } from '../helpers/date';
import mediaSizes, { ScreenSize } from '../helpers/mediaSizes'; import mediaSizes, { ScreenSize } from '../helpers/mediaSizes';
import { formatBytes } from '../helpers/number';
import { IS_SAFARI } from '../environment/userAgent'; import { IS_SAFARI } from '../environment/userAgent';
import { Message, PhotoSize, StickerSet } from '../layer'; import { Message, PhotoSize, StickerSet } from '../layer';
import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager"; import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager";
@ -56,6 +55,8 @@ import throttle from '../helpers/schedulers/throttle';
import { SendMessageEmojiInteractionData } from '../types'; import { SendMessageEmojiInteractionData } from '../types';
import IS_VIBRATE_SUPPORTED from '../environment/vibrateSupport'; import IS_VIBRATE_SUPPORTED from '../environment/vibrateSupport';
import Row from './row'; import Row from './row';
import { ChatAutoDownloadSettings } from '../helpers/autoDownload';
import formatBytes from '../helpers/formatBytes';
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB
@ -82,7 +83,7 @@ mediaSizes.addEventListener('changeScreen', (from, to) => {
} }
}); });
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises, noPlayButton, noAutoDownload, size, searchContext}: { export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises, noPlayButton, autoDownloadSize, size, searchContext}: {
doc: MyDocument, doc: MyDocument,
container?: HTMLElement, container?: HTMLElement,
message?: Message.message, message?: Message.message,
@ -98,10 +99,11 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
onlyPreview?: boolean, onlyPreview?: boolean,
withoutPreloader?: boolean, withoutPreloader?: boolean,
loadPromises?: Promise<any>[], loadPromises?: Promise<any>[],
noAutoDownload?: boolean, autoDownloadSize?: number,
size?: PhotoSize, size?: PhotoSize,
searchContext?: MediaSearchContext, searchContext?: MediaSearchContext,
}) { }) {
let noAutoDownload = autoDownloadSize === 0;
const isAlbumItem = !(boxWidth && boxHeight); const isAlbumItem = !(boxWidth && boxHeight);
const canAutoplay = /* doc.sticker || */( const canAutoplay = /* doc.sticker || */(
( (
@ -163,7 +165,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
middleware, middleware,
withoutPreloader, withoutPreloader,
loadPromises, loadPromises,
noAutoDownload, autoDownloadSize,
size size
}); });
@ -371,7 +373,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
middleware, middleware,
withoutPreloader: true, withoutPreloader: true,
loadPromises, loadPromises,
noAutoDownload, autoDownloadSize,
size size
}); });
@ -550,7 +552,7 @@ rootScope.addEventListener('download_start', (docId) => {
}); });
}); });
export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext, loadPromises, noAutoDownload, lazyLoadQueue}: { export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext, loadPromises, autoDownloadSize, lazyLoadQueue}: {
message: any, message: any,
withTime?: boolean, withTime?: boolean,
fontWeight?: number, fontWeight?: number,
@ -558,10 +560,11 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
showSender?: boolean, showSender?: boolean,
searchContext?: MediaSearchContext, searchContext?: MediaSearchContext,
loadPromises?: Promise<any>[], loadPromises?: Promise<any>[],
noAutoDownload?: boolean, autoDownloadSize?: number,
lazyLoadQueue?: LazyLoadQueue lazyLoadQueue?: LazyLoadQueue
}): HTMLElement { }): HTMLElement {
if(!fontWeight) fontWeight = 500; if(!fontWeight) fontWeight = 500;
const noAutoDownload = autoDownloadSize === 0;
const doc = (message.media.document || message.media.webpage.document) as MyDocument; const doc = (message.media.document || message.media.webpage.document) as MyDocument;
const uploading = message.pFlags.is_outgoing && message.media?.preloader; const uploading = message.pFlags.is_outgoing && message.media?.preloader;
@ -682,7 +685,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
} }
}; };
const load = (e: Event) => { const load = (e?: Event) => {
const save = !e || e.isTrusted; const save = !e || e.isTrusted;
const doc = appDocsManager.getDoc(docDiv.dataset.docId); const doc = appDocsManager.getDoc(docDiv.dataset.docId);
let download: DownloadBlob; let download: DownloadBlob;
@ -690,13 +693,16 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
if(!save) { if(!save) {
download = appDocsManager.downloadDoc(doc, queueId); download = appDocsManager.downloadDoc(doc, queueId);
} else if(doc.type === 'pdf') { } else if(doc.type === 'pdf') {
const canOpenAfter = appDocsManager.downloading.has(doc.id) || cacheContext.downloaded;
download = appDocsManager.downloadDoc(doc, queueId); download = appDocsManager.downloadDoc(doc, queueId);
if(canOpenAfter) {
download.then(() => { download.then(() => {
setTimeout(() => { // wait for preloader animation end setTimeout(() => { // wait for preloader animation end
const url = appDownloadManager.getCacheContext(doc).url; const url = appDownloadManager.getCacheContext(doc).url;
window.open(url); window.open(url);
}, rootScope.settings.animationsEnabled ? 250 : 0); }, rootScope.settings.animationsEnabled ? 250 : 0);
}); });
}
} else if(MEDIA_MIME_TYPES_SUPPORTED.has(doc.mime_type) && doc.thumbs?.length) { } else if(MEDIA_MIME_TYPES_SUPPORTED.has(doc.mime_type) && doc.thumbs?.length) {
download = appDocsManager.downloadDoc(doc, queueId); download = appDocsManager.downloadDoc(doc, queueId);
} else { } else {
@ -726,6 +732,10 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
preloader.setManual(); preloader.setManual();
preloader.attach(downloadDiv); preloader.attach(downloadDiv);
preloader.setDownloadFunction(load); preloader.setDownloadFunction(load);
if(autoDownloadSize !== undefined && autoDownloadSize >= doc.size) {
simulateClickEvent(preloader.preloader);
}
} else { } else {
preloader.attach(downloadDiv); preloader.attach(downloadDiv);
message.media.promise.then(onLoad); message.media.promise.then(onLoad);
@ -802,7 +812,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
return img; return img;
} */ } */
export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware, size, withoutPreloader, loadPromises, noAutoDownload, noBlur, noThumb, noFadeIn, blurAfter}: { export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware, size, withoutPreloader, loadPromises, autoDownloadSize, noBlur, noThumb, noFadeIn, blurAfter}: {
photo: MyPhoto | MyDocument, photo: MyPhoto | MyDocument,
message?: any, message?: any,
container: HTMLElement, container: HTMLElement,
@ -815,7 +825,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
size?: PhotoSize, size?: PhotoSize,
withoutPreloader?: boolean, withoutPreloader?: boolean,
loadPromises?: Promise<any>[], loadPromises?: Promise<any>[],
noAutoDownload?: boolean, autoDownloadSize?: number,
noBlur?: boolean, noBlur?: boolean,
noThumb?: boolean, noThumb?: boolean,
noFadeIn?: boolean, noFadeIn?: boolean,
@ -840,6 +850,8 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
}; };
} }
let noAutoDownload = autoDownloadSize === 0;
if(!size) { if(!size) {
if(boxWidth === undefined) boxWidth = mediaSizes.active.regular.width; if(boxWidth === undefined) boxWidth = mediaSizes.active.regular.width;
if(boxHeight === undefined) boxHeight = mediaSizes.active.regular.height; if(boxHeight === undefined) boxHeight = mediaSizes.active.regular.height;
@ -897,7 +909,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
middleware, middleware,
withoutPreloader, withoutPreloader,
withTail, withTail,
noAutoDownload, autoDownloadSize,
noBlur, noBlur,
noThumb: true, noThumb: true,
blurAfter: true blurAfter: true
@ -1895,7 +1907,7 @@ export function prepareAlbum(options: {
} */ } */
} }
export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut, chat, loadPromises, noAutoDownload}: { export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut, chat, loadPromises, autoDownload}: {
groupId: string, groupId: string,
attachmentDiv: HTMLElement, attachmentDiv: HTMLElement,
middleware?: () => boolean, middleware?: () => boolean,
@ -1904,7 +1916,7 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo
isOut: boolean, isOut: boolean,
chat: Chat, chat: Chat,
loadPromises?: Promise<any>[], loadPromises?: Promise<any>[],
noAutoDownload?: boolean, autoDownload?: ChatAutoDownloadSettings,
}) { }) {
const items: {size: PhotoSize.photoSize, media: any, message: any}[] = []; const items: {size: PhotoSize.photoSize, media: any, message: any}[] = [];
@ -1939,7 +1951,9 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo
div.dataset.mid = '' + message.mid; div.dataset.mid = '' + message.mid;
div.dataset.peerId = '' + message.peerId; div.dataset.peerId = '' + message.peerId;
const mediaDiv = div.firstElementChild as HTMLElement; const mediaDiv = div.firstElementChild as HTMLElement;
if(media._ === 'photo') { const isPhoto = media._ === 'photo';
const autoDownloadSize = autoDownload ? autoDownload[isPhoto ? 'photo' : 'video'] : undefined;
if(isPhoto) {
wrapPhoto({ wrapPhoto({
photo: media, photo: media,
message, message,
@ -1951,7 +1965,7 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo
middleware, middleware,
size, size,
loadPromises, loadPromises,
noAutoDownload autoDownloadSize
}); });
} else { } else {
wrapVideo({ wrapVideo({
@ -1965,13 +1979,13 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo
lazyLoadQueue, lazyLoadQueue,
middleware, middleware,
loadPromises, loadPromises,
noAutoDownload autoDownloadSize
}); });
} }
}); });
} }
export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat, loadPromises, noAutoDownload, lazyLoadQueue, searchContext, useSearch}: { export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat, loadPromises, autoDownloadSize, lazyLoadQueue, searchContext, useSearch}: {
albumMustBeRenderedFull: boolean, albumMustBeRenderedFull: boolean,
message: any, message: any,
messageDiv: HTMLElement, messageDiv: HTMLElement,
@ -1979,7 +1993,7 @@ export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble,
uploading?: boolean, uploading?: boolean,
chat: Chat, chat: Chat,
loadPromises?: Promise<any>[], loadPromises?: Promise<any>[],
noAutoDownload?: boolean, autoDownloadSize?: number,
lazyLoadQueue?: LazyLoadQueue, lazyLoadQueue?: LazyLoadQueue,
searchContext?: MediaSearchContext, searchContext?: MediaSearchContext,
useSearch?: boolean, useSearch?: boolean,
@ -1995,7 +2009,7 @@ export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble,
const div = wrapDocument({ const div = wrapDocument({
message, message,
loadPromises, loadPromises,
noAutoDownload, autoDownloadSize,
lazyLoadQueue, lazyLoadQueue,
searchContext searchContext
}); });

44
src/helpers/autoDownload.ts

@ -0,0 +1,44 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { State } from "../lib/appManagers/appStateManager";
import rootScope from "../lib/rootScope";
export type ChatAutoDownloadSettings = {
photo: number,
video: number,
file: number
};
export default function getAutoDownloadSettingsByPeerId(peerId: PeerId): ChatAutoDownloadSettings {
let type: keyof State['settings']['autoDownload'];
let photoSizeMax = 0, videoSizeMax = 0, fileSizeMax = 0;
const settings = rootScope.settings;
if(!settings.autoDownloadNew.pFlags.disabled && peerId) {
if(peerId.isUser()) {
if(peerId.isContact()) {
type = 'contacts';
} else {
type = 'private';
}
} else if(peerId.isBroadcast()) {
type = 'channels';
} else {
type = 'groups';
}
if(settings.autoDownload.photo[type]) photoSizeMax = settings.autoDownloadNew.photo_size_max;
if(settings.autoDownload.video[type]) videoSizeMax = settings.autoDownloadNew.video_size_max;
if(settings.autoDownload.file[type]) fileSizeMax = settings.autoDownloadNew.file_size_max;
}
return {
photo: photoSizeMax,
video: videoSizeMax,
file: fileSizeMax
};
}

19
src/helpers/formatBytes.ts

@ -0,0 +1,19 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { i18n, LangPackKey } from "../lib/langPack";
export default function formatBytes(bytes: number, decimals = 2) {
if(bytes === 0) return i18n('FileSize.B', [0]);
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes: LangPackKey[] = ['FileSize.B', 'FileSize.KB', 'FileSize.MB', 'FileSize.GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return i18n(sizes[i], [parseFloat((bytes / Math.pow(k, i)).toFixed(dm))]);
}

12
src/helpers/number.ts

@ -10,18 +10,6 @@ export function numberThousandSplitter(x: number, joiner = ' ') {
return parts.join("."); return parts.join(".");
} }
export function formatBytes(bytes: number, decimals = 2) {
if(bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
export function formatNumber(bytes: number, decimals = 2) { export function formatNumber(bytes: number, decimals = 2) {
if(bytes === 0) return '0'; if(bytes === 0) return '0';

37
src/lang.ts

@ -13,8 +13,6 @@ const lang = {
"FilterAllNonContacts": "All Non-Contacts", "FilterAllNonContacts": "All Non-Contacts",
"FilterAllChannels": "All Channels", "FilterAllChannels": "All Channels",
"FilterAllBots": "All Bots", "FilterAllBots": "All Bots",
"WordDelimiter": ", ",
"WordDelimiterLast": " and ",
"EditContact.OriginalName": "original name", "EditContact.OriginalName": "original name",
"EditProfile.FirstNameLabel": "Name", "EditProfile.FirstNameLabel": "Name",
"EditProfile.BioLabel": "Bio (optional)", "EditProfile.BioLabel": "Bio (optional)",
@ -66,7 +64,6 @@ const lang = {
"General.SendShortcut.CtrlEnter": "Send by %s + Enter", "General.SendShortcut.CtrlEnter": "Send by %s + Enter",
"General.SendShortcut.NewLine.ShiftEnter": "New line by Shift + Enter", "General.SendShortcut.NewLine.ShiftEnter": "New line by Shift + Enter",
"General.SendShortcut.NewLine.Enter": "New line by Enter", "General.SendShortcut.NewLine.Enter": "New line by Enter",
"General.AutoplayMedia": "Auto-Play Media",
"General.TimeFormat": "Time Format", "General.TimeFormat": "Time Format",
"General.TimeFormat.h12": "12-hour", "General.TimeFormat.h12": "12-hour",
"General.TimeFormat.h23": "24-hour", "General.TimeFormat.h23": "24-hour",
@ -652,6 +649,34 @@ const lang = {
"other_value": "%1$d Seen" "other_value": "%1$d Seen"
}, },
// "Close": "Close", // "Close": "Close",
"DataSettings": "Data and Storage",
"GroupsAndChannelsHelp": "Change who can add you to groups and channels.",
"SessionsInfo": "Control your sessions on other devices.",
"StickersBotInfo": "Artists are welcome to add their own sticker sets using our @stickers bot.",
"AutomaticMediaDownload": "Automatic media download",
"AutoDownloadPhotos": "Photos",
"AutoDownloadVideos": "Videos",
"AutoDownloadFiles": "Files",
"AutoDownloadOnAllChats": "On in all chats",
"AutoDownloadUpToOnAllChats": "Up to %1$s in all chats",
"AutoDownloadOff": "Off",
"AutoDownloadOnUpToFor": "Up to %1$s for %2$s",
"AutoDownloadOnFor": "On for %1$s",
"AutoDownloadContacts": "Contacts",
"AutoDownloadPm": "PM",
"AutoDownloadGroups": "Groups",
"AutoDownloadChannels": "Channels",
"AutoDownloadAudioInfo": "Voice messages are tiny, so they\'re always downloaded automatically.",
"AutoplayMedia": "Auto-play media",
"AutoDownloadPhotosTitle": "Auto-download photos",
"AutoDownloadVideosTitle": "Auto-download videos and GIFs",
"AutoDownloadFilesTitle": "Auto-download files and music",
"AutoDownloadMaxFileSize": "Maximum file size",
"AutodownloadSizeLimitUpTo": "up to %1$s",
"ResetAutomaticMediaDownload": "Reset Auto-Download Settings",
"ResetAutomaticMediaDownloadAlertTitle": "Reset settings",
"ResetAutomaticMediaDownloadAlert": "Are you sure you want to reset auto-download settings?",
"Reset": "Reset",
// * macos // * macos
"AccountSettings.Filters": "Chat Folders", "AccountSettings.Filters": "Chat Folders",
@ -661,6 +686,8 @@ const lang = {
"Alert.UserDoesntExists": "Sorry, this user doesn't seem to exist.", "Alert.UserDoesntExists": "Sorry, this user doesn't seem to exist.",
"Alert.Confirm.Discard": "Discard", "Alert.Confirm.Discard": "Discard",
"Appearance.Reset": "Reset to Defaults", "Appearance.Reset": "Reset to Defaults",
"AutoDownloadSettings.Delimeter": ", ",
"AutoDownloadSettings.LastDelimeter": " and ",
"Bio.Description": "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco", "Bio.Description": "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco",
"Call.Accept": "Accept", "Call.Accept": "Accept",
"Call.Decline": "Decline", "Call.Decline": "Decline",
@ -853,6 +880,10 @@ const lang = {
"Emoji.Objects": "Objects", "Emoji.Objects": "Objects",
//"Emoji.Symbols": "Symbols", //"Emoji.Symbols": "Symbols",
"Emoji.Flags": "Flags", "Emoji.Flags": "Flags",
"FileSize.B": "%@ B",
"FileSize.KB": "%@ KB",
"FileSize.MB": "%@ MB",
"FileSize.GB": "%@ GB",
"InstalledStickers.LoopAnimated": "Loop Animated Stickers", "InstalledStickers.LoopAnimated": "Loop Animated Stickers",
"LastSeen.HoursAgo": { "LastSeen.HoursAgo": {
"one_value": "last seen %d hour ago", "one_value": "last seen %d hour ago",

50
src/lib/appManagers/appStateManager.ts

@ -18,7 +18,7 @@ import { copy, setDeepProperty, validateInitObject } from '../../helpers/object'
import App from '../../config/app'; import App from '../../config/app';
import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug'; import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug';
import AppStorage from '../storage'; import AppStorage from '../storage';
import { Chat } from '../../layer'; import { AutoDownloadSettings, Chat } from '../../layer';
import { IS_MOBILE } from '../../environment/userAgent'; import { IS_MOBILE } from '../../environment/userAgent';
import DATABASE_STATE from '../../config/databases/state'; import DATABASE_STATE from '../../config/databases/state';
import sessionStorage from '../sessionStorage'; import sessionStorage from '../sessionStorage';
@ -45,6 +45,13 @@ export type Theme = {
background: Background background: Background
}; };
export type AutoDownloadPeerTypeSettings = {
contacts: boolean,
private: boolean,
groups: boolean,
channels: boolean
};
export type State = { export type State = {
allDialogsLoaded: DialogsStorage['allDialogsLoaded'], allDialogsLoaded: DialogsStorage['allDialogsLoaded'],
pinnedOrders: DialogsStorage['pinnedOrders'], pinnedOrders: DialogsStorage['pinnedOrders'],
@ -75,11 +82,15 @@ export type State = {
sendShortcut: 'enter' | 'ctrlEnter', sendShortcut: 'enter' | 'ctrlEnter',
animationsEnabled: boolean, animationsEnabled: boolean,
autoDownload: { autoDownload: {
contacts: boolean contacts: boolean,
private: boolean private: boolean,
groups: boolean groups: boolean,
channels: boolean channels: boolean,
photo: AutoDownloadPeerTypeSettings,
video: AutoDownloadPeerTypeSettings,
file: AutoDownloadPeerTypeSettings
}, },
autoDownloadNew: AutoDownloadSettings,
autoPlay: { autoPlay: {
gifs: boolean, gifs: boolean,
videos: boolean videos: boolean
@ -129,10 +140,39 @@ export const STATE_INIT: State = {
sendShortcut: 'enter', sendShortcut: 'enter',
animationsEnabled: true, animationsEnabled: true,
autoDownload: { autoDownload: {
contacts: true,
private: true,
groups: true,
channels: true,
photo: {
contacts: true,
private: true,
groups: true,
channels: true
},
video: {
contacts: true,
private: true,
groups: true,
channels: true
},
file: {
contacts: true, contacts: true,
private: true, private: true,
groups: true, groups: true,
channels: true channels: true
}
},
autoDownloadNew: {
_: 'autoDownloadSettings',
file_size_max: 3145728,
pFlags: {
video_preload_large: true,
audio_preload_next: true
},
photo_size_max: 1048576,
video_size_max: 15728640,
video_upload_maxbitrate: 100
}, },
autoPlay: { autoPlay: {
gifs: true, gifs: true,

2
src/lib/langPack.ts

@ -528,7 +528,7 @@ export function join(elements: (Node | string)[], useLast?: boolean, plain?: fal
export function join(elements: (Node | string)[], useLast: boolean, plain: boolean): string | (string | Node)[]; export function join(elements: (Node | string)[], useLast: boolean, plain: boolean): string | (string | Node)[];
export function join(elements: (Node | string)[], useLast = true, plain?: boolean): string | (string | Node)[] { export function join(elements: (Node | string)[], useLast = true, plain?: boolean): string | (string | Node)[] {
const joined = joinElementsWith(elements, (isLast) => { const joined = joinElementsWith(elements, (isLast) => {
const langPackKey: LangPackKey = isLast && useLast ? 'WordDelimiterLast' : 'WordDelimiter'; const langPackKey: LangPackKey = isLast && useLast ? 'AutoDownloadSettings.LastDelimeter' : 'AutoDownloadSettings.Delimeter';
return plain ? I18n.format(langPackKey, true) : i18n(langPackKey); return plain ? I18n.format(langPackKey, true) : i18n(langPackKey);
}); });

4
src/scss/partials/_button.scss

@ -561,6 +561,10 @@
@include hover-background-effect(red); @include hover-background-effect(red);
} }
&.primary {
@include hover-background-effect(primary);
}
// * tgico // * tgico
&:before { &:before {
color: var(--secondary-text-color); color: var(--secondary-text-color);

9
src/scss/partials/_row.scss

@ -12,6 +12,15 @@
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
@include animation-level(2) {
transition: opacity var(--transition-standard-in);
}
&.is-disabled {
pointer-events: none !important;
opacity: var(--disabled-opacity);
}
a { a {
position: relative; position: relative;
z-index: 1; z-index: 1;

Loading…
Cancel
Save