Merge tag 'v4.16.0' into dev

# Conflicts:
#	README.md
#	Telegram/Resources/winrc/Telegram.rc
#	Telegram/Resources/winrc/Updater.rc
#	Telegram/SourceFiles/calls/calls_call.cpp
#	Telegram/SourceFiles/core/local_url_handlers.cpp
#	Telegram/SourceFiles/core/version.h
#	Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp
#	Telegram/SourceFiles/history/view/media/history_view_sticker.cpp
#	Telegram/lib_ui
#	snap/snapcraft.yaml
This commit is contained in:
AlexeyZavar 2024-04-01 23:56:12 +03:00
commit c6b42e2940
511 changed files with 21299 additions and 4877 deletions

View file

@ -64,7 +64,10 @@ jobs:
- name: First set up.
run: |
sudo chown -R `whoami`:admin /usr/local/share
brew install automake ninja pkg-config nasm meson
brew update
brew upgrade || true
brew install automake meson nasm ninja pkg-config
# Disable spotlight.
sudo mdutil -a -i off

View file

@ -60,7 +60,7 @@ include(cmake/options.cmake)
if (NOT DESKTOP_APP_USE_PACKAGED)
if (WIN32)
set(qt_version 5.15.12)
set(qt_version 5.15.13)
elseif (APPLE)
set(qt_version 6.2.7)
endif()
@ -68,6 +68,7 @@ endif()
include(cmake/external/qt/package.cmake)
set(desktop_app_skip_libs
glibmm
variant
)

View file

