From 7b2f1a015e48746209d3bf1edd3d5d6ab99db83c Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Tue, 15 Feb 2022 20:17:51 +0400 Subject: [PATCH 1/4] Better auto download settings --- src/components/appSearchSuper..ts | 2 +- src/components/audio.ts | 8 +- src/components/chat/bubbles.ts | 16 +- src/components/chat/chat.ts | 27 +-- .../sidebarLeft/tabs/autoDownload/file.ts | 53 +++++ .../sidebarLeft/tabs/autoDownload/photo.ts | 59 ++++++ .../sidebarLeft/tabs/autoDownload/video.ts | 18 ++ .../sidebarLeft/tabs/dataAndStorage.ts | 196 ++++++++++++++++++ .../sidebarLeft/tabs/generalSettings.ts | 86 +++----- .../sidebarLeft/tabs/privacyAndSecurity.ts | 14 +- src/components/sidebarLeft/tabs/settings.ts | 21 +- src/components/sliderTab.ts | 4 + src/components/wrappers.ts | 66 +++--- src/helpers/autoDownload.ts | 44 ++++ src/helpers/formatBytes.ts | 19 ++ src/helpers/number.ts | 12 -- src/lang.ts | 37 +++- src/lib/appManagers/appStateManager.ts | 52 ++++- src/lib/langPack.ts | 2 +- src/scss/partials/_button.scss | 4 + src/scss/partials/_row.scss | 9 + 21 files changed, 596 insertions(+), 153 deletions(-) create mode 100644 src/components/sidebarLeft/tabs/autoDownload/file.ts create mode 100644 src/components/sidebarLeft/tabs/autoDownload/photo.ts create mode 100644 src/components/sidebarLeft/tabs/autoDownload/video.ts create mode 100644 src/components/sidebarLeft/tabs/dataAndStorage.ts create mode 100644 src/helpers/autoDownload.ts create mode 100644 src/helpers/formatBytes.ts diff --git a/src/components/appSearchSuper..ts b/src/components/appSearchSuper..ts index f8d91e5c..7fc72293 100644 --- a/src/components/appSearchSuper..ts +++ b/src/components/appSearchSuper..ts @@ -734,7 +734,7 @@ export default class AppSearchSuper { showSender, searchContext: this.copySearchContext(inputFilter), lazyLoadQueue: this.lazyLoadQueue, - noAutoDownload: true + autoDownloadSize: 0 }); if((['audio', 'voice', 'round'] as MyDocument['type'][]).includes(document.type)) { diff --git a/src/components/audio.ts b/src/components/audio.ts index 56a75efa..b74c8a96 100644 --- a/src/components/audio.ts +++ b/src/components/audio.ts @@ -26,9 +26,10 @@ import { joinElementsWith } from "../lib/langPack"; import { MiddleEllipsisElement } from "./middleEllipsis"; import htmlToSpan from "../helpers/dom/htmlToSpan"; import { formatFullSentTime } from "../helpers/date"; -import { clamp, formatBytes } from "../helpers/number"; import throttleWithRaf from "../helpers/schedulers/throttleWithRaf"; 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}) => { mids.forEach(mid => { @@ -523,7 +524,8 @@ export default class AudioElement extends HTMLElement { if(!isOutgoing) { let preloader: ProgressivePreloader = this.preloader; - onLoad(doc.type !== 'audio' && !this.noAutoDownload); + const autoDownload = doc.type !== 'audio'/* || !this.noAutoDownload */; + onLoad(autoDownload); const r = (shouldPlay: boolean) => { if(this.audio.src) { @@ -634,7 +636,7 @@ export default class AudioElement extends HTMLElement { }; if(!this.audio?.src) { - if(doc.type !== 'audio' && !this.noAutoDownload) { + if(autoDownload) { r(false); } else { attachClickEvent(toggle, () => { diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index c5b4e29e..5b00fc1c 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -3102,7 +3102,7 @@ export default class ChatBubbles { lazyLoadQueue: this.lazyLoadQueue, chat: this.chat, loadPromises, - noAutoDownload: this.chat.noAutoDownloadMedia, + autoDownload: this.chat.autoDownload, }); break; @@ -3119,7 +3119,7 @@ export default class ChatBubbles { lazyLoadQueue: this.lazyLoadQueue, middleware: this.getMiddleware(), loadPromises, - noAutoDownload: this.chat.noAutoDownloadMedia, + autoDownloadSize: this.chat.autoDownload.photo, }); break; @@ -3174,13 +3174,13 @@ export default class ChatBubbles { isOut, group: CHAT_ANIMATION_GROUP, loadPromises, - noAutoDownload: this.chat.noAutoDownloadMedia, + autoDownloadSize: this.chat.autoDownload.video, }); //} } else { const docDiv = wrapDocument({ message, - noAutoDownload: this.chat.noAutoDownloadMedia, + autoDownloadSize: this.chat.autoDownload.file, lazyLoadQueue: this.lazyLoadQueue, loadPromises }); @@ -3265,7 +3265,7 @@ export default class ChatBubbles { middleware: this.getMiddleware(), loadPromises, withoutPreloader: isSquare, - noAutoDownload: this.chat.noAutoDownloadMedia, + autoDownloadSize: this.chat.autoDownload.photo, }); } @@ -3346,7 +3346,7 @@ export default class ChatBubbles { lazyLoadQueue: this.lazyLoadQueue, chat: this.chat, loadPromises, - noAutoDownload: this.chat.noAutoDownloadMedia, + autoDownload: this.chat.autoDownload, }); } else { const withTail = !IS_ANDROID && !IS_APPLE && !isRound && canHaveTail && !withReplies && USE_MEDIA_TAILS; @@ -3363,7 +3363,7 @@ export default class ChatBubbles { middleware: this.getMiddleware(), group: CHAT_ANIMATION_GROUP, loadPromises, - noAutoDownload: this.chat.noAutoDownloadMedia, + autoDownloadSize: this.chat.autoDownload.video, searchContext: isRound ? { peerId: this.peerId, inputFilter: {_: 'inputMessagesFilterRoundVoice'}, @@ -3382,7 +3382,7 @@ export default class ChatBubbles { messageDiv, chat: this.chat, loadPromises, - noAutoDownload: this.chat.noAutoDownloadMedia, + autoDownloadSize: this.chat.autoDownload.file, lazyLoadQueue: this.lazyLoadQueue, searchContext: doc.type === 'voice' || doc.type === 'audio' ? { peerId: this.peerId, diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index 50dc76a5..13aaf2c7 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -42,6 +42,7 @@ import AppPrivateSearchTab from "../sidebarRight/tabs/search"; import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl"; import mediaSizes from "../../helpers/mediaSizes"; import ChatSearch from "./search"; +import getAutoDownloadSettingsByPeerId, { ChatAutoDownloadSettings } from "../../helpers/autoDownload"; export type ChatType = 'chat' | 'pinned' | 'replies' | 'discussion' | 'scheduled'; @@ -69,10 +70,11 @@ export default class Chat extends EventListenerBase<{ public type: ChatType; - public noAutoDownloadMedia: boolean; public noForwards: boolean; public inited: boolean; + + public autoDownload: ChatAutoDownloadSettings; constructor(public appImManager: AppImManager, public appChatsManager: AppChatsManager, @@ -346,28 +348,7 @@ export default class Chat extends EventListenerBase<{ } public setAutoDownloadMedia() { - const peerId = 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]; + this.autoDownload = getAutoDownloadSettingsByPeerId(this.peerId); } public setMessageId(messageId?: number) { diff --git a/src/components/sidebarLeft/tabs/autoDownload/file.ts b/src/components/sidebarLeft/tabs/autoDownload/file.ts new file mode 100644 index 00000000..d1650f7a --- /dev/null +++ b/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); + } +} diff --git a/src/components/sidebarLeft/tabs/autoDownload/photo.ts b/src/components/sidebarLeft/tabs/autoDownload/photo.ts new file mode 100644 index 00000000..2b2750ad --- /dev/null +++ b/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); + } +} diff --git a/src/components/sidebarLeft/tabs/autoDownload/video.ts b/src/components/sidebarLeft/tabs/autoDownload/video.ts new file mode 100644 index 00000000..bc13ac0b --- /dev/null +++ b/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); + } +} diff --git a/src/components/sidebarLeft/tabs/dataAndStorage.ts b/src/components/sidebarLeft/tabs/dataAndStorage.ts new file mode 100644 index 00000000..918559c0 --- /dev/null +++ b/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)); + } +} diff --git a/src/components/sidebarLeft/tabs/generalSettings.ts b/src/components/sidebarLeft/tabs/generalSettings.ts index 4cbe2a50..7d59073c 100644 --- a/src/components/sidebarLeft/tabs/generalSettings.ts +++ b/src/components/sidebarLeft/tabs/generalSettings.ts @@ -4,7 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { generateSection } from ".."; +import { generateSection, SettingSection } from ".."; import RangeSelector from "../../rangeSelector"; import Button from "../../button"; import CheckboxField from "../../checkboxField"; @@ -31,11 +31,19 @@ import AppQuickReactionTab from "./quickReaction"; export class RangeSettingSelector { public container: HTMLDivElement; + public valueContainer: HTMLElement; private range: RangeSelector; 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'; this.container = document.createElement('div'); this.container.classList.add(BASE_CLASS); @@ -47,9 +55,12 @@ export class RangeSettingSelector { nameDiv.classList.add(BASE_CLASS + '-name'); _i18n(nameDiv, name); - const valueDiv = document.createElement('div'); + const valueDiv = this.valueContainer = document.createElement('div'); valueDiv.classList.add(BASE_CLASS + '-value'); - valueDiv.innerHTML = '' + initialValue; + + if(writeValue) { + valueDiv.innerHTML = '' + initialValue; + } details.append(nameDiv, valueDiv); @@ -65,8 +76,10 @@ export class RangeSettingSelector { this.onChange(value); } - //console.log('font size scrub:', value); - valueDiv.innerText = '' + value; + if(writeValue) { + //console.log('font size scrub:', value); + valueDiv.innerText = '' + value; + } } }); @@ -213,58 +226,6 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable { 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'); @@ -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({ titleLangKey: 'DoubleTapSetting', @@ -324,6 +285,8 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable { const stickerSets: {[id: string]: Row} = {}; + const stickersContent = section.generateContentElement(); + const lazyLoadQueue = new LazyLoadQueue(); const renderStickerSet = (stickerSet: StickerSet.stickerSet, method: 'append' | 'prepend' = 'append') => { const row = new Row({ @@ -353,7 +316,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable { row.container.append(div); - container[method](row.container); + stickersContent[method](row.container); }; 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); } } diff --git a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts index c86b5e4f..c242a162 100644 --- a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts +++ b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts @@ -38,13 +38,14 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { private authorizations: Authorization.authorization[]; protected init() { + this.header.classList.add('with-border'); this.container.classList.add('dont-u-dare-block-me'); this.setTitle('PrivacySettings'); const SUBTITLE: LangPackKey = 'Loading'; { - const section = new SettingSection({noDelimiter: true}); + const section = new SettingSection({noDelimiter: true, caption: 'SessionsInfo'}); let blockedPeerIds: PeerId[]; 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'); @@ -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); for(const key in rowsByKeys) { diff --git a/src/components/sidebarLeft/tabs/settings.ts b/src/components/sidebarLeft/tabs/settings.ts index 70af4815..5dec2e2c 100644 --- a/src/components/sidebarLeft/tabs/settings.ts +++ b/src/components/sidebarLeft/tabs/settings.ts @@ -19,6 +19,7 @@ import PeerTitle from "../../peerTitle"; import AppLanguageTab from "./language"; import lottieLoader from "../../../lib/rlottie/lottieLoader"; import PopupPeer from "../../popups/peer"; +import AppDataAndStorageTab from "./dataAndStorage"; //import AppMediaViewer from "../../appMediaViewerNew"; export default class AppSettingsTab extends SliderSuperTab { @@ -31,6 +32,7 @@ export default class AppSettingsTab extends SliderSuperTab { folders: HTMLButtonElement, general: HTMLButtonElement, notifications: HTMLButtonElement, + storage: HTMLButtonElement, privacy: HTMLButtonElement, language: HTMLButtonElement } = {} as any; @@ -115,12 +117,15 @@ export default class AppSettingsTab extends SliderSuperTab { buttonsDiv.classList.add('profile-buttons'); const className = 'profile-button btn-primary btn-transparent'; - buttonsDiv.append(this.buttons.edit = Button(className, {icon: 'edit', text: 'EditAccount.Title'})); - buttonsDiv.append(this.buttons.folders = Button(className, {icon: 'folder', text: 'AccountSettings.Filters'})); - buttonsDiv.append(this.buttons.general = Button(className, {icon: 'settings', text: 'Telegram.GeneralSettingsViewController'})); - buttonsDiv.append(this.buttons.notifications = Button(className, {icon: 'unmute', text: 'AccountSettings.Notifications'})); - buttonsDiv.append(this.buttons.privacy = Button(className, {icon: 'lock', text: 'AccountSettings.PrivacyAndSecurity'})); - buttonsDiv.append(this.buttons.language = Button(className, {icon: 'language', text: 'AccountSettings.Language'})); + buttonsDiv.append( + this.buttons.edit = Button(className, {icon: 'edit', text: 'EditAccount.Title'}), + this.buttons.folders = Button(className, {icon: 'folder', text: 'AccountSettings.Filters'}), + this.buttons.general = Button(className, {icon: 'settings', text: 'Telegram.GeneralSettingsViewController'}), + this.buttons.storage = Button(className, {icon: 'data', text: 'DataSettings'}), + 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.container.classList.add('profile-content-wrapper'); @@ -142,6 +147,10 @@ export default class AppSettingsTab extends SliderSuperTab { new AppGeneralSettingsTab(this.slider).open(); }); + this.buttons.storage.addEventListener('click', () => { + new AppDataAndStorageTab(this.slider).open(); + }); + this.buttons.notifications.addEventListener('click', () => { new AppNotificationsTab(this.slider).open(); }); diff --git a/src/components/sliderTab.ts b/src/components/sliderTab.ts index a42adf27..0c686177 100644 --- a/src/components/sliderTab.ts +++ b/src/components/sliderTab.ts @@ -22,6 +22,10 @@ export interface SliderSuperTabConstructable { new(slider: SidebarSlider, destroyable: boolean): SliderSuperTab; } +export interface SliderSuperTabEventableConstructable { + new(slider: SidebarSlider, destroyable: boolean): SliderSuperTabEventable; +} + export default class SliderSuperTab implements SliderTab { public container: HTMLElement; diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 2ab0399c..58b0a931 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -9,7 +9,6 @@ import { getEmojiToneIndex } from '../vendor/emoji'; import { deferredPromise } from '../helpers/cancellablePromise'; import { formatFullSentTime } from '../helpers/date'; import mediaSizes, { ScreenSize } from '../helpers/mediaSizes'; -import { formatBytes } from '../helpers/number'; import { IS_SAFARI } from '../environment/userAgent'; import { Message, PhotoSize, StickerSet } from '../layer'; import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager"; @@ -56,6 +55,8 @@ import throttle from '../helpers/schedulers/throttle'; import { SendMessageEmojiInteractionData } from '../types'; import IS_VIBRATE_SUPPORTED from '../environment/vibrateSupport'; import Row from './row'; +import { ChatAutoDownloadSettings } from '../helpers/autoDownload'; +import formatBytes from '../helpers/formatBytes'; 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, container?: HTMLElement, message?: Message.message, @@ -98,10 +99,11 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai onlyPreview?: boolean, withoutPreloader?: boolean, loadPromises?: Promise[], - noAutoDownload?: boolean, + autoDownloadSize?: number, size?: PhotoSize, searchContext?: MediaSearchContext, }) { + let noAutoDownload = autoDownloadSize === 0; const isAlbumItem = !(boxWidth && boxHeight); const canAutoplay = /* doc.sticker || */( ( @@ -163,7 +165,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai middleware, withoutPreloader, loadPromises, - noAutoDownload, + autoDownloadSize, size }); @@ -371,7 +373,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai middleware, withoutPreloader: true, loadPromises, - noAutoDownload, + autoDownloadSize, 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, withTime?: boolean, fontWeight?: number, @@ -558,10 +560,11 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS showSender?: boolean, searchContext?: MediaSearchContext, loadPromises?: Promise[], - noAutoDownload?: boolean, + autoDownloadSize?: number, lazyLoadQueue?: LazyLoadQueue }): HTMLElement { if(!fontWeight) fontWeight = 500; + const noAutoDownload = autoDownloadSize === 0; const doc = (message.media.document || message.media.webpage.document) as MyDocument; 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 doc = appDocsManager.getDoc(docDiv.dataset.docId); let download: DownloadBlob; @@ -690,13 +693,16 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS if(!save) { download = appDocsManager.downloadDoc(doc, queueId); } else if(doc.type === 'pdf') { + const canOpenAfter = appDocsManager.downloading.has(doc.id) || cacheContext.downloaded; download = appDocsManager.downloadDoc(doc, queueId); - download.then(() => { - setTimeout(() => { // wait for preloader animation end - const url = appDownloadManager.getCacheContext(doc).url; - window.open(url); - }, rootScope.settings.animationsEnabled ? 250 : 0); - }); + if(canOpenAfter) { + download.then(() => { + setTimeout(() => { // wait for preloader animation end + const url = appDownloadManager.getCacheContext(doc).url; + window.open(url); + }, rootScope.settings.animationsEnabled ? 250 : 0); + }); + } } else if(MEDIA_MIME_TYPES_SUPPORTED.has(doc.mime_type) && doc.thumbs?.length) { download = appDocsManager.downloadDoc(doc, queueId); } else { @@ -726,6 +732,10 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS preloader.setManual(); preloader.attach(downloadDiv); preloader.setDownloadFunction(load); + + if(autoDownloadSize !== undefined && autoDownloadSize >= doc.size) { + simulateClickEvent(preloader.preloader); + } } else { preloader.attach(downloadDiv); message.media.promise.then(onLoad); @@ -739,7 +749,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS load(e); } }); - + return docDiv; } @@ -802,7 +812,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS 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, message?: any, container: HTMLElement, @@ -815,7 +825,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT size?: PhotoSize, withoutPreloader?: boolean, loadPromises?: Promise[], - noAutoDownload?: boolean, + autoDownloadSize?: number, noBlur?: boolean, noThumb?: boolean, noFadeIn?: boolean, @@ -840,6 +850,8 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT }; } + let noAutoDownload = autoDownloadSize === 0; + if(!size) { if(boxWidth === undefined) boxWidth = mediaSizes.active.regular.width; if(boxHeight === undefined) boxHeight = mediaSizes.active.regular.height; @@ -897,7 +909,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT middleware, withoutPreloader, withTail, - noAutoDownload, + autoDownloadSize, noBlur, noThumb: 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, attachmentDiv: HTMLElement, middleware?: () => boolean, @@ -1904,7 +1916,7 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo isOut: boolean, chat: Chat, loadPromises?: Promise[], - noAutoDownload?: boolean, + autoDownload?: ChatAutoDownloadSettings, }) { 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.peerId = '' + message.peerId; const mediaDiv = div.firstElementChild as HTMLElement; - if(media._ === 'photo') { + const isPhoto = media._ === 'photo'; + const autoDownloadSize = autoDownload ? autoDownload[isPhoto ? 'photo' : 'video'] : undefined; + if(isPhoto) { wrapPhoto({ photo: media, message, @@ -1951,7 +1965,7 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo middleware, size, loadPromises, - noAutoDownload + autoDownloadSize }); } else { wrapVideo({ @@ -1965,13 +1979,13 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo lazyLoadQueue, middleware, 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, message: any, messageDiv: HTMLElement, @@ -1979,7 +1993,7 @@ export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, uploading?: boolean, chat: Chat, loadPromises?: Promise[], - noAutoDownload?: boolean, + autoDownloadSize?: number, lazyLoadQueue?: LazyLoadQueue, searchContext?: MediaSearchContext, useSearch?: boolean, @@ -1995,7 +2009,7 @@ export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, const div = wrapDocument({ message, loadPromises, - noAutoDownload, + autoDownloadSize, lazyLoadQueue, searchContext }); diff --git a/src/helpers/autoDownload.ts b/src/helpers/autoDownload.ts new file mode 100644 index 00000000..8042a131 --- /dev/null +++ b/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 + }; +} diff --git a/src/helpers/formatBytes.ts b/src/helpers/formatBytes.ts new file mode 100644 index 00000000..5eabee6a --- /dev/null +++ b/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))]); +} diff --git a/src/helpers/number.ts b/src/helpers/number.ts index 1625051b..aa1b7229 100644 --- a/src/helpers/number.ts +++ b/src/helpers/number.ts @@ -10,18 +10,6 @@ export function numberThousandSplitter(x: number, joiner = ' ') { 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) { if(bytes === 0) return '0'; diff --git a/src/lang.ts b/src/lang.ts index ba55134a..8908acbb 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -13,8 +13,6 @@ const lang = { "FilterAllNonContacts": "All Non-Contacts", "FilterAllChannels": "All Channels", "FilterAllBots": "All Bots", - "WordDelimiter": ", ", - "WordDelimiterLast": " and ", "EditContact.OriginalName": "original name", "EditProfile.FirstNameLabel": "Name", "EditProfile.BioLabel": "Bio (optional)", @@ -66,7 +64,6 @@ const lang = { "General.SendShortcut.CtrlEnter": "Send by %s + Enter", "General.SendShortcut.NewLine.ShiftEnter": "New line by Shift + Enter", "General.SendShortcut.NewLine.Enter": "New line by Enter", - "General.AutoplayMedia": "Auto-Play Media", "General.TimeFormat": "Time Format", "General.TimeFormat.h12": "12-hour", "General.TimeFormat.h23": "24-hour", @@ -652,6 +649,34 @@ const lang = { "other_value": "%1$d Seen" }, // "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 "AccountSettings.Filters": "Chat Folders", @@ -661,6 +686,8 @@ const lang = { "Alert.UserDoesntExists": "Sorry, this user doesn't seem to exist.", "Alert.Confirm.Discard": "Discard", "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", "Call.Accept": "Accept", "Call.Decline": "Decline", @@ -853,6 +880,10 @@ const lang = { "Emoji.Objects": "Objects", //"Emoji.Symbols": "Symbols", "Emoji.Flags": "Flags", + "FileSize.B": "%@ B", + "FileSize.KB": "%@ KB", + "FileSize.MB": "%@ MB", + "FileSize.GB": "%@ GB", "InstalledStickers.LoopAnimated": "Loop Animated Stickers", "LastSeen.HoursAgo": { "one_value": "last seen %d hour ago", diff --git a/src/lib/appManagers/appStateManager.ts b/src/lib/appManagers/appStateManager.ts index 2d17c112..fc307ca9 100644 --- a/src/lib/appManagers/appStateManager.ts +++ b/src/lib/appManagers/appStateManager.ts @@ -18,7 +18,7 @@ import { copy, setDeepProperty, validateInitObject } from '../../helpers/object' import App from '../../config/app'; import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug'; import AppStorage from '../storage'; -import { Chat } from '../../layer'; +import { AutoDownloadSettings, Chat } from '../../layer'; import { IS_MOBILE } from '../../environment/userAgent'; import DATABASE_STATE from '../../config/databases/state'; import sessionStorage from '../sessionStorage'; @@ -45,6 +45,13 @@ export type Theme = { background: Background }; +export type AutoDownloadPeerTypeSettings = { + contacts: boolean, + private: boolean, + groups: boolean, + channels: boolean +}; + export type State = { allDialogsLoaded: DialogsStorage['allDialogsLoaded'], pinnedOrders: DialogsStorage['pinnedOrders'], @@ -75,11 +82,15 @@ export type State = { sendShortcut: 'enter' | 'ctrlEnter', animationsEnabled: boolean, autoDownload: { - contacts: boolean - private: boolean - groups: boolean - channels: boolean + contacts: boolean, + private: boolean, + groups: boolean, + channels: boolean, + photo: AutoDownloadPeerTypeSettings, + video: AutoDownloadPeerTypeSettings, + file: AutoDownloadPeerTypeSettings }, + autoDownloadNew: AutoDownloadSettings, autoPlay: { gifs: boolean, videos: boolean @@ -132,7 +143,36 @@ export const STATE_INIT: State = { contacts: true, private: true, groups: true, - channels: true + channels: true, + photo: { + contacts: true, + private: true, + groups: true, + channels: true + }, + video: { + contacts: true, + private: true, + groups: true, + channels: true + }, + file: { + contacts: true, + private: true, + groups: 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: { gifs: true, diff --git a/src/lib/langPack.ts b/src/lib/langPack.ts index 8cb421ae..966f3816 100644 --- a/src/lib/langPack.ts +++ b/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 = true, plain?: boolean): string | (string | Node)[] { 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); }); diff --git a/src/scss/partials/_button.scss b/src/scss/partials/_button.scss index 0f99b528..3e1390a6 100644 --- a/src/scss/partials/_button.scss +++ b/src/scss/partials/_button.scss @@ -560,6 +560,10 @@ &.danger { @include hover-background-effect(red); } + + &.primary { + @include hover-background-effect(primary); + } // * tgico &:before { diff --git a/src/scss/partials/_row.scss b/src/scss/partials/_row.scss index 57fbbda7..23d9a9c7 100644 --- a/src/scss/partials/_row.scss +++ b/src/scss/partials/_row.scss @@ -12,6 +12,15 @@ flex-direction: column; justify-content: center; + @include animation-level(2) { + transition: opacity var(--transition-standard-in); + } + + &.is-disabled { + pointer-events: none !important; + opacity: var(--disabled-opacity); + } + a { position: relative; z-index: 1; From 86ab42f871c059d91f3363fe0055055afc601026 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Tue, 15 Feb 2022 22:27:04 +0400 Subject: [PATCH 2/4] Return reply hovers back --- src/scss/partials/_chatBubble.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 818bab63..04221926 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -1109,6 +1109,10 @@ $bubble-beside-button-width: 38px; min-width: 10rem; width: auto; + @include hover() { + background-color: var(--light-filled-message-primary-color); + } + &-media { top: .125rem; } From 55c9f42c4814456372cb202195a75ae9f1143a17 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Wed, 16 Feb 2022 17:38:25 +0400 Subject: [PATCH 3/4] Fix extra import of api manager --- src/lib/calls/groupCallConnectionInstance.ts | 2 +- src/lib/calls/groupCallInstance.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/calls/groupCallConnectionInstance.ts b/src/lib/calls/groupCallConnectionInstance.ts index 7ddd6c3a..8e9eaa3a 100644 --- a/src/lib/calls/groupCallConnectionInstance.ts +++ b/src/lib/calls/groupCallConnectionInstance.ts @@ -9,7 +9,7 @@ import throttle from "../../helpers/schedulers/throttle"; import { Updates, PhoneJoinGroupCall, PhoneJoinGroupCallPresentation, Update } from "../../layer"; import apiUpdatesManager from "../appManagers/apiUpdatesManager"; import appGroupCallsManager, { GroupCallConnectionType, JoinGroupCallJsonPayload } from "../appManagers/appGroupCallsManager"; -import apiManager from "../mtproto/apiManager"; +import apiManager from "../mtproto/mtprotoworker"; import rootScope from "../rootScope"; import CallConnectionInstanceBase, { CallConnectionInstanceOptions } from "./callConnectionInstanceBase"; import GroupCallInstance from "./groupCallInstance"; diff --git a/src/lib/calls/groupCallInstance.ts b/src/lib/calls/groupCallInstance.ts index b631e817..cc7c4732 100644 --- a/src/lib/calls/groupCallInstance.ts +++ b/src/lib/calls/groupCallInstance.ts @@ -13,7 +13,7 @@ import apiUpdatesManager from "../appManagers/apiUpdatesManager"; import appGroupCallsManager, { GroupCallConnectionType, GroupCallId, GroupCallOutputSource } from "../appManagers/appGroupCallsManager"; import appPeersManager from "../appManagers/appPeersManager"; import { logger } from "../logger"; -import apiManager from "../mtproto/apiManager"; +import apiManager from "../mtproto/mtprotoworker"; import { NULL_PEER_ID } from "../mtproto/mtproto_config"; import rootScope from "../rootScope"; import CallInstanceBase, { TryAddTrackOptions } from "./callInstanceBase"; From 7955e20a782d8627f1f8d224251e8c5bd6f74b75 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Wed, 16 Feb 2022 19:00:49 +0400 Subject: [PATCH 4/4] Migrate deprecated auto download settings --- src/lib/appManagers/appStateManager.ts | 42 +++++++++++++++++++++----- src/lib/mtproto/apiFileManager.ts | 2 +- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/lib/appManagers/appStateManager.ts b/src/lib/appManagers/appStateManager.ts index fc307ca9..3d05a138 100644 --- a/src/lib/appManagers/appStateManager.ts +++ b/src/lib/appManagers/appStateManager.ts @@ -82,10 +82,10 @@ export type State = { sendShortcut: 'enter' | 'ctrlEnter', animationsEnabled: boolean, autoDownload: { - contacts: boolean, - private: boolean, - groups: boolean, - channels: boolean, + contacts?: boolean, // ! DEPRECATED + private?: boolean, // ! DEPRECATED + groups?: boolean, // ! DEPRECATED + channels?: boolean, // ! DEPRECATED photo: AutoDownloadPeerTypeSettings, video: AutoDownloadPeerTypeSettings, file: AutoDownloadPeerTypeSettings @@ -140,10 +140,6 @@ export const STATE_INIT: State = { sendShortcut: 'enter', animationsEnabled: true, autoDownload: { - contacts: true, - private: true, - groups: true, - channels: true, photo: { contacts: true, private: true, @@ -463,6 +459,36 @@ export class AppStateManager extends EventListenerBase<{ } } + // * migrate auto download settings + const autoDownloadSettings = state.settings.autoDownload; + if(autoDownloadSettings?.private !== undefined) { + const oldTypes = [ + 'contacts' as const, + 'private' as const, + 'groups' as const, + 'channels' as const + ]; + + const mediaTypes = [ + 'photo' as const, + 'video' as const, + 'file' as const + ]; + + mediaTypes.forEach(mediaType => { + const peerTypeSettings: AutoDownloadPeerTypeSettings = autoDownloadSettings[mediaType] = {} as any; + oldTypes.forEach(peerType => { + peerTypeSettings[peerType] = autoDownloadSettings[peerType]; + }); + }); + + oldTypes.forEach(peerType => { + delete autoDownloadSettings[peerType]; + }); + + this.pushToState('settings', state.settings); + } + validateInitObject(STATE_INIT, state, (missingKey) => { // @ts-ignore this.pushToState(missingKey, state[missingKey]); diff --git a/src/lib/mtproto/apiFileManager.ts b/src/lib/mtproto/apiFileManager.ts index 40dcd62b..13424942 100644 --- a/src/lib/mtproto/apiFileManager.ts +++ b/src/lib/mtproto/apiFileManager.ts @@ -63,7 +63,7 @@ export interface RefreshReferenceTaskResponse extends WorkerTaskVoidTemplate { originalPayload: ReferenceBytes }; -const MAX_FILE_SAVE_SIZE = 20e6; +const MAX_FILE_SAVE_SIZE = 20 * 1024 * 1024; export class ApiFileManager { private cacheStorage = new CacheStorageController('cachedFiles');