@ -30,8 +30,9 @@ include(cmake/lib_tgvoip.cmake)
include(cmake/lib_tgcalls.cmake)
include(cmake/lib_prisma.cmake)
include(cmake/td_export.cmake)
include(cmake/td_mtproto.cmake)
include(cmake/td_iv.cmake)
include(cmake/td_lang.cmake)
include(cmake/td_mtproto.cmake)
include(cmake/td_scheme.cmake)
include(cmake/td_ui.cmake)
include(cmake/generate_appdata_changelog.cmake)
@ -62,8 +63,9 @@ PRIVATE
desktop-app::external_minizip
tdesktop::td_export
tdesktop::td_mtproto
tdesktop::td_iv
tdesktop::td_lang
tdesktop::td_mtproto
tdesktop::td_scheme
tdesktop::td_ui
desktop-app::lib_webrtc
@ -176,6 +178,8 @@ PRIVATE
api/api_chat_filters.h
api/api_chat_invite.cpp
api/api_chat_invite.h
api/api_chat_links.cpp
api/api_chat_links.h
api/api_chat_participants.cpp
api/api_chat_participants.h
api/api_cloud_password.cpp
@ -184,6 +188,8 @@ PRIVATE
api/api_common.h
api/api_confirm_phone.cpp
api/api_confirm_phone.h
api/api_earn.cpp
api/api_earn.h
api/api_editing.cpp
api/api_editing.h
api/api_global_privacy.cpp
@ -458,8 +464,6 @@ PRIVATE
chat_helpers/spellchecker_common.h
chat_helpers/stickers_dice_pack.cpp
chat_helpers/stickers_dice_pack.h
chat_helpers/stickers_emoji_image_loader.cpp
chat_helpers/stickers_emoji_image_loader.h
chat_helpers/stickers_emoji_pack.cpp
chat_helpers/stickers_emoji_pack.h
chat_helpers/stickers_gift_box_pack.cpp
@ -786,6 +790,8 @@ PRIVATE
history/view/media/history_view_media.h
history/view/media/history_view_media_common.cpp
history/view/media/history_view_media_common.h
history/view/media/history_view_media_generic.cpp
history/view/media/history_view_media_generic.h
history/view/media/history_view_media_grouped.cpp
history/view/media/history_view_media_grouped.h
history/view/media/history_view_media_spoiler.cpp
@ -925,14 +931,18 @@ PRIVATE
history/history_view_highlight_manager.h
history/history_widget.cpp
history/history_widget.h
info/boosts/giveaway/giveaway_list_controllers.cpp
info/boosts/giveaway/giveaway_list_controllers.h
info/boosts/create_giveaway_box.cpp
info/boosts/create_giveaway_box.h
info/boosts/info_boosts_inner_widget.cpp
info/boosts/info_boosts_inner_widget.h
info/boosts/info_boosts_widget.cpp
info/boosts/info_boosts_widget.h
info/channel_statistics/boosts/create_giveaway_box.cpp
info/channel_statistics/boosts/create_giveaway_box.h
info/channel_statistics/boosts/giveaway/giveaway_list_controllers.cpp
info/channel_statistics/boosts/giveaway/giveaway_list_controllers.h
info/channel_statistics/boosts/info_boosts_inner_widget.cpp
info/channel_statistics/boosts/info_boosts_inner_widget.h
info/channel_statistics/boosts/info_boosts_widget.cpp
info/channel_statistics/boosts/info_boosts_widget.h
info/channel_statistics/earn/info_earn_inner_widget.cpp
info/channel_statistics/earn/info_earn_inner_widget.h
info/channel_statistics/earn/info_earn_widget.cpp
info/channel_statistics/earn/info_earn_widget.h
info/common_groups/info_common_groups_inner_widget.cpp
info/common_groups/info_common_groups_inner_widget.h
info/common_groups/info_common_groups_widget.cpp
@ -1063,6 +1073,10 @@ PRIVATE
intro/intro_step.h
intro/intro_widget.cpp
intro/intro_widget.h
iv/iv_delegate_impl.cpp
iv/iv_delegate_impl.h
iv/iv_instance.cpp
iv/iv_instance.h
lang/lang_cloud_manager.cpp
lang/lang_cloud_manager.h
lang/lang_instance.cpp
@ -1197,6 +1211,8 @@ PRIVATE
menu/menu_mute.h
menu/menu_send.cpp
menu/menu_send.h
menu/menu_sponsored.cpp
menu/menu_sponsored.h
menu/menu_ttl_validator.cpp
menu/menu_ttl_validator.h
mtproto/config_loader.cpp
@ -1251,8 +1267,6 @@ PRIVATE
payments/payments_checkout_process.h
payments/payments_form.cpp
payments/payments_form.h
platform/linux/linux_desktop_environment.cpp
platform/linux/linux_desktop_environment.h
platform/linux/linux_wayland_integration_dummy.cpp
platform/linux/linux_wayland_integration.cpp
platform/linux/linux_wayland_integration.h
@ -1359,6 +1373,10 @@ PRIVATE
settings/business/settings_away_message.h
settings/business/settings_shortcut_messages.cpp
settings/business/settings_shortcut_messages.h
settings/business/settings_chat_intro.cpp
settings/business/settings_chat_intro.h
settings/business/settings_chat_links.cpp
settings/business/settings_chat_links.h
settings/business/settings_chatbots.cpp
settings/business/settings_chatbots.h
settings/business/settings_greeting.cpp
@ -1561,8 +1579,6 @@ PRIVATE
window/window_session_controller.cpp
window/window_session_controller.h
window/window_session_controller_link_info.h
window/window_slide_animation.cpp
window/window_slide_animation.h
window/window_top_bar_wrap.h
window/themes/window_theme.cpp
window/themes/window_theme.h
@ -1637,6 +1653,7 @@ PRIVATE
qrc/emoji_preview.qrc
qrc/telegram/animations.qrc
qrc/telegram/export.qrc
qrc/telegram/iv.qrc
qrc/telegram/telegram.qrc
qrc/telegram/sounds.qrc
winrc/Telegram.rc
@ -1744,13 +1761,9 @@ elseif (APPLE)
)
endif()
else()
target_link_libraries(Telegram
PRIVATE
desktop-app::external_glibmm
)
include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake)
generate_dbus(Telegram org.freedesktop.portal. XdpBackground ${third_party_loc}/xdg-desktop-portal/data/org.freedesktop.portal.Background.xml)
generate_dbus(Telegram org.freedesktop. XdgNotifications ${src_loc}/platform/linux/org.freedesktop.Notifications.xml)
if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
target_link_libraries(Telegram

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 893 B

View file

@ -0,0 +1 @@
.hljs{display:block;overflow-x:auto;padding:0.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,657 @@
var IV = {
notify: function(message) {
if (window.external && window.external.invoke) {
window.external.invoke(JSON.stringify(message));
}
},
frameClickHandler: function(e) {
var target = e.target;
var context = '';
while (target) {
if (target.id == 'menu_page_blocker') {
IV.notify({ event: 'menu_page_blocker_click' });
IV.menuShown(false);
return;
}
if (target.tagName == 'AUDIO' || target.tagName == 'VIDEO') {
return;
}
if (context === ''
&& target.hasAttribute
&& target.hasAttribute('data-context')) {
context = String(target.getAttribute('data-context'));
}
if (target.tagName == 'A') {
break;
}
target = target.parentNode;
}
if (!target || !target.hasAttribute('href')) {
return;
}
var base = document.createElement('A');
base.href = window.location.href;
if (base.origin != target.origin
|| base.pathname != target.pathname
|| base.search != target.search) {
IV.notify({
event: 'link_click',
url: target.href,
context: context,
});
} else if (target.hash.length < 2) {
IV.jumpToHash('');
} else {
IV.jumpToHash(decodeURIComponent(target.hash.substr(1)));
}
e.preventDefault();
},
getElementTop: function (element) {
var top = 0;
while (element && !element.classList.contains('page-scroll')) {
top += element.offsetTop;
element = element.offsetParent;
}
return top;
},
jumpToHash: function (hash, instant) {
var current = IV.computeCurrentState();
current.hash = hash;
window.history.replaceState(
current,
'',
'page' + IV.index + '.html');
if (hash == '') {
IV.scrollTo(0, instant);
return;
}
var element = document.getElementsByName(hash)[0];
if (element) {
IV.scrollTo(IV.getElementTop(element), instant);
}
},
frameKeyDown: function (e) {
const keyW = (e.key === 'w')
|| (e.code === 'KeyW')
|| (e.keyCode === 87);
const keyQ = (e.key === 'q')
|| (e.code === 'KeyQ')
|| (e.keyCode === 81);
const keyM = (e.key === 'm')
|| (e.code === 'KeyM')
|| (e.keyCode === 77);
if ((e.metaKey || e.ctrlKey) && (keyW || keyQ || keyM)) {
e.preventDefault();
IV.notify({
event: 'keydown',
modifier: e.ctrlKey ? 'ctrl' : 'cmd',
key: keyW ? 'w' : keyQ ? 'q' : 'm',
});
} else if (e.key === 'Escape' || e.keyCode === 27) {
e.preventDefault();
if (IV.position) {
window.history.back();
} else {
IV.notify({
event: 'keydown',
key: 'escape',
});
}
}
},
frameMouseEnter: function (e) {
IV.notify({ event: 'mouseenter' });
},
frameMouseUp: function (e) {
IV.notify({ event: 'mouseup' });
},
lastScrollTop: 0,
frameScrolled: function (e) {
const was = IV.lastScrollTop;
IV.lastScrollTop = IV.findPageScroll().scrollTop;
IV.updateJumpToTop(was < IV.lastScrollTop);
IV.checkVideos();
},
updateJumpToTop: function (scrolledDown) {
if (IV.lastScrollTop < 100) {
document.getElementById('bottom_up').classList.add('hidden');
} else if (scrolledDown && IV.lastScrollTop > 200) {
document.getElementById('bottom_up').classList.remove('hidden');
}
},
updateStyles: function (styles) {
if (IV.styles !== styles) {
IV.styles = styles;
document.getElementsByTagName('html')[0].style = styles;
}
},
toggleChannelJoined: function (id, joined) {
IV.channelsJoined['channel' + id] = joined;
IV.checkChannelButtons();
},
checkChannelButtons: function() {
const channels = document.getElementsByClassName('channel');
for (var i = 0; i < channels.length; ++i) {
const channel = channels[i];
const full = String(channel.getAttribute('data-context'));
const value = IV.channelsJoined[full];
if (value !== undefined) {
channel.classList.toggle('joined', value);
}
}
},
slideshowSlide: function(el, delta) {
var dir = window.getComputedStyle(el, null).direction || 'ltr';
var marginProp = dir == 'rtl' ? 'marginRight' : 'marginLeft';
if (delta) {
var form = el.parentNode.firstChild;
var s = form.s;
const next = +s.value + delta;
s.value = (next == s.length) ? 0 : (next == -1) ? (s.length - 1) : next;
form.nextSibling.firstChild.style[marginProp] = (-100 * s.value) + '%';
} else {
el.form.nextSibling.firstChild.style[marginProp] = (-100 * el.value) + '%';
}
return false;
},
initPreBlocks: function() {
if (!hljs) {
return;
}
var pres = document.getElementsByTagName('pre');
for (var i = 0; i < pres.length; i++) {
if (pres[i].hasAttribute('data-language')) {
hljs.highlightBlock(pres[i]);
}
}
},
initEmbedBlocks: function() {
var iframes = document.getElementsByTagName('iframe');
for (var i = 0; i < iframes.length; i++) {
(function(iframe) {
window.addEventListener('message', function(event) {
if (event.source !== iframe.contentWindow ||
event.origin != window.origin) {
return;
}
try {
var data = JSON.parse(event.data);
} catch(e) {
var data = {};
}
if (data.eventType == 'resize_frame') {
if (data.eventData.height) {
iframe.style.height = data.eventData.height + 'px';
}
}
}, false);
})(iframes[i]);
}
},
addRipple: function (button, x, y) {
const ripple = document.createElement('span');
ripple.classList.add('ripple');
const inner = document.createElement('span');
inner.classList.add('inner');
x -= button.offsetLeft;
y -= button.offsetTop;
const mx = button.clientWidth - x;
const my = button.clientHeight - y;
const sq1 = x * x + y * y;
const sq2 = mx * mx + y * y;
const sq3 = x * x + my * my;
const sq4 = mx * mx + my * my;
const radius = Math.sqrt(Math.max(sq1, sq2, sq3, sq4));
inner.style.width = inner.style.height = `${2 * radius}px`;
inner.style.left = `${x - radius}px`;
inner.style.top = `${y - radius}px`;
inner.classList.add('inner');
ripple.addEventListener('animationend', function (e) {
if (e.animationName === 'fadeOut') {
ripple.remove();
}
});
ripple.appendChild(inner);
button.appendChild(ripple);
},
stopRipples: function (button) {
const id = button.id ? button.id : button;
button = document.getElementById(id);
const ripples = button.getElementsByClassName('ripple');
for (var i = 0; i < ripples.length; ++i) {
const ripple = ripples[i];
if (!ripple.classList.contains('hiding')) {
ripple.classList.add('hiding');
}
}
},
init: function () {
var current = IV.computeCurrentState();
window.history.replaceState(current, '', IV.pageUrl(0));
IV.jumpToHash(current.hash, true);
IV.lastScrollTop = window.history.state.scroll;
IV.findPageScroll().onscroll = IV.frameScrolled;
const buttons = document.getElementsByClassName('fixed_button');
for (let i = 0; i < buttons.length; ++i) {
const button = buttons[i];
button.addEventListener('mousedown', function (e) {
IV.addRipple(e.currentTarget, e.clientX, e.clientY);
});
button.addEventListener('mouseup', function (e) {
const id = e.currentTarget.id;
setTimeout(function () {
IV.stopRipples(id);
}, 0);
});
button.addEventListener('mouseleave', function (e) {
IV.stopRipples(e.currentTarget);
});
}
IV.initMedia();
IV.notify({ event: 'ready' });
IV.forceScrollFocus();
IV.frameScrolled();
},
initMedia: function () {
var scroll = IV.findPageScroll();
const photos = scroll.getElementsByClassName('photo');
for (let i = 0; i < photos.length; ++i) {
const photo = photos[i];
if (photo.classList.contains('loaded')) {
continue;
}
const url = photo.style.backgroundImage;
if (!url || url.length < 7) {
continue;
}
var img = new Image();
img.onload = function () {
photo.classList.add('loaded');
}
img.src = url.substr(5, url.length - 7);
if (img.complete) {
photo.classList.add('loaded');
IV.stopAnimations(photo);
}
}
IV.videos = [];
const videos = scroll.getElementsByClassName('video');
for (let i = 0; i < videos.length; ++i) {
const element = videos[i];
IV.videos.push({
element: element,
src: String(element.getAttribute('data-src')),
autoplay: (element.getAttribute('data-autoplay') == '1'),
loop: (element.getAttribute('data-loop') == '1'),
small: (element.getAttribute('data-small') == '1'),
filled: (element.firstChild
&& element.firstChild.tagName == 'VIDEO'),
});
}
},
checkVideos: function () {
const visibleTop = IV.lastScrollTop;
const visibleBottom = visibleTop + IV.findPageScroll().offsetHeight;
const videos = IV.videos;
for (let i = 0; i < videos.length; ++i) {
const video = videos[i];
const element = video.element;
const wrap = element.offsetParent; // video-wrap
const top = IV.getElementTop(wrap);
const bottom = top + wrap.offsetHeight;
if (top < visibleBottom && bottom > visibleTop) {
if (!video.created) {
video.created = new Date();
video.loaded = false;
element.innerHTML = '<video muted class="'
+ (video.small ? 'video-small' : '')
+ '"'
+ (video.autoplay
? ' preload="auto" autoplay'
: (video.small
? ''
: ' controls'))
+ (video.loop ? ' loop' : '')
+ ' oncanplay="IV.checkVideos();"'
+ ' onloadeddata="IV.checkVideos();">'
+ '<source src="'
+ video.src
+ '" type="video/mp4" />'
+ '</video>';
var media = element.firstChild;
media.oncontextmenu = function () { return false; };
media.oncanplay = IV.checkVideos;
media.onloadeddata = IV.checkVideos;
}
} else if (video.created && video.autoplay) {
video.created = false;
element.innerHTML = '';
}
if (video.created && !video.loaded) {
var media = element.firstChild;
const HAVE_CURRENT_DATA = 2;
if (media && media.readyState >= HAVE_CURRENT_DATA) {
video.loaded = true;
media.classList.add('loaded');
if ((new Date() - video.created) < 100) {
IV.stopAnimations(media);
}
}
}
}
},
showTooltip: function (text) {
var toast = document.createElement('div');
toast.classList.add('toast');
toast.textContent = text;
document.body.appendChild(toast);
setTimeout(function () {
toast.classList.add('hiding');
}, 2000);
setTimeout(function () {
document.body.removeChild(toast);
}, 3000);
},
scrollTo: function (y, instant) {
if (y < 200) {
document.getElementById('bottom_up').classList.add('hidden');
}
IV.findPageScroll().scrollTo({
top: y || 0,
behavior: instant ? 'instant' : 'smooth'
});
},
computeCurrentState: function () {
var now = IV.findPageScroll();
return {
position: IV.position,
index: IV.index,
hash: ((!window.history.state
|| window.history.state.hash === undefined)
? window.location.hash.substr(1)
: window.history.state.hash),
scroll: now ? now.scrollTop : 0
};
},
pageUrl: function (index, hash) {
var result = 'page' + index + '.html';
if (hash) {
result += '#' + hash;
}
return result;
},
navigateTo: function (index, hash) {
if (!index && !IV.index) {
IV.navigateToDOM(IV.index, hash);
return;
}
IV.pending = [index, hash];
if (!IV.cache[index]) {
IV.loadPage(index);
} else if (IV.cache[index].dom) {
IV.navigateToDOM(index, hash);
} else if (IV.cache[index].content) {
IV.navigateToLoaded(index, hash);
}
},
applyUpdatedContent: function (index) {
if (IV.index != index) {
IV.cache[index].contentUpdated = (IV.cache[index].dom !== undefined);
return;
}
var data = JSON.parse(IV.cache[index].content);
var article = function (el) {
return el.getElementsByTagName('article')[0];
};
var from = article(IV.findPageScroll());
var to = article(IV.makeScrolledContent(data.html));
morphdom(from, to, {
onBeforeElUpdated: function (fromEl, toEl) {
if (fromEl.classList.contains('video')
&& toEl.classList.contains('video')
&& fromEl.hasAttribute('data-src')
&& toEl.hasAttribute('data-src')
&& (fromEl.getAttribute('data-src')
== toEl.getAttribute('data-src'))) {
return false;
} else if (fromEl.tagName == 'SECTION'
&& fromEl.classList.contains('channel')
&& fromEl.hasAttribute('data-context')
&& toEl.tagName == 'SECTION'
&& toEl.classList.contains('channel')
&& toEl.hasAttribute('data-context')
&& (String(fromEl.getAttribute('data-context'))
== String(toEl.getAttribute('data-context')))) {
return false;
} else if (fromEl.classList.contains('loaded')) {
toEl.classList.add('loaded');
}
return !fromEl.isEqualNode(toEl);
}
});
IV.initMedia();
eval(data.js);
},
loadPage: function (index) {
if (!IV.cache[index]) {
IV.cache[index] = {};
}
IV.cache[index].loading = true;
let xhr = new XMLHttpRequest();
xhr.onload = function () {
IV.cache[index].loading = false;
IV.cache[index].content = xhr.responseText;
IV.applyUpdatedContent(index);
if (IV.pending && IV.pending[0] == index) {
IV.navigateToLoaded(index, IV.pending[1]);
}
if (IV.cache[index].reloadPending) {
IV.cache[index].reloadPending = false;
IV.reloadPage(index);
}
}
xhr.open('GET', 'page' + index + '.json');
xhr.send();
},
reloadPage: function (index) {
if (IV.cache[index] && IV.cache[index].loading) {
IV.cache[index].reloadPending = true;
return;
}
IV.loadPage(index);
},
makeScrolledContent: function (html) {
var result = document.createElement('div');
result.className = 'page-scroll';
result.tabIndex = '-1';
result.innerHTML = '<div class="page-slide"><article>'
+ html
+ '</article></div>';
result.onscroll = IV.frameScrolled;
return result;
},
navigateToLoaded: function (index, hash) {
if (IV.cache[index].dom) {
IV.navigateToDOM(index, hash);
} else {
var data = JSON.parse(IV.cache[index].content);
IV.cache[index].dom = IV.makeScrolledContent(data.html);
IV.navigateToDOM(index, hash);
eval(data.js);
}
},
navigateToDOM: function (index, hash) {
IV.pending = null;
if (IV.index == index) {
IV.jumpToHash(hash);
IV.forceScrollFocus();
return;
}
window.history.replaceState(
IV.computeCurrentState(),
'',
IV.pageUrl(IV.index));
IV.position = IV.position + 1;
window.history.pushState(
{ position: IV.position, index: index, hash: hash },
'',
IV.pageUrl(index));
IV.showDOM(index, hash);
},
findPageScroll: function () {
var all = document.getElementsByClassName('page-scroll');
for (i = 0; i < all.length; ++i) {
if (!all[i].classList.contains('hidden-left')
&& !all[i].classList.contains('hidden-right')) {
return all[i];
}
}
return null;
},
showDOM: function (index, hash, scroll) {
IV.pending = null;
if (IV.index != index) {
var initial = !window.history.state
|| window.history.state.position === undefined;
var back = initial
|| IV.position > window.history.state.position;
IV.position = initial ? 0 : window.history.state.position;
var now = IV.cache[index].dom;
var was = IV.findPageScroll();
if (!IV.cache[IV.index]) {
IV.cache[IV.index] = {};
}
IV.cache[IV.index].dom = was;
was.parentNode.appendChild(now);
if (scroll !== undefined) {
now.scrollTop = scroll;
setTimeout(function () {
// When returning by history.back to an URL with a hash
// for the first time browser forces the scroll to the
// hash instead of the saved scroll position.
//
// This workaround prevents incorrect scroll position.
now.scrollTop = scroll;
}, 0);
}
now.classList.add(back ? 'hidden-left' : 'hidden-right');
now.classList.remove(back ? 'hidden-right' : 'hidden-left');
IV.stopAnimations(now.firstChild);
if (!was.listening) {
was.listening = true;
was.firstChild.addEventListener('transitionend', function (e) {
if (was.classList.contains('hidden-left')
|| was.classList.contains('hidden-right')) {
if (was.parentNode) {
was.parentNode.removeChild(was);
var videos = was.getElementsByClassName('video');
for (var i = 0; i < videos.length; ++i) {
videos[i].innerHTML = '';
}
}
}
});
}
was.classList.add(back ? 'hidden-right' : 'hidden-left');
now.classList.remove(back ? 'hidden-left' : 'hidden-right');
IV.index = index;
IV.notify({
event: 'location_change',
index: IV.index,
position: IV.position,
hash: IV.computeCurrentState().hash,
});
if (IV.cache[index].contentUpdated) {
IV.cache[index].contentUpdated = false;
IV.applyUpdatedContent(index);
} else {
IV.initMedia();
}
IV.checkChannelButtons();
if (scroll === undefined) {
IV.jumpToHash(hash, true);
} else {
IV.lastScrollTop = scroll;
IV.updateJumpToTop(true);
}
} else if (scroll !== undefined) {
IV.scrollTo(scroll);
IV.lastScrollTop = scroll;
IV.updateJumpToTop(true);
} else {
IV.jumpToHash(hash);
}
IV.forceScrollFocus();
IV.frameScrolled();
},
forceScrollFocus: function () {
IV.findPageScroll().focus();
setTimeout(function () {
// Doesn't work on #hash-ed pages in Windows WebView2 otherwise.
IV.findPageScroll().focus();
}, 100);
},
stopAnimations: function (element) {
element.getAnimations().forEach(
(animation) => animation.finish());
},
back: function () {
window.history.back();
},
menuShown: function (shown) {
var already = document.getElementById('menu_page_blocker');
if (already && shown) {
return;
} else if (already) {
document.body.removeChild(already);
return;
} else if (!shown) {
return;
}
var blocker = document.createElement('div');
blocker.id = 'menu_page_blocker';
document.body.appendChild(blocker);
},
videos: {},
videosPlaying: {},
cache: {},
channelsJoined: {},
index: 0,
position: 0
};
document.onclick = IV.frameClickHandler;
document.onkeydown = IV.frameKeyDown;
document.onmouseenter = IV.frameMouseEnter;
document.onmouseup = IV.frameMouseUp;
document.onresize = IV.checkVideos;
window.onmessage = IV.postMessageHandler;
window.addEventListener('popstate', function (e) {
if (e.state) {
IV.showDOM(e.state.index, e.state.hash, e.state.scroll);
}
});
document.addEventListener("DOMContentLoaded", IV.forceScrollFocus);

View file

@ -652,6 +652,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_messages_privacy" = "Messages";
"lng_settings_voices_privacy" = "Voice messages";
"lng_settings_bio_privacy" = "Bio";
"lng_settings_birthday_privacy" = "Date of Birth";
"lng_settings_privacy_premium" = "Only subscribers of {link} can restrict receiving voice messages.";
"lng_settings_privacy_premium_link" = "Telegram Premium";
"lng_settings_passcode_disable" = "Disable Passcode";
@ -664,6 +665,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_phone_label" = "Phone number";
"lng_settings_username_add" = "Add username";
"lng_settings_username_about" = "Username lets people contact you on Telegram without needing your phone number.";
"lng_settings_birthday_label" = "Date of Birth";
"lng_settings_birthday_add" = "Add";
"lng_settings_birthday_about" = "Choose who can see your birthday in {link}.";
"lng_settings_birthday_about_link" = "Settings";
"lng_settings_birthday_contacts" = "Only your contacts can see your birthday. {link}";
"lng_settings_birthday_contacts_link" = "Change >";
"lng_settings_birthday_saved" = "Your date of birth was updated.";
"lng_settings_birthday_reset" = "Remove";
"lng_settings_channel_label" = "Personal channel";
"lng_settings_channel_add" = "Add";
"lng_settings_channel_remove" = "Remove";
"lng_settings_channel_no_yet" = "You don't have any public channels yet.";
"lng_settings_channel_start" = "Start a Channel";
"lng_settings_channel_saved" = "Your personal channel was updated.";
"lng_settings_channel_removed" = "Your personal channel was removed.";
"lng_settings_add_account_about" = "You can add up to four accounts with different phone numbers.";
"lng_settings_peer_to_peer_about" = "Disabling peer-to-peer will relay all calls through Telegram servers to avoid revealing your IP address, but may slightly decrease audio quality.";
"lng_settings_advanced" = "Advanced";
@ -1087,12 +1103,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_everyone" = "Everybody";
"lng_edit_privacy_contacts" = "My contacts";
"lng_edit_privacy_close_friends" = "Close friends";
"lng_edit_privacy_contacts_and_premium" = "Contacts & Premium";
"lng_edit_privacy_nobody" = "Nobody";
"lng_edit_privacy_premium" = "Premium users";
"lng_edit_privacy_exceptions" = "Add exceptions";
"lng_edit_privacy_user_types" = "User types";
"lng_edit_privacy_users_and_groups" = "Users and groups";
"lng_edit_privacy_premium_status" = "all Telegram Premium subscribers";
"lng_edit_privacy_exceptions_count#one" = "{count} user";
"lng_edit_privacy_exceptions_count#other" = "{count} users";
"lng_edit_privacy_exceptions_premium_and" = "Premium & {users}";
"lng_edit_privacy_exceptions_add" = "Add users or groups";
"lng_edit_privacy_phone_number_title" = "Phone number privacy";
@ -1136,6 +1157,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_about_always_title" = "Always allow";
"lng_edit_privacy_about_never_title" = "Never allow";
"lng_edit_privacy_birthday_title" = "Date of birth privacy";
"lng_edit_privacy_birthday_header" = "Who can see my date of birth";
"lng_edit_privacy_birthday_always_empty" = "Always allow";
"lng_edit_privacy_birthday_never_empty" = "Never allow";
"lng_edit_privacy_birthday_exceptions" = "These users will or will not be able to see your date of birth regardless of the settings above.";
"lng_edit_privacy_birthday_always_title" = "Always allow";
"lng_edit_privacy_birthday_never_title" = "Never allow";
"lng_edit_privacy_birthday_yet" = "You haven't entered your date of birth yet.\n{link}";
"lng_edit_privacy_birthday_yet_link" = "Add my birthday >";
"lng_edit_privacy_calls_title" = "Voice calls privacy";
"lng_edit_privacy_calls_header" = "Who can call you";
"lng_edit_privacy_calls_always_empty" = "Always allow";
@ -1288,6 +1319,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_photo_by_you" = "photo set by you";
"lng_profile_public_photo" = "public photo";
"lng_invite_upgrade_title" = "Upgrade to Premium";
"lng_invite_upgrade_group_invite#one" = "{users} only accepts invitations to groups from Contacts and **Premium** users.";
"lng_invite_upgrade_group_invite#other" = "{users} only accept invitations to groups from Contacts and **Premium** users.";
"lng_invite_upgrade_group_write#one" = "{users} only accepts messages and invitations to groups from Contacts and **Premium** users.";
"lng_invite_upgrade_group_write#other" = "{users} only accept messages and invitations to groups from Contacts and **Premium** users.";
"lng_invite_upgrade_channel_invite#one" = "{users} only accepts invitations to channels from Contacts and **Premium** users.";
"lng_invite_upgrade_channel_invite#other" = "{users} only accept invitations to channels from Contacts and **Premium** users.";
"lng_invite_upgrade_channel_write#one" = "{users} only accepts messages and invitations to channels from Contacts and **Premium** users.";
"lng_invite_upgrade_channel_write#other" = "{users} only accept messages and invitations to channels from Contacts and **Premium** users.";
"lng_invite_upgrade_users_few" = "{users} and {last}";
"lng_invite_upgrade_users_many#one" = "{users} and **{count}** more person";
"lng_invite_upgrade_users_many#other" = "{users} and **{count}** more people";
"lng_invite_upgrade_or" = "or";
"lng_invite_upgrade_via_title" = "Invite via Link";
"lng_invite_upgrade_via_group_about" = "You can send an invite link to the group in a private message instead.";
"lng_invite_upgrade_via_channel_about" = "You can send an invite link to the channel in a private message instead.";
"lng_invite_status_disabled" = "available only to Premium users";
"lng_via_link_group_one" = "**{user}** restricts adding them to groups.\nYou can send them an invite link as message instead.";
"lng_via_link_group_many#one" = "**{count} user** restricts adding them to groups.\nYou can send them an invite link as message instead.";
"lng_via_link_group_many#other" = "**{count} users** restrict adding them to groups.\nYou can send them an invite link as message instead.";
@ -1303,12 +1352,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_via_link_shared_many#one" = "Link shared with **{count} user**.";
"lng_via_link_shared_many#other" = "Link shared with **{count} users**.";
"lng_info_personal_channel_label" = "Channel";
"lng_info_mobile_label" = "Mobile";
"lng_info_mobile_context_menu_fragment_about" = "This number is not tied to a SIM card and was acquired on {link}.";
"lng_info_mobile_context_menu_fragment_about_link" = "Fragment";
"lng_info_mobile_hidden" = "Hidden";
"lng_info_username_label" = "Username";
"lng_info_usernames_label" = "also";
"lng_info_birthday_label" = "Date of birth";
"lng_info_birthday_years#one" = "{date} ({count} year old)";
"lng_info_birthday_years#other" = "{date} ({count} years old)";
"lng_info_birthday_today_years#one" = "{date}\n({count} year old)";
"lng_info_birthday_today_years#other" = "{date}\n({count} years old)";
"lng_info_birthday_today_label" = "Birthday today";
"lng_info_birthday_today" = "{emoji} {date}";
"lng_info_bio_label" = "Bio";
"lng_info_link_label" = "Link";
"lng_info_location_label" = "Location";
@ -1540,6 +1597,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_report_button" = "Report";
"lng_report_thanks" = "Thank you! Your report will be reviewed by our team very soon.";
"lng_report_sponsored_hidden" = "Sponsored messages will be hidden.";
"lng_report_sponsored_reported" = "We will review this ad to ensure it matches out {link}.";
"lng_report_sponsored_reported_link" = "Ad Policies and Guidelines";
"lng_report_sponsored_reported_learn" = "Learn more about our {link}.";
"lng_channel_add_members" = "Add members";
"lng_channel_add_users" = "Add users";
"lng_channel_add_removed" = "Remove user";
@ -1760,6 +1822,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_giveaway_results_none" = "No winners of the giveaway could be selected.";
"lng_action_boost_apply#one" = "{from} boosted the group";
"lng_action_boost_apply#other" = "{from} boosted the group {count} times";
"lng_action_set_chat_intro" = "{from} added the message below for all empty chats. How?";
"lng_similar_channels_title" = "Similar channels";
"lng_similar_channels_view_all" = "View all";
@ -1919,6 +1982,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_forwarded_imported" = "This message was imported from another app. It may not be real.";
"lng_signed_author" = "Author: {user}";
"lng_sponsored_message_title" = "Sponsored";
"lng_sponsored_message_revenue_button" = "what's this?";
"lng_recommended_message_title" = "Recommended";
"lng_edited" = "edited";
"lng_commented" = "commented";
@ -2185,6 +2249,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_business_about_away_messages" = "Define messages that are automatically sent when you are off.";
"lng_business_subtitle_chatbots" = "Chatbots";
"lng_business_about_chatbots" = "Add any third party chatbots that will process customer interactions.";
"lng_business_subtitle_chat_intro" = "Custom Intro";
"lng_business_about_chat_intro" = "Customize the message people see before they start a chat with you.";
"lng_business_subtitle_chat_links" = "Links to Chat";
"lng_business_about_chat_links" = "Create links that start a chat with you, suggesting the first message.";
"lng_location_title" = "Location";
"lng_location_about" = "Display the location of your business on your account.";
@ -2286,8 +2354,52 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_chatbots_reply_about" = "The bot will be able to view all new incoming messages, but not the messages that had been sent before you added the bot.";
"lng_chatbots_remove" = "Remove Bot";
"lng_chatbots_not_found" = "Chatbot not found.";
"lng_chatbots_not_supported" = "This bot doesn't support Telegram Business yet.";
"lng_chatbots_add" = "Add";
"lng_chatbots_info_url" = "https://telegram.org/privacy";
"lng_chatbot_status_can_reply" = "bot manages this chat";
"lng_chatbot_status_paused" = "bot stopped";
"lng_chatbot_status_views" = "bot has access to this chat";
"lng_chatbot_button_pause" = "Stop";
"lng_chatbot_button_resume" = "Start";
"lng_chatbot_menu_manage" = "Manage bot";
"lng_chatbot_menu_remove" = "Remove bot from this chat";
"lng_chatbot_menu_revoke" = "Revoke access to this chat";
"lng_chat_intro_title" = "Custom Intro";
"lng_chat_intro_subtitle" = "Customize your intro";
"lng_chat_intro_default_title" = "No messages here yet...";
"lng_chat_intro_default_message" = "Send a message or click on the greeting below";
"lng_chat_intro_enter_title" = "Enter Title";
"lng_chat_intro_enter_message" = "Enter Message";
"lng_chat_intro_choose_sticker" = "Choose Sticker";
"lng_chat_intro_random_sticker" = "Random";
"lng_chat_intro_about" = "You can customize the message people see before they start a chat with you.";
"lng_chat_intro_reset" = "Reset to Default";
"lng_chat_links_title" = "Links to Chat";
"lng_chat_links_about" = "Give your customers short links that start a chat with you and suggest the first message from them to you.";
"lng_chat_links_create_link" = "Create a Link to Chat";
"lng_chat_links_footer" = "You can also use a simple link for a chat with you {links}";
"lng_chat_links_footer_both" = "{username} or {link}";
"lng_chat_links_no_clicks" = "no clicks";
"lng_chat_links_clicks#one" = "{count} click";
"lng_chat_links_clicks#other" = "{count} clicks";
"lng_chat_link_new_title" = "New Link";
"lng_chat_link_edit_title" = "Edit Link";
"lng_chat_link_description" = "Add a message that will be entered in the message field for anyone who starts a chat with you using this link.";
"lng_chat_link_placeholder" = "Add Preset Message";
"lng_chat_link_saved" = "Chat link saved.";
"lng_chat_link_copy" = "Copy";
"lng_chat_link_share" = "Share";
"lng_chat_link_rename" = "Rename";
"lng_chat_link_delete" = "Delete";
"lng_chat_link_name" = "Link Name (optional)";
"lng_chat_link_name_about" = "Add a name for this link that only you will see.";
"lng_chat_link_delete_sure" = "Are you sure you want to delete this chat link?";
"lng_chat_link_qr_title" = "Chat Link QR Code";
"lng_chat_link_qr_about" = "Everyone on Telegram can scan this code to contact you.";
"lng_chat_link_copied" = "Chat link copied to clipboard.";
"lng_boost_channel_button" = "Boost Channel";
"lng_boost_group_button" = "Boost Group";
@ -2387,6 +2499,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_boost_channel_needs_level_reactions#one" = "Your channel needs to reach **Level {count}** to add **{same_count}** custom emoji as a reaction.";
"lng_boost_channel_needs_level_reactions#other" = "Your channel needs to reach **Level {count}** to add **{same_count}** custom emoji as reactions.";
"lng_boost_channel_title_cpm" = "Boost Channel";
"lng_boost_channel_needs_level_cpm#one" = "Your channel needs to reach **Level {count}** to switch off ads.";
"lng_boost_channel_needs_level_cpm#other" = "Your channel needs to reach **Level {count}** to switch off ads.";
"lng_boost_group_title_emoji" = "Enable emoji pack";
"lng_boost_group_needs_level_emoji#one" = "Your group needs to reach **Level {count}** to set emoji pack.";
"lng_boost_group_needs_level_emoji#other" = "Your group needs to reach **Level {count}** to set emoji pack.";
@ -2593,6 +2709,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_link_also_send" = "You can also {link} to a friend as a gift.";
"lng_gift_link_also_send_link" = "send this link";
"lng_gift_link_use" = "Use Link";
"lng_gift_link_already_title" = "You already have Telegram Premium";
"lng_gift_link_already_about" = "You can activate this link after {date} or {link} to a friend.";
"lng_gift_link_already_link" = "send the link";
"lng_gift_link_used_title" = "Used Gift Link";
"lng_gift_link_used_about" = "This link was used to activate\na **Telegram Premium** subscription.";
"lng_gift_link_used_footer" = "This link was used on {date}.";
@ -2883,6 +3002,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_bot_remove_from_side_menu_done" = "Bot removed from the main menu.";
"lng_bot_settings" = "Settings";
"lng_bot_open" = "Open Bot";
"lng_bot_terms" = "Terms of Use";
"lng_bot_reload_page" = "Reload Page";
"lng_bot_add_to_menu" = "{bot} asks your permission to be added as an option to your attachments menu so you can access it from any chat.";
"lng_bot_add_to_menu_done" = "Bot added to the menu.";
@ -3013,6 +3133,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_delete_msg" = "Delete Message";
"lng_context_auto_delete_in" = "auto-delete in {duration}";
"lng_context_select_msg" = "Select Message";
"lng_context_select_msg_bulk" = "Select up to this message";
"lng_context_report_msg" = "Report Message";
"lng_context_pin_msg" = "Pin Message";
"lng_context_unpin_msg" = "Unpin Message";
@ -3511,6 +3632,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_call_cancel" = "Cancel";
"lng_call_microphone_off" = "{user}'s microphone is off";
"lng_call_battery_level_low" = "{user}'s battery level is low";
"lng_group_call_title" = "Voice Chat";
"lng_group_call_title_channel" = "Live Stream";
@ -4561,6 +4683,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_view_button_external_link" = "Open link";
"lng_view_button_boost" = "Boost";
"lng_view_button_giftcode" = "Open";
"lng_view_button_iv" = "Instant View";
"lng_sponsored_hide_ads" = "Hide";
"lng_sponsored_title" = "What are sponsored messages?";
@ -4568,6 +4691,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sponsored_info_description2" = "Sponsored Messages are currently in test mode. Once they are fully launched and allow Telegram to cover its basic costs, we will start sharing ad revenue with the owners of public channels in which sponsored messages are displayed.\n\nOnline ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate together.";
"lng_sponsored_info_menu" = "About this ad";
"lng_sponsored_info_submenu" = "Advertiser: {text}";
"lng_sponsored_menu_revenued_about" = "About These Ads";
"lng_sponsored_menu_revenued_report" = "Report Ad";
"lng_sponsored_revenued_subtitle" = "Telegram Ads are very different from ads on other platforms. Ads such as this one:";
"lng_sponsored_revenued_info1_title" = "Respect Your Privacy";
"lng_sponsored_revenued_info1_description" = "Ads on Telegram do not use your personal information and are abased on the channel in which you see them.";
"lng_sponsored_revenued_info2_title" = "Help the Channel Creator";
"lng_sponsored_revenued_info2_description" = "50% of the revenue from Telegram Ads goes to the owner of the channel where they are displayed.";
"lng_sponsored_revenued_info3_title" = "Can Be Removed";
"lng_sponsored_revenued_info3_description#one" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers.";
"lng_sponsored_revenued_info3_description#other" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers.";
"lng_sponsored_revenued_footer_title" = "Can I Launch an Ad?";
"lng_sponsored_revenued_footer_description" = "Anyone can create an ad to display in this channel — with minimal budgets. Check out the **Telegram Ad Platform** for details. {link}";
"lng_telegram_features_url" = "https://t.me/TelegramTips";
@ -4863,9 +4998,74 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_boosts_prepaid_giveaway_status#one" = "{count} subscription {duration}";
"lng_boosts_prepaid_giveaway_status#other" = "{count} subscriptions {duration}";
"lng_channel_earn_title" = "Monetization";
"lng_channel_earn_about" = "Telegram shares 50% of the revenue from ads displayed in your channel. {link}";
"lng_channel_earn_about_link" = "Learn more {emoji}";
"lng_channel_earn_overview_title" = "Proceeds overview";
"lng_channel_earn_available" = "Balance available to withdraw";
"lng_channel_earn_reward" = "Proceeds since last withdrawal";
"lng_channel_earn_total" = "Total lifetime proceeds";
"lng_channel_earn_balance_title" = "Available balance";
"lng_channel_earn_balance_button" = "Withdraw";
"lng_channel_earn_balance_password_title" = "Two-step verification";
"lng_channel_earn_balance_password_description" = "Please enter your password to withdraw.";
"lng_channel_earn_balance_about" = "Collect your reward using Fragment, a third-party platform used by the advertiser to pay for the ad. {link}";
"lng_channel_earn_balance_about_temp" = "In the coming weeks you will be able to collect your reward using Fragment, a third-party platform used by advertisers to pay ads. {link}";
"lng_channel_earn_transfer_sure_about1" = "Check the address of the recipient:";
"lng_channel_earn_transfer_sure_about2" = "This action can not be undone. If the address above is incorrect you will lose your TON.";
"lng_channel_earn_history_title" = "Transaction history";
"lng_channel_earn_history_in" = "Proceeds from ads";
"lng_channel_earn_history_in_about" = "Proceeds from ads displayed in";
"lng_channel_earn_history_out" = "Withdrawal";
"lng_channel_earn_history_out_failed" = "Not Completed";
"lng_channel_earn_history_out_about_failed" = "Withdrawal failed";
"lng_channel_earn_history_out_button" = "View in Blockchain Explorer";
"lng_channel_earn_history_return" = "Refund";
"lng_channel_earn_history_return_about" = "Refunded back";
"lng_channel_earn_history_pending" = "Pending";
"lng_channel_earn_history_show_more#one" = "Show {count} More Transaction";
"lng_channel_earn_history_show_more#other" = "Show {count} More Transactions";
"lng_channel_earn_off" = "Switch Off Ads";
"lng_channel_earn_off_about" = "You will not be eligible for any rewards if you switch off ads.";
"lng_channel_earn_cpm_min" = "No Ads";
"lng_channel_earn_cpm#one" = "{emoji} {count} CPM";
"lng_channel_earn_cpm#other" = "{emoji} {count} CPM";
"lng_channel_earn_learn_title" = "Earn From Your Channel";
"lng_channel_earn_learn_in_subtitle" = "Telegram Ads";
"lng_channel_earn_learn_in_about" = "Telegram can display ads in your channel.";
"lng_channel_earn_learn_split_subtitle" = "50:50 revenue split";
"lng_channel_earn_learn_split_about" = "You can receive 50% of the ad revenue in TON.";
"lng_channel_earn_learn_out_subtitle" = "Flexible withdrawals";
"lng_channel_earn_learn_out_about" = "You can withdraw your TON any time.";
"lng_channel_earn_learn_coin_title" = "What is {emoji} TON?";
"lng_channel_earn_learn_coin_about" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its high speed and low commissions on transactions. {link}";
"lng_channel_earn_learn_close" = "Got it";
"lng_channel_earn_chart_top_hours" = "Ad impressions";
"lng_channel_earn_chart_revenue" = "Ad revenue";
"lng_channel_earn_chart_overriden_detail_currency" = "Revenue in TON";
"lng_channel_earn_chart_overriden_detail_usd" = "Revenue in USD";
"lng_contact_add" = "Add";
"lng_contact_send_message" = "message";
"lng_iv_open_in_browser" = "Open in Browser";
"lng_iv_share" = "Share";
"lng_iv_join_channel" = "Join";
"lng_iv_window_title" = "Instant View";
"lng_limit_download_title" = "Download speed limited";
"lng_limit_download_subscribe" = "Subscribe to {link} and increase download speed {increase}.";
"lng_limit_download_increase_times#one" = "**{count}** time";
"lng_limit_download_increase_times#other" = "**{count}** times";
"lng_limit_download_increase_speed" = "by **{percent}**";
"lng_limit_download_subscribe_link" = "Telegram Premium";
"lng_limit_upload_title" = "Upload speed limited";
"lng_limit_upload_subscribe" = "Subscribe to {link} and increase upload speed {increase}.";
"lng_limit_upload_increase_times#one" = "**{count}** time";
"lng_limit_upload_increase_times#other" = "**{count}** times";
"lng_limit_upload_increase_speed" = "by **{percent}**";
"lng_limit_upload_subscribe_link" = "Telegram Premium";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";

View file

@ -21,5 +21,6 @@
<file alias="writing.tgs">../../animations/writing.tgs</file>
<file alias="hours.tgs">../../animations/hours.tgs</file>
<file alias="phone.tgs">../../animations/phone.tgs</file>
<file alias="chat_link.tgs">../../animations/chat_link.tgs</file>
</qresource>
</RCC>

View file

@ -0,0 +1,9 @@
<RCC>
<qresource prefix="/iv">
<file alias="page.css">../../iv_html/page.css</file>
<file alias="page.js">../../iv_html/page.js</file>
<file alias="highlight.css">../../iv_html/highlight.9.12.0.css</file>
<file alias="highlight.js">../../iv_html/highlight.9.12.0.js</file>
<file alias="morphdom.js">../../iv_html/morphdom-umd.min.2.7.2.js</file>
</qresource>
</RCC>

View file

@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="4.15.2.0" />
Version="4.16.0.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View file

@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,15,2,0
PRODUCTVERSION 4,15,2,0
FILEVERSION 4,16,0,0
PRODUCTVERSION 4,16,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop"
VALUE "FileVersion", "4.15.2.0"
VALUE "FileVersion", "4.16.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.15.2.0"
VALUE "ProductVersion", "4.16.0.0"
END
END
BLOCK "VarFileInfo"

View file

@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,15,2,0
PRODUCTVERSION 4,15,2,0
FILEVERSION 4,16,0,0
PRODUCTVERSION 4,16,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop Updater"
VALUE "FileVersion", "4.15.2.0"
VALUE "FileVersion", "4.16.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.15.2.0"
VALUE "ProductVersion", "4.16.0.0"
END
END
BLOCK "VarFileInfo"

View file

@ -421,10 +421,10 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
MTP_int(itemId),
MTP_int(id),
MTP_vector_from_range(
result
| ranges::views::transform([](
not_null<PeerData*> peer) {
return MTPInputPeer(peer->input); }))
result | ranges::views::transform([](
not_null<PeerData*> peer) {
return MTPInputPeer(peer->input);
}))
)).done([=](const MTPUpdates &result) {
peer->session().api().applyUpdates(result);
}).send();

View file

@ -0,0 +1,170 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_chat_links.h"
#include "api/api_text_entities.h"
#include "apiwrap.h"
#include "data/data_session.h"
#include "main/main_session.h"
namespace Api {
namespace {
[[nodiscard]] ChatLink FromMTP(
not_null<Main::Session*> session,
const MTPBusinessChatLink &link) {
const auto &data = link.data();
return {
.link = qs(data.vlink()),
.title = qs(data.vtitle().value_or_empty()),
.message = {
qs(data.vmessage()),
EntitiesFromMTP(
session,
data.ventities().value_or_empty())
},
.clicks = data.vviews().v,
};
}
[[nodiscard]] MTPInputBusinessChatLink ToMTP(
not_null<Main::Session*> session,
const QString &title,
const TextWithEntities &message) {
auto entities = EntitiesToMTP(
session,
message.entities,
ConvertOption::SkipLocal);
using Flag = MTPDinputBusinessChatLink::Flag;
const auto flags = (title.isEmpty() ? Flag() : Flag::f_title)
| (entities.v.isEmpty() ? Flag() : Flag::f_entities);
return MTP_inputBusinessChatLink(
MTP_flags(flags),
MTP_string(message.text),
std::move(entities),
MTP_string(title));
}
} // namespace
ChatLinks::ChatLinks(not_null<ApiWrap*> api) : _api(api) {
}
void ChatLinks::create(
const QString &title,
const TextWithEntities &message,
Fn<void(Link)> done) {
const auto session = &_api->session();
_api->request(MTPaccount_CreateBusinessChatLink(
ToMTP(session, title, message)
)).done([=](const MTPBusinessChatLink &result) {
const auto link = FromMTP(session, result);
_list.push_back(link);
_updates.fire({ .was = QString(), .now = link });
if (done) done(link);
}).fail([=](const MTP::Error &error) {
const auto type = error.type();
if (done) done(Link());
}).send();
}
void ChatLinks::edit(
const QString &link,
const QString &title,
const TextWithEntities &message,
Fn<void(Link)> done) {
const auto session = &_api->session();
_api->request(MTPaccount_EditBusinessChatLink(
MTP_string(link),
ToMTP(session, title, message)
)).done([=](const MTPBusinessChatLink &result) {
const auto parsed = FromMTP(session, result);
if (parsed.link != link) {
LOG(("API Error: EditBusinessChatLink changed the link."));
if (done) done(Link());
return;
}
const auto i = ranges::find(_list, link, &Link::link);
if (i != end(_list)) {
*i = parsed;
_updates.fire({ .was = link, .now = parsed });
if (done) done(parsed);
} else {
LOG(("API Error: EditBusinessChatLink link not found."));
if (done) done(Link());
}
}).fail([=](const MTP::Error &error) {
const auto type = error.type();
if (done) done(Link());
}).send();
}
void ChatLinks::destroy(
const QString &link,
Fn<void()> done) {
_api->request(MTPaccount_DeleteBusinessChatLink(
MTP_string(link)
)).done([=] {
const auto i = ranges::find(_list, link, &Link::link);
if (i != end(_list)) {
_list.erase(i);
_updates.fire({ .was = link });
if (done) done();
} else {
LOG(("API Error: DeleteBusinessChatLink link not found."));
if (done) done();
}
}).fail([=](const MTP::Error &error) {
const auto type = error.type();
if (done) done();
}).send();
}
void ChatLinks::preload() {
if (_loaded || _requestId) {
return;
}
_requestId = _api->request(MTPaccount_GetBusinessChatLinks(
)).done([=](const MTPaccount_BusinessChatLinks &result) {
const auto &data = result.data();
const auto session = &_api->session();
const auto owner = &session->data();
owner->processUsers(data.vusers());
owner->processChats(data.vchats());
auto links = std::vector<Link>();
links.reserve(data.vlinks().v.size());
for (const auto &link : data.vlinks().v) {
links.push_back(FromMTP(session, link));
}
_list = std::move(links);
_loaded = true;
_loadedUpdates.fire({});
}).fail([=] {
_requestId = 0;
_loaded = true;
_loadedUpdates.fire({});
}).send();
}
const std::vector<ChatLink> &ChatLinks::list() const {
return _list;
}
bool ChatLinks::loaded() const {
return _loaded;
}
rpl::producer<> ChatLinks::loadedUpdates() const {
return _loadedUpdates.events();
}
rpl::producer<ChatLinks::Update> ChatLinks::updates() const {
return _updates.events();
}
} // namespace Api

View file

@ -0,0 +1,64 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class ApiWrap;
namespace Api {
struct ChatLink {
QString link;
QString title;
TextWithEntities message;
int clicks = 0;
};
struct ChatLinkUpdate {
QString was;
std::optional<ChatLink> now;
};
class ChatLinks final {
public:
explicit ChatLinks(not_null<ApiWrap*> api);
using Link = ChatLink;
using Update = ChatLinkUpdate;
void create(
const QString &title,
const TextWithEntities &message,
Fn<void(Link)> done = nullptr);
void edit(
const QString &link,
const QString &title,
const TextWithEntities &message,
Fn<void(Link)> done = nullptr);
void destroy(
const QString &link,
Fn<void()> done = nullptr);
void preload();
[[nodiscard]] const std::vector<ChatLink> &list() const;
[[nodiscard]] bool loaded() const;
[[nodiscard]] rpl::producer<> loadedUpdates() const;
[[nodiscard]] rpl::producer<Update> updates() const;
private:
const not_null<ApiWrap*> _api;
std::vector<Link> _list;
rpl::event_stream<> _loadedUpdates;
mtpRequestId _requestId = 0;
bool _loaded = false;
rpl::event_stream<Update> _updates;
};
} // namespace Api

View file

@ -487,9 +487,9 @@ void ChatParticipants::requestCountDelayed(
}
void ChatParticipants::add(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
const std::vector<not_null<UserData*>> &users,
std::shared_ptr<Ui::Show> show,
bool passGroupHistory,
Fn<void(bool)> done) {
if (const auto chat = peer->asChat()) {
@ -498,19 +498,28 @@ void ChatParticipants::add(
chat->inputChat,
user->inputUser,
MTP_int(passGroupHistory ? kForwardMessagesOnAdd : 0)
)).done([=](const MTPUpdates &result) {
chat->session().api().applyUpdates(result);
)).done([=](const MTPmessages_InvitedUsers &result) {
const auto &data = result.data();
chat->session().api().applyUpdates(data.vupdates());
if (done) done(true);
ChatInviteForbidden(
show,
chat,
CollectForbiddenUsers(&chat->session(), result));
}).fail([=](const MTP::Error &error) {
const auto type = error.type();
ShowAddParticipantsError(type, peer, { 1, user }, show);
ShowAddParticipantsError(show, type, peer, user);
if (done) done(false);
}).afterDelay(kSmallDelayMs).send();
}
} else if (const auto channel = peer->asChannel()) {
const auto hasBot = ranges::any_of(users, &UserData::isBot);
if (!peer->isMegagroup() && hasBot) {
ShowAddParticipantsError("USER_BOT", peer, users, show);
ShowAddParticipantsError(
show,
u"USER_BOT"_q,
peer,
{ .users = users });
return;
}
auto list = QVector<MTPInputUser>();
@ -520,8 +529,9 @@ void ChatParticipants::add(
_api.request(MTPchannels_InviteToChannel(
channel->inputChannel,
MTP_vector<MTPInputUser>(list)
)).done([=](const MTPUpdates &result) {
channel->session().api().applyUpdates(result);
)).done([=](const MTPmessages_InvitedUsers &result) {
const auto &data = result.data();
channel->session().api().applyUpdates(data.vupdates());
requestCountDelayed(channel);
if (callback) callback(true);
ChatInviteForbidden(
@ -529,7 +539,9 @@ void ChatParticipants::add(
channel,
CollectForbiddenUsers(&channel->session(), result));
}).fail([=](const MTP::Error &error) {
ShowAddParticipantsError(error.type(), peer, users, show);
ShowAddParticipantsError(show, error.type(), peer, {
.users = users,
});
if (callback) callback(false);
}).afterDelay(kSmallDelayMs).send();
};

View file

@ -97,9 +97,9 @@ public:
not_null<ChannelData*> channel,
const TLMembers &data);
void add(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
const std::vector<not_null<UserData*>> &users,
std::shared_ptr<Ui::Show> show = nullptr,
bool passGroupHistory = true,
Fn<void(bool)> done = nullptr);

View file

@ -0,0 +1,88 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_earn.h"
#include "api/api_cloud_password.h"
#include "apiwrap.h"
#include "boxes/passcode_box.h"
#include "data/data_channel.h"
#include "data/data_session.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/basic_click_handlers.h"
#include "ui/widgets/buttons.h"
namespace Api {
void RestrictSponsored(
not_null<ChannelData*> channel,
bool restricted,
Fn<void(QString)> failed) {
channel->session().api().request(MTPchannels_RestrictSponsoredMessages(
channel->inputChannel,
MTP_bool(restricted))
).done([=](const MTPUpdates &updates) {
channel->session().api().applyUpdates(updates);
}).fail([=](const MTP::Error &error) {
failed(error.type());
}).send();
}
void HandleWithdrawalButton(
not_null<ChannelData*> channel,
not_null<Ui::RippleButton*> button,
std::shared_ptr<Ui::Show> show) {
struct State {
rpl::lifetime lifetime;
bool loading = false;
};
const auto state = button->lifetime().make_state<State>();
const auto session = &channel->session();
session->api().cloudPassword().reload();
button->setClickedCallback([=] {
if (state->loading) {
return;
}
state->loading = true;
state->lifetime = session->api().cloudPassword().state(
) | rpl::take(
1
) | rpl::start_with_next([=](const Core::CloudPasswordState &pass) {
state->loading = false;
auto fields = PasscodeBox::CloudFields::From(pass);
fields.customTitle =
tr::lng_channel_earn_balance_password_title();
fields.customDescription =
tr::lng_channel_earn_balance_password_description(tr::now);
fields.customSubmitButton = tr::lng_passcode_submit();
fields.customCheckCallback = crl::guard(button, [=](
const Core::CloudPasswordResult &result) {
session->api().request(
MTPstats_GetBroadcastRevenueWithdrawalUrl(
channel->inputChannel,
result.result
)).done([=](const MTPstats_BroadcastRevenueWithdrawalUrl &r) {
const auto url = qs(r.data().vurl());
if (!url.isEmpty()) {
UrlClickHandler::Open(url);
}
}).fail([=](const MTP::Error &error) {
show->showToast(error.type());
}).send();
});
show->show(Box<PasscodeBox>(session, fields));
});
});
}
} // namespace Api

View file

@ -0,0 +1,29 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class ChannelData;
namespace Ui {
class RippleButton;
class Show;
} // namespace Ui
namespace Api {
void RestrictSponsored(
not_null<ChannelData*> channel,
bool restricted,
Fn<void(QString)> failed);
void HandleWithdrawalButton(
not_null<ChannelData*> channel,
not_null<Ui::RippleButton*> button,
std::shared_ptr<Ui::Show> show);
} // namespace Api

View file

@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
namespace Api {
@ -40,9 +39,9 @@ void GlobalPrivacy::reload(Fn<void()> callback) {
}
}).send();
_session->account().appConfig().value(
_session->appConfig().value(
) | rpl::start_with_next([=] {
_showArchiveAndMute = _session->account().appConfig().get<bool>(
_showArchiveAndMute = _session->appConfig().get<bool>(
u"autoarchive_setting_available"_q,
false);
}, _session->lifetime());
@ -75,12 +74,12 @@ rpl::producer<bool> GlobalPrivacy::showArchiveAndMute() const {
}
rpl::producer<> GlobalPrivacy::suggestArchiveAndMute() const {
return _session->account().appConfig().suggestionRequested(
return _session->appConfig().suggestionRequested(
u"AUTOARCHIVE_POPULAR"_q);
}
void GlobalPrivacy::dismissArchiveAndMuteSuggestion() {
_session->account().appConfig().dismissSuggestion(
_session->appConfig().dismissSuggestion(
u"AUTOARCHIVE_POPULAR"_q);
}
@ -142,6 +141,8 @@ void GlobalPrivacy::update(
using Flag = MTPDglobalPrivacySettings::Flag;
_api.request(_requestId).cancel();
const auto newRequirePremiumAllowed = _session->premium()
|| _session->appConfig().newRequirePremiumFree();
const auto flags = Flag()
| (archiveAndMute
? Flag::f_archive_and_mute_new_noncontact_peers
@ -153,7 +154,7 @@ void GlobalPrivacy::update(
? Flag::f_keep_archived_folders
: Flag())
| (hideReadTime ? Flag::f_hide_read_marks : Flag())
| ((newRequirePremium && _session->premium())
| ((newRequirePremium && newRequirePremiumAllowed)
? Flag::f_new_noncontact_peers_require_premium
: Flag());
_requestId = _api.request(MTPaccount_SetGlobalPrivacySettings(

View file

@ -19,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h"
#include "history/history.h"
#include "history/history_item.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "payments/payments_form.h"
@ -109,6 +108,18 @@ rpl::producer<> Premium::cloudSetUpdated() const {
return _cloudSetUpdated.events();
}
auto Premium::helloStickers() const
-> const std::vector<not_null<DocumentData*>> & {
if (_helloStickers.empty()) {
const_cast<Premium*>(this)->reloadHelloStickers();
}
return _helloStickers;
}
rpl::producer<> Premium::helloStickersUpdated() const {
return _helloStickersUpdated.events();
}
int64 Premium::monthlyAmount() const {
return _monthlyAmount;
}
@ -225,6 +236,33 @@ void Premium::reloadCloudSet() {
}).send();
}
void Premium::reloadHelloStickers() {
if (_helloStickersRequestId) {
return;
}
_helloStickersRequestId = _api.request(MTPmessages_GetStickers(
MTP_string("\xf0\x9f\x91\x8b\xe2\xad\x90\xef\xb8\x8f"),
MTP_long(_helloStickersHash)
)).done([=](const MTPmessages_Stickers &result) {
_helloStickersRequestId = 0;
result.match([&](const MTPDmessages_stickersNotModified &) {
}, [&](const MTPDmessages_stickers &data) {
_helloStickersHash = data.vhash().v;
const auto owner = &_session->data();
_helloStickers.clear();
for (const auto &sticker : data.vstickers().v) {
const auto document = owner->processDocument(sticker);
if (document->sticker()) {
_helloStickers.push_back(document);
}
}
_helloStickersUpdated.fire({});
});
}).fail([=] {
_helloStickersRequestId = 0;
}).send();
}
void Premium::checkGiftCode(
const QString &slug,
Fn<void(GiftCode)> done) {
@ -532,34 +570,34 @@ Data::SubscriptionOptions PremiumGiftCodeOptions::options(int amount) {
int PremiumGiftCodeOptions::giveawayBoostsPerPremium() const {
constexpr auto kFallbackCount = 4;
return _peer->session().account().appConfig().get<int>(
return _peer->session().appConfig().get<int>(
u"giveaway_boosts_per_premium"_q,
kFallbackCount);
}
int PremiumGiftCodeOptions::giveawayCountriesMax() const {
constexpr auto kFallbackCount = 10;
return _peer->session().account().appConfig().get<int>(
return _peer->session().appConfig().get<int>(
u"giveaway_countries_max"_q,
kFallbackCount);
}
int PremiumGiftCodeOptions::giveawayAddPeersMax() const {
constexpr auto kFallbackCount = 10;
return _peer->session().account().appConfig().get<int>(
return _peer->session().appConfig().get<int>(
u"giveaway_add_peers_max"_q,
kFallbackCount);
}
int PremiumGiftCodeOptions::giveawayPeriodMax() const {
constexpr auto kFallbackCount = 3600 * 24 * 7;
return _peer->session().account().appConfig().get<int>(
return _peer->session().appConfig().get<int>(
u"giveaway_period_max"_q,
kFallbackCount);
}
bool PremiumGiftCodeOptions::giveawayGiftsPurchaseAvailable() const {
return _peer->session().account().appConfig().get<bool>(
return _peer->session().appConfig().get<bool>(
u"giveaway_gifts_purchase_available"_q,
false);
}
@ -609,4 +647,24 @@ RequirePremiumState ResolveRequiresPremiumToWrite(
return RequirePremiumState::Unknown;
}
rpl::producer<DocumentData*> RandomHelloStickerValue(
not_null<Main::Session*> session) {
const auto premium = &session->api().premium();
const auto random = [=] {
const auto &v = premium->helloStickers();
Assert(!v.empty());
return v[base::RandomIndex(v.size())].get();
};
const auto &v = premium->helloStickers();
if (!v.empty()) {
return rpl::single(random());
}
return rpl::single<DocumentData*>(
nullptr
) | rpl::then(premium->helloStickersUpdated(
) | rpl::filter([=] {
return !premium->helloStickers().empty();
}) | rpl::take(1) | rpl::map(random));
}
} // namespace Api

View file

@ -85,6 +85,10 @@ public:
-> const std::vector<not_null<DocumentData*>> &;
[[nodiscard]] rpl::producer<> cloudSetUpdated() const;
[[nodiscard]] auto helloStickers() const
-> const std::vector<not_null<DocumentData*>> &;
[[nodiscard]] rpl::producer<> helloStickersUpdated() const;
[[nodiscard]] int64 monthlyAmount() const;
[[nodiscard]] QString monthlyCurrency() const;
@ -111,6 +115,7 @@ private:
void reloadPromo();
void reloadStickers();
void reloadCloudSet();
void reloadHelloStickers();
void requestPremiumRequiredSlice();
const not_null<Main::Session*> _session;
@ -133,6 +138,11 @@ private:
std::vector<not_null<DocumentData*>> _cloudSet;
rpl::event_stream<> _cloudSetUpdated;
mtpRequestId _helloStickersRequestId = 0;
uint64 _helloStickersHash = 0;
std::vector<not_null<DocumentData*>> _helloStickers;
rpl::event_stream<> _helloStickersUpdated;
int64 _monthlyAmount = 0;
QString _monthlyCurrency;
@ -215,4 +225,7 @@ enum class RequirePremiumState {
not_null<PeerData*> peer,
History *maybeHistory);
[[nodiscard]] rpl::producer<DocumentData*> RandomHelloStickerValue(
not_null<Main::Session*> session);
} // namespace Api

View file

@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_origin.h"
#include "data/data_session.h"
#include "data/notify/data_notify_settings.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "storage/file_upload.h"
@ -191,20 +190,20 @@ void Ringtones::remove(DocumentId id) {
}
int64 Ringtones::maxSize() const {
return _session->account().appConfig().get<int>(
"ringtone_size_max",
return _session->appConfig().get<int>(
u"ringtone_size_max"_q,
100 * 1024);
}
int Ringtones::maxSavedCount() const {
return _session->account().appConfig().get<int>(
"ringtone_saved_count_max",
return _session->appConfig().get<int>(
u"ringtone_saved_count_max"_q,
100);
}
crl::time Ringtones::maxDuration() const {
return crl::time(1000) * _session->account().appConfig().get<int>(
"ringtone_duration_max",
return crl::time(1000) * _session->appConfig().get<int>(
u"ringtone_duration_max"_q,
5);
}

View file

@ -28,7 +28,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_entity.h" // TextWithEntities.
#include "ui/item_text_options.h" // Ui::ItemTextOptions.
#include "main/main_session.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "storage/localimageloader.h"
#include "storage/file_upload.h"
@ -239,8 +238,7 @@ bool SendDice(MessageToSend &message) {
|| !message.textWithTags.tags.isEmpty()) {
return false;
}
auto &account = message.action.history->session().account();
auto &config = account.appConfig();
auto &config = message.action.history->session().appConfig();
static const auto hardcoded = std::vector<QString>{
Stickers::DicePacks::kDiceString,
Stickers::DicePacks::kDartString,

View file

@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
namespace Api {
@ -22,7 +21,7 @@ constexpr auto kRefreshAppConfigTimeout = 3 * crl::time(1000);
SensitiveContent::SensitiveContent(not_null<ApiWrap*> api)
: _session(&api->session())
, _api(&api->instance())
, _appConfigReloadTimer([=] { _session->account().appConfig().refresh(); }) {
, _appConfigReloadTimer([=] { _session->appConfig().refresh(); }) {
}
void SensitiveContent::reload() {

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_statistics.h"
#include "apiwrap.h"
#include "base/unixtime.h"
#include "data/data_channel.h"
#include "data/data_session.h"
#include "data/data_stories.h"
@ -746,4 +747,125 @@ Data::BoostStatus Boosts::boostStatus() const {
return _boostStatus;
}
EarnStatistics::EarnStatistics(not_null<ChannelData*> channel)
: StatisticsRequestSender(channel) {
}
rpl::producer<rpl::no_value, QString> EarnStatistics::request() {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
makeRequest(MTPstats_GetBroadcastRevenueStats(
MTP_flags(0),
channel()->inputChannel
)).done([=](const MTPstats_BroadcastRevenueStats &result) {
const auto &data = result.data();
_data = Data::EarnStatistics{
.topHoursGraph = StatisticalGraphFromTL(
data.vtop_hours_graph()),
.revenueGraph = StatisticalGraphFromTL(data.vrevenue_graph()),
.currentBalance = data.vcurrent_balance().v,
.availableBalance = data.vavailable_balance().v,
.overallRevenue = data.voverall_revenue().v,
.usdRate = data.vusd_rate().v,
};
requestHistory({}, [=](Data::EarnHistorySlice &&slice) {
_data.firstHistorySlice = std::move(slice);
api().request(
MTPchannels_GetFullChannel(channel()->inputChannel)
).done([=](const MTPmessages_ChatFull &result) {
result.data().vfull_chat().match([&](
const MTPDchannelFull &d) {
_data.switchedOff = d.is_restricted_sponsored();
}, [](const auto &) {
});
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
});
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
return lifetime;
};
}
void EarnStatistics::requestHistory(
const Data::EarnHistorySlice::OffsetToken &token,
Fn<void(Data::EarnHistorySlice)> done) {
if (_requestId) {
return;
}
constexpr auto kTlFirstSlice = tl::make_int(kFirstSlice);
constexpr auto kTlLimit = tl::make_int(kLimit);
_requestId = api().request(MTPstats_GetBroadcastRevenueTransactions(
channel()->inputChannel,
MTP_int(token),
(!token) ? kTlFirstSlice : kTlLimit
)).done([=](const MTPstats_BroadcastRevenueTransactions &result) {
_requestId = 0;
const auto &tlTransactions = result.data().vtransactions().v;
auto list = std::vector<Data::EarnHistoryEntry>();
list.reserve(tlTransactions.size());
for (const auto &tlTransaction : tlTransactions) {
list.push_back(tlTransaction.match([&](
const MTPDbroadcastRevenueTransactionProceeds &d) {
return Data::EarnHistoryEntry{
.type = Data::EarnHistoryEntry::Type::In,
.amount = d.vamount().v,
.date = base::unixtime::parse(d.vfrom_date().v),
.dateTo = base::unixtime::parse(d.vto_date().v),
};
}, [&](const MTPDbroadcastRevenueTransactionWithdrawal &d) {
return Data::EarnHistoryEntry{
.type = Data::EarnHistoryEntry::Type::Out,
.status = d.is_pending()
? Data::EarnHistoryEntry::Status::Pending
: d.is_failed()
? Data::EarnHistoryEntry::Status::Failed
: Data::EarnHistoryEntry::Status::Success,
.amount = (std::numeric_limits<Data::EarnInt>::max()
- d.vamount().v
+ 1),
.date = base::unixtime::parse(d.vdate().v),
// .provider = qs(d.vprovider()),
.successDate = d.vtransaction_date()
? base::unixtime::parse(d.vtransaction_date()->v)
: QDateTime(),
.successLink = d.vtransaction_url()
? qs(*d.vtransaction_url())
: QString(),
};
}, [&](const MTPDbroadcastRevenueTransactionRefund &d) {
return Data::EarnHistoryEntry{
.type = Data::EarnHistoryEntry::Type::Return,
.amount = d.vamount().v,
.date = base::unixtime::parse(d.vdate().v),
// .provider = qs(d.vprovider()),
};
}));
}
const auto nextToken = token + tlTransactions.size();
done(Data::EarnHistorySlice{
.list = std::move(list),
.total = result.data().vcount().v,
.allLoaded = (result.data().vcount().v == nextToken),
.token = Data::EarnHistorySlice::OffsetToken(nextToken),
});
}).fail([=] {
_requestId = 0;
}).send();
}
Data::EarnStatistics EarnStatistics::data() const {
return _data;
}
} // namespace Api

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h"
#include "data/data_boosts.h"
#include "data/data_channel_earn.h"
#include "data/data_statistics.h"
#include "mtproto/sender.h"
@ -107,6 +108,27 @@ private:
};
class EarnStatistics final : public StatisticsRequestSender {
public:
explicit EarnStatistics(not_null<ChannelData*> channel);
[[nodiscard]] rpl::producer<rpl::no_value, QString> request();
void requestHistory(
const Data::EarnHistorySlice::OffsetToken &token,
Fn<void(Data::EarnHistorySlice)> done);
[[nodiscard]] Data::EarnStatistics data() const;
static constexpr auto kFirstSlice = int(5);
static constexpr auto kLimit = int(10);
private:
Data::EarnStatistics _data;
mtpRequestId _requestId = 0;
};
class Boosts final {
public:
explicit Boosts(not_null<PeerData*> peer);

View file

@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_item_helpers.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
@ -36,10 +35,10 @@ bool Transcribes::freeFor(not_null<HistoryItem*> item) const {
bool Transcribes::trialsSupport() {
if (!_trialsSupport) {
const auto count = _session->account().appConfig().get<int>(
const auto count = _session->appConfig().get<int>(
u"transcribe_audio_trial_weekly_number"_q,
0);
const auto until = _session->account().appConfig().get<int>(
const auto until = _session->appConfig().get<int>(
u"transcribe_audio_trial_cooldown_until"_q,
0);
_trialsSupport = (count > 0) || (until > 0);
@ -49,7 +48,7 @@ bool Transcribes::trialsSupport() {
TimeId Transcribes::trialsRefreshAt() {
if (_trialsRefreshAt < 0) {
_trialsRefreshAt = _session->account().appConfig().get<int>(
_trialsRefreshAt = _session->appConfig().get<int>(
u"transcribe_audio_trial_cooldown_until"_q,
0);
}
@ -58,7 +57,7 @@ TimeId Transcribes::trialsRefreshAt() {
int Transcribes::trialsCount() {
if (_trialsCount < 0) {
_trialsCount = _session->account().appConfig().get<int>(
_trialsCount = _session->appConfig().get<int>(
u"transcribe_audio_trial_weekly_number"_q,
-1);
return std::max(_trialsCount, 0);
@ -67,7 +66,7 @@ int Transcribes::trialsCount() {
}
crl::time Transcribes::trialsMaxLengthMs() const {
return 1000 * _session->account().appConfig().get<int>(
return 1000 * _session->appConfig().get<int>(
u"transcribe_audio_trial_duration_max"_q,
300);
}

View file

@ -1128,6 +1128,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTPPeer(), // saved_peer_id
d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
MTP_long(d.vvia_bot_id().value_or_empty()),
MTPlong(), // via_business_bot_id
d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(),
d.vdate(),
d.vmessage(),
@ -1162,6 +1163,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTPPeer(), // saved_peer_id
d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
MTP_long(d.vvia_bot_id().value_or_empty()),
MTPlong(), // via_business_bot_id
d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(),
d.vdate(),
d.vmessage(),
@ -1949,7 +1951,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
const auto &d = update.c_updatePeerSettings();
const auto peerId = peerFromMTP(d.vpeer());
if (const auto peer = session().data().peerLoaded(peerId)) {
peer->setSettings(d.vsettings());
peer->setBarSettings(d.vsettings());
}
} break;

View file

@ -26,7 +26,9 @@ using TLInputRules = MTPVector<MTPInputPrivacyRule>;
using TLRules = MTPVector<MTPPrivacyRule>;
TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
const auto collectInputUsers = [](const auto &peers) {
using Exceptions = UserPrivacy::Exceptions;
const auto collectInputUsers = [](const Exceptions &exceptions) {
const auto &peers = exceptions.peers;
auto result = QVector<MTPInputUser>();
result.reserve(peers.size());
for (const auto &peer : peers) {
@ -36,7 +38,8 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
}
return result;
};
const auto collectInputChats = [](const auto &peers) {
const auto collectInputChats = [](const Exceptions &exceptions) {
const auto &peers = exceptions.peers;
auto result = QVector<MTPlong>();
result.reserve(peers.size());
for (const auto &peer : peers) {
@ -47,6 +50,7 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
return result;
};
using Option = UserPrivacy::Option;
auto result = QVector<MTPInputPrivacyRule>();
result.reserve(kMaxRules);
if (!rule.ignoreAlways) {
@ -62,6 +66,9 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
MTP_inputPrivacyValueAllowChatParticipants(
MTP_vector<MTPlong>(chats)));
}
if (rule.always.premiums && (rule.option != Option::Everyone)) {
result.push_back(MTP_inputPrivacyValueAllowPremium());
}
}
if (!rule.ignoreNever) {
const auto users = collectInputUsers(rule.never);
@ -78,7 +85,6 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
}
}
result.push_back([&] {
using Option = UserPrivacy::Option;
switch (rule.option) {
case Option::Everyone: return MTP_inputPrivacyValueAllowAll();
case Option::Contacts: return MTP_inputPrivacyValueAllowContacts();
@ -101,12 +107,14 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) {
auto result = UserPrivacy::Rule();
auto optionSet = false;
const auto setOption = [&](Option option) {
if (optionSet) return;
if (optionSet) {
return;
}
optionSet = true;
result.option = option;
};
auto &always = result.always;
auto &never = result.never;
auto &always = result.always.peers;
auto &never = result.never.peers;
const auto feed = [&](const MTPPrivacyRule &rule) {
rule.match([&](const MTPDprivacyValueAllowAll &) {
setOption(Option::Everyone);
@ -114,6 +122,8 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) {
setOption(Option::Contacts);
}, [&](const MTPDprivacyValueAllowCloseFriends &) {
setOption(Option::CloseFriends);
}, [&](const MTPDprivacyValueAllowPremium &) {
result.always.premiums = true;
}, [&](const MTPDprivacyValueAllowUsers &data) {
const auto &users = data.vusers().v;
always.reserve(always.size() + users.size());
@ -188,6 +198,7 @@ MTPInputPrivacyKey KeyToTL(UserPrivacy::Key key) {
case Key::ProfilePhoto: return MTP_inputPrivacyKeyProfilePhoto();
case Key::Voices: return MTP_inputPrivacyKeyVoiceMessages();
case Key::About: return MTP_inputPrivacyKeyAbout();
case Key::Birthday: return MTP_inputPrivacyKeyBirthday();
}
Unexpected("Key in Api::UserPrivacy::KetToTL.");
}
@ -215,6 +226,8 @@ std::optional<UserPrivacy::Key> TLToKey(mtpTypeId type) {
case mtpc_inputPrivacyKeyVoiceMessages: return Key::Voices;
case mtpc_privacyKeyAbout:
case mtpc_inputPrivacyKeyAbout: return Key::About;
case mtpc_privacyKeyBirthday:
case mtpc_inputPrivacyKeyBirthday: return Key::Birthday;
}
return std::nullopt;
}

View file

@ -30,6 +30,7 @@ public:
ProfilePhoto,
Voices,
About,
Birthday,
};
enum class Option {
Everyone,
@ -37,10 +38,14 @@ public:
CloseFriends,
Nobody,
};
struct Exceptions {
std::vector<not_null<PeerData*>> peers;
bool premiums = false;
};
struct Rule {
Option option = Option::Everyone;
std::vector<not_null<PeerData*>> always;
std::vector<not_null<PeerData*>> never;
Exceptions always;
Exceptions never;
bool ignoreAlways = false;
bool ignoreNever = false;
};

View file

@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "base/unixtime.h"
#include "base/weak_ptr.h"
#include "ui/controls/who_reacted_context_action.h"
@ -697,7 +696,7 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|| user->readDatesPrivate()) {
return false;
}
const auto &appConfig = peer->session().account().appConfig();
const auto &appConfig = peer->session().appConfig();
const auto expirePeriod = appConfig.get<int>(
"pm_read_date_expire_period",
7 * 86400);
@ -713,7 +712,7 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
&& (megagroup->flags() & ChannelDataFlag::ParticipantsHidden))) {
return false;
}
const auto &appConfig = peer->session().account().appConfig();
const auto &appConfig = peer->session().appConfig();
const auto expirePeriod = appConfig.get<int>(
"chat_read_mark_expire_period",
7 * 86400);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_authorizations.h"
#include "api/api_attached_stickers.h"
#include "api/api_blocked_peers.h"
#include "api/api_chat_links.h"
#include "api/api_chat_participants.h"
#include "api/api_cloud_password.h"
#include "api/api_hash.h"
@ -169,6 +170,7 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
, _globalPrivacy(std::make_unique<Api::GlobalPrivacy>(this))
, _userPrivacy(std::make_unique<Api::UserPrivacy>(this))
, _inviteLinks(std::make_unique<Api::InviteLinks>(this))
, _chatLinks(std::make_unique<Api::ChatLinks>(this))
, _views(std::make_unique<Api::ViewsManager>(this))
, _confirmPhone(std::make_unique<Api::ConfirmPhone>(this))
, _peerPhoto(std::make_unique<Api::PeerPhoto>(this))
@ -1246,7 +1248,7 @@ void ApiWrap::requestPeerSettings(not_null<PeerData*> peer) {
result.match([&](const MTPDmessages_peerSettings &data) {
_session->data().processUsers(data.vusers());
_session->data().processChats(data.vchats());
peer->setSettings(data.vsettings());
peer->setBarSettings(data.vsettings());
_requestedPeerSettings.erase(peer);
});
}).fail([=] {
@ -1775,7 +1777,7 @@ void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
}
return QString();
}();
if (!text.isEmpty()) {
if (show && !text.isEmpty()) {
show->showToast(text, kJoinErrorDuration);
}
}
@ -2581,6 +2583,10 @@ void ApiWrap::refreshFileReference(
request(MTPaccount_GetSavedRingtones(MTP_long(0)));
}, [&](Data::FileOriginPremiumPreviews data) {
request(MTPhelp_GetPremiumPromo());
}, [&](Data::FileOriginWebPage data) {
request(MTPmessages_GetWebPage(
MTP_string(data.url),
MTP_int(0)));
}, [&](Data::FileOriginStory data) {
request(MTPstories_GetStoriesByID(
_session->data().peer(data.peer)->input,
@ -3667,11 +3673,16 @@ void ApiWrap::cancelLocalItem(not_null<HistoryItem*> item) {
void ApiWrap::sendShortcutMessages(
not_null<PeerData*> peer,
BusinessShortcutId id) {
auto ids = QVector<MTPint>();
auto randomIds = QVector<MTPlong>();
request(MTPmessages_SendQuickReplyMessages(
peer->input,
MTP_int(id)
MTP_int(id),
MTP_vector<MTPint>(ids),
MTP_vector<MTPlong>(randomIds)
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
}).fail([=](const MTP::Error &error) {
}).send();
}
@ -3903,13 +3914,14 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
}
void ApiWrap::sendBotStart(
std::shared_ptr<Ui::Show> show,
not_null<UserData*> bot,
PeerData *chat,
const QString &startTokenForChat) {
Expects(bot->isBot());
if (chat && chat->isChannel() && !chat->isMegagroup()) {
ShowAddParticipantsError("USER_BOT", chat, { 1, bot });
ShowAddParticipantsError(show, "USER_BOT", chat, bot);
return;
}
@ -3943,7 +3955,7 @@ void ApiWrap::sendBotStart(
}).fail([=](const MTP::Error &error) {
if (chat) {
const auto type = error.type();
ShowAddParticipantsError(type, chat, { 1, bot });
ShowAddParticipantsError(show, type, chat, bot);
}
}).send();
}
@ -4059,6 +4071,8 @@ void ApiWrap::uploadAlbumMedia(
};
request(MTPmessages_UploadMedia(
MTP_flags(0),
MTPstring(), // business_connection_id
item->history()->peer->input,
media
)).done([=](const MTPMessageMedia &result) {
@ -4495,6 +4509,10 @@ Api::InviteLinks &ApiWrap::inviteLinks() {
return *_inviteLinks;
}
Api::ChatLinks &ApiWrap::chatLinks() {
return *_chatLinks;
}
Api::ViewsManager &ApiWrap::views() {
return *_views;
}

View file

@ -53,6 +53,7 @@ class Key;
namespace Ui {
struct PreparedList;
class Show;
} // namespace Ui
namespace Api {
@ -69,6 +70,7 @@ class SensitiveContent;
class GlobalPrivacy;
class UserPrivacy;
class InviteLinks;
class ChatLinks;
class ViewsManager;
class ConfirmPhone;
class PeerPhoto;
@ -342,6 +344,7 @@ public:
BusinessShortcutId id);
void sendMessage(MessageToSend &&message);
void sendBotStart(
std::shared_ptr<Ui::Show> show,
not_null<UserData*> bot,
PeerData *chat = nullptr,
const QString &startTokenForChat = QString());
@ -384,6 +387,7 @@ public:
[[nodiscard]] Api::GlobalPrivacy &globalPrivacy();
[[nodiscard]] Api::UserPrivacy &userPrivacy();
[[nodiscard]] Api::InviteLinks &inviteLinks();
[[nodiscard]] Api::ChatLinks &chatLinks();
[[nodiscard]] Api::ViewsManager &views();
[[nodiscard]] Api::ConfirmPhone &confirmPhone();
[[nodiscard]] Api::PeerPhoto &peerPhoto();
@ -703,6 +707,7 @@ private:
const std::unique_ptr<Api::GlobalPrivacy> _globalPrivacy;
const std::unique_ptr<Api::UserPrivacy> _userPrivacy;
const std::unique_ptr<Api::InviteLinks> _inviteLinks;
const std::unique_ptr<Api::ChatLinks> _chatLinks;
const std::unique_ptr<Api::ViewsManager> _views;
const std::unique_ptr<Api::ConfirmPhone> _confirmPhone;
const std::unique_ptr<Api::PeerPhoto> _peerPhoto;

View file

@ -66,11 +66,12 @@ void ChatCreateDone(
not_null<Window::SessionNavigation*> navigation,
QImage image,
TimeId ttlPeriod,
const MTPUpdates &updates,
const MTPmessages_InvitedUsers &result,
Fn<void(not_null<PeerData*>)> done) {
navigation->session().api().applyUpdates(updates);
const auto &data = result.data();
navigation->session().api().applyUpdates(data.vupdates());
const auto success = base::make_optional(&updates)
const auto success = base::make_optional(&data.vupdates())
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
switch (updates->type()) {
case mtpc_updates:
@ -109,7 +110,7 @@ void ChatCreateDone(
ChatInviteForbidden(
show,
chat,
CollectForbiddenUsers(&chat->session(), updates));
CollectForbiddenUsers(&chat->session(), result));
}
};
if (!success) {
@ -168,19 +169,31 @@ TextWithEntities PeerFloodErrorText(
}
void ShowAddParticipantsError(
std::shared_ptr<Ui::Show> show,
const QString &error,
not_null<PeerData*> chat,
const std::vector<not_null<UserData*>> &users,
std::shared_ptr<Ui::Show> show) {
not_null<UserData*> user) {
ShowAddParticipantsError(
std::move(show),
error,
chat,
{ .users = { 1, user } });
}
void ShowAddParticipantsError(
std::shared_ptr<Ui::Show> show,
const QString &error,
not_null<PeerData*> chat,
const ForbiddenInvites &forbidden) {
if (error == u"USER_BOT"_q) {
const auto channel = chat->asChannel();
if ((users.size() == 1)
&& users.front()->isBot()
if ((forbidden.users.size() == 1)
&& forbidden.users.front()->isBot()
&& channel
&& !channel->isMegagroup()
&& channel->canAddAdmins()) {
const auto makeAdmin = [=] {
const auto user = users.front();
const auto user = forbidden.users.front();
const auto weak = std::make_shared<QPointer<EditAdminBox>>();
const auto close = [=](auto&&...) {
if (*weak) {
@ -188,6 +201,7 @@ void ShowAddParticipantsError(
}
};
const auto saveCallback = SaveAdminCallback(
show,
channel,
user,
close,
@ -198,9 +212,10 @@ void ShowAddParticipantsError(
ChatAdminRightsInfo(),
QString());
box->setSaveCallback(saveCallback);
*weak = Ui::show(std::move(box));
*weak = box.data();
show->showBox(std::move(box));
};
Ui::show(
show->showBox(
Ui::MakeConfirmBox({
.text = tr::lng_cant_invite_offer_admin(),
.confirmed = makeAdmin,
@ -210,7 +225,7 @@ void ShowAddParticipantsError(
return;
}
}
const auto hasBot = ranges::any_of(users, &UserData::isBot);
const auto hasBot = ranges::any_of(forbidden.users, &UserData::isBot);
if (error == u"PEER_FLOOD"_q) {
const auto type = (chat->isChat() || chat->isMegagroup())
? PeerFloodType::InviteGroup
@ -218,8 +233,8 @@ void ShowAddParticipantsError(
const auto text = PeerFloodErrorText(&chat->session(), type);
Ui::show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther);
return;
} else if (error == u"USER_PRIVACY_RESTRICTED"_q && show) {
ChatInviteForbidden(show, chat, users);
} else if (error == u"USER_PRIVACY_RESTRICTED"_q) {
ChatInviteForbidden(show, chat, forbidden);
return;
}
const auto text = [&] {
@ -230,8 +245,6 @@ void ShowAddParticipantsError(
} else if (error == u"USER_KICKED"_q) {
// Trying to return a user who was kicked by admin.
return tr::lng_cant_invite_banned(tr::now);
} else if (error == u"USER_PRIVACY_RESTRICTED"_q) {
return tr::lng_cant_invite_privacy(tr::now);
} else if (error == u"USER_NOT_MUTUAL_CONTACT"_q) {
// Trying to return user who does not have me in contacts.
return tr::lng_failed_add_not_mutual(tr::now);
@ -246,7 +259,7 @@ void ShowAddParticipantsError(
}
return tr::lng_failed_add_participant(tr::now);
}();
Ui::show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther);
show->show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther);
}
AddContactBox::AddContactBox(
@ -708,7 +721,7 @@ void GroupInfoBox::createGroup(
MTP_vector<TLUsers>(inputs),
MTP_string(title),
MTP_int(ttlPeriod())
)).done([=](const MTPUpdates &result) {
)).done([=](const MTPmessages_InvitedUsers &result) {
auto image = _photo->takeResultImage();
const auto period = ttlPeriod();
const auto navigation = _navigation;

View file

@ -34,6 +34,7 @@ template <typename Enum>
class Radioenum;
class LinkButton;
class UserpicButton;
class Show;
} // namespace Ui
enum class PeerFloodType {
@ -42,14 +43,21 @@ enum class PeerFloodType {
InviteChannel,
};
struct ForbiddenInvites;
[[nodiscard]] TextWithEntities PeerFloodErrorText(
not_null<Main::Session*> session,
PeerFloodType type);
void ShowAddParticipantsError(
std::shared_ptr<Ui::Show> show,
const QString &error,
not_null<PeerData*> chat,
const std::vector<not_null<UserData*>> &users,
std::shared_ptr<Ui::Show> show = nullptr);
const ForbiddenInvites &forbidden);
void ShowAddParticipantsError(
std::shared_ptr<Ui::Show> show,
const QString &error,
not_null<PeerData*> chat,
not_null<UserData*> user);
class AddContactBox : public Ui::BoxContent {
public:

View file

@ -40,11 +40,11 @@ namespace {
constexpr auto kBackgroundsInRow = 3;
QImage TakeMiddleSample(QImage original, QSize size) {
size *= cIntRetinaFactor();
size *= style::DevicePixelRatio();
const auto from = original.size();
if (from.isEmpty()) {
auto result = original.scaled(size);
result.setDevicePixelRatio(cRetinaFactor());
result.setDevicePixelRatio(style::DevicePixelRatio());
return result;
}
@ -58,7 +58,7 @@ QImage TakeMiddleSample(QImage original, QSize size) {
take.width(),
take.height()
).scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
result.setDevicePixelRatio(cRetinaFactor());
result.setDevicePixelRatio(style::DevicePixelRatio());
return result;
}
@ -657,10 +657,10 @@ void BackgroundBox::Inner::validatePaperThumbnail(
} else if (!paper.data.backgroundColors().empty()) {
paper.thumbnail = Ui::PixmapFromImage(
Ui::GenerateBackgroundImage(
st::backgroundSize * cIntRetinaFactor(),
st::backgroundSize * style::DevicePixelRatio(),
paper.data.backgroundColors(),
paper.data.gradientRotation()));
paper.thumbnail.setDevicePixelRatio(cRetinaFactor());
paper.thumbnail.setDevicePixelRatio(style::DevicePixelRatio());
return;
} else {
return;
@ -680,7 +680,7 @@ void BackgroundBox::Inner::validatePaperThumbnail(
paper.thumbnail = Ui::PixmapFromImage(TakeMiddleSample(
original,
st::backgroundSize));
paper.thumbnail.setDevicePixelRatio(cRetinaFactor());
paper.thumbnail.setDevicePixelRatio(style::DevicePixelRatio());
}
bool BackgroundBox::Inner::forChannel() const {

Some files were not shown because too many files have changed in this diff Show more