mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
Merge tag 'v4.16.6' into dev
# Conflicts: # Telegram/Resources/winrc/Telegram.rc # Telegram/Resources/winrc/Updater.rc # Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp # Telegram/SourceFiles/core/version.h # Telegram/SourceFiles/info/profile/info_profile_actions.cpp # Telegram/SourceFiles/mtproto/facade.h # Telegram/SourceFiles/storage/file_upload.cpp # Telegram/SourceFiles/storage/file_upload.h # Telegram/SourceFiles/window/window_main_menu.cpp # Telegram/lib_ui # snap/snapcraft.yaml
This commit is contained in:
commit
6a02bd66a9
233 changed files with 2525 additions and 21320 deletions
6
.github/workflows/linux.yml
vendored
6
.github/workflows/linux.yml
vendored
|
@ -52,7 +52,7 @@ jobs:
|
|||
- "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION"
|
||||
|
||||
env:
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
UPLOAD_ARTIFACT: "true"
|
||||
|
||||
steps:
|
||||
- name: Get repository name.
|
||||
|
@ -115,8 +115,8 @@ jobs:
|
|||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
run: |
|
||||
cd $REPO_NAME/out/Debug
|
||||
mkdir artifact
|
||||
mv {Telegram,Updater} artifact/
|
||||
sudo mkdir artifact
|
||||
sudo mv {Telegram,Updater} artifact/
|
||||
- uses: actions/upload-artifact@master
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
name: Upload artifact.
|
||||
|
|
2
.github/workflows/mac.yml
vendored
2
.github/workflows/mac.yml
vendored
|
@ -47,7 +47,7 @@ jobs:
|
|||
defines:
|
||||
- ""
|
||||
env:
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
UPLOAD_ARTIFACT: "true"
|
||||
ONLY_CACHE: "false"
|
||||
PREPARE_PATH: "Telegram/build/prepare/prepare.py"
|
||||
|
||||
|
|
2
.github/workflows/mac_packaged.yml
vendored
2
.github/workflows/mac_packaged.yml
vendored
|
@ -50,7 +50,7 @@ jobs:
|
|||
env:
|
||||
GIT: "https://github.com"
|
||||
OPENALDIR: "/usr/local/opt/openal-soft"
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
UPLOAD_ARTIFACT: "true"
|
||||
ONLY_CACHE: "false"
|
||||
MANUAL_CACHING: "1"
|
||||
AUTO_CACHING: "1"
|
||||
|
|
2
.github/workflows/snap.yml
vendored
2
.github/workflows/snap.yml
vendored
|
@ -43,7 +43,7 @@ jobs:
|
|||
runs-on: ubuntu-20.04
|
||||
|
||||
env:
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
UPLOAD_ARTIFACT: "true"
|
||||
|
||||
steps:
|
||||
- name: Clone.
|
||||
|
|
2
.github/workflows/win.yml
vendored
2
.github/workflows/win.yml
vendored
|
@ -50,7 +50,7 @@ jobs:
|
|||
generator: ["", "Ninja Multi-Config"]
|
||||
|
||||
env:
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
UPLOAD_ARTIFACT: "true"
|
||||
ONLY_CACHE: "false"
|
||||
PREPARE_PATH: "Telegram/build/prepare/prepare.py"
|
||||
|
||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -76,6 +76,9 @@
|
|||
[submodule "Telegram/lib_webview"]
|
||||
path = Telegram/lib_webview
|
||||
url = https://github.com/desktop-app/lib_webview.git
|
||||
[submodule "Telegram/ThirdParty/jemalloc"]
|
||||
path = Telegram/ThirdParty/jemalloc
|
||||
url = https://github.com/jemalloc/jemalloc
|
||||
[submodule "Telegram/ThirdParty/dispatch"]
|
||||
path = Telegram/ThirdParty/dispatch
|
||||
url = https://github.com/apple/swift-corelibs-libdispatch
|
||||
|
|
|
@ -1574,6 +1574,8 @@ PRIVATE
|
|||
window/window_lock_widgets.h
|
||||
window/window_main_menu.cpp
|
||||
window/window_main_menu.h
|
||||
window/window_main_menu_helpers.cpp
|
||||
window/window_main_menu_helpers.h
|
||||
window/window_media_preview.cpp
|
||||
window/window_media_preview.h
|
||||
window/window_peer_menu.cpp
|
||||
|
|
BIN
Telegram/Resources/animations/collectible_phone.tgs
Normal file
BIN
Telegram/Resources/animations/collectible_phone.tgs
Normal file
Binary file not shown.
BIN
Telegram/Resources/animations/collectible_username.tgs
Normal file
BIN
Telegram/Resources/animations/collectible_username.tgs
Normal file
Binary file not shown.
|
@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_menu_set_status" = "Set Emoji Status";
|
||||
"lng_menu_change_status" = "Change Emoji Status";
|
||||
"lng_menu_my_stories" = "My Stories";
|
||||
"lng_menu_my_groups" = "My Groups";
|
||||
"lng_menu_my_channels" = "My Channels";
|
||||
|
||||
"lng_disable_notifications_from_tray" = "Disable notifications";
|
||||
"lng_enable_notifications_from_tray" = "Enable notifications";
|
||||
|
@ -469,6 +471,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
"lng_bio_placeholder" = "Bio";
|
||||
|
||||
"lng_collectible_username_title" = "{username} is a collectible username that belongs to";
|
||||
"lng_collectible_username_info" = "This username was bought on **Fragment** on {date} for {price}";
|
||||
"lng_collectible_username_copy" = "Copy Link";
|
||||
"lng_collectible_phone_title" = "{phone} is a collectible phone number that belongs to";
|
||||
"lng_collectible_phone_info" = "This phone number was bought on **Fragment** on {date} for {price}";
|
||||
"lng_collectible_phone_copy" = "Copy Phone Number";
|
||||
"lng_collectible_learn_more" = "Learn More";
|
||||
|
||||
"lng_settings_section_info" = "My info";
|
||||
|
||||
"lng_settings_section_notify" = "Notifications";
|
||||
|
@ -1058,6 +1068,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_settings_faq_button" = "Go to FAQ";
|
||||
"lng_settings_ask_ok" = "Ask a Volunteer";
|
||||
"lng_settings_faq" = "Telegram FAQ";
|
||||
"lng_settings_faq_link" = "https://telegram.org/faq#general-questions";
|
||||
"lng_settings_features" = "Telegram Features";
|
||||
"lng_settings_logout" = "Log Out";
|
||||
"lng_sure_logout" = "Are you sure you want to log out?";
|
||||
|
@ -5040,6 +5051,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"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_learn_coin_link" = "https://telegram.org/blog/monetization-for-channels";
|
||||
"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";
|
||||
|
|
|
@ -22,5 +22,7 @@
|
|||
<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>
|
||||
<file alias="collectible_username.tgs">../../animations/collectible_username.tgs</file>
|
||||
<file alias="collectible_phone.tgs">../../animations/collectible_phone.tgs</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="4.16.0.0" />
|
||||
Version="4.16.6.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
|
|
@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,16,0,0
|
||||
PRODUCTVERSION 4,16,0,0
|
||||
FILEVERSION 4,16,6,0
|
||||
PRODUCTVERSION 4,16,6,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -62,10 +62,10 @@ BEGIN
|
|||
BEGIN
|
||||
VALUE "CompanyName", "Radolyn Labs"
|
||||
VALUE "FileDescription", "AyuGram Desktop"
|
||||
VALUE "FileVersion", "4.16.0.0"
|
||||
VALUE "FileVersion", "4.16.6.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "AyuGram Desktop"
|
||||
VALUE "ProductVersion", "4.16.0.0"
|
||||
VALUE "ProductVersion", "4.16.6.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
|
@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,16,0,0
|
||||
PRODUCTVERSION 4,16,0,0
|
||||
FILEVERSION 4,16,6,0
|
||||
PRODUCTVERSION 4,16,6,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.16.0.0"
|
||||
VALUE "FileVersion", "4.16.6.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "AyuGram Desktop"
|
||||
VALUE "ProductVersion", "4.16.0.0"
|
||||
VALUE "ProductVersion", "4.16.6.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace {
|
|||
|
||||
constexpr auto kSharedMediaLimit = 100;
|
||||
|
||||
[[nodiscard]] SendMediaReady PreparePeerPhoto(
|
||||
[[nodiscard]] std::shared_ptr<FilePrepareResult> PreparePeerPhoto(
|
||||
MTP::DcId dcId,
|
||||
PeerId peerId,
|
||||
QImage &&image) {
|
||||
|
@ -80,24 +80,17 @@ constexpr auto kSharedMediaLimit = 100;
|
|||
MTPVector<MTPVideoSize>(),
|
||||
MTP_int(dcId));
|
||||
|
||||
QString file, filename;
|
||||
int64 filesize = 0;
|
||||
QByteArray data;
|
||||
|
||||
return SendMediaReady(
|
||||
SendMediaType::Photo,
|
||||
file,
|
||||
filename,
|
||||
filesize,
|
||||
data,
|
||||
id,
|
||||
id,
|
||||
u"jpg"_q,
|
||||
peerId,
|
||||
photo,
|
||||
photoThumbs,
|
||||
MTP_documentEmpty(MTP_long(0)),
|
||||
jpeg);
|
||||
auto result = MakePreparedFile({
|
||||
.id = id,
|
||||
.type = SendMediaType::Photo,
|
||||
});
|
||||
result->type = SendMediaType::Photo;
|
||||
result->setFileData(jpeg);
|
||||
result->thumbId = id;
|
||||
result->thumbname = "thumb.jpg";
|
||||
result->photo = photo;
|
||||
result->photoThumbs = photoThumbs;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MTPVideoSize> PrepareMtpMarkup(
|
||||
|
@ -239,7 +232,7 @@ void PeerPhoto::upload(
|
|||
_api.instance().mainDcId(),
|
||||
peer->id,
|
||||
base::take(photo.image));
|
||||
_session->uploader().uploadMedia(fakeId, ready);
|
||||
_session->uploader().upload(fakeId, ready);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,16 +24,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Api {
|
||||
namespace {
|
||||
|
||||
SendMediaReady PrepareRingtoneDocument(
|
||||
std::shared_ptr<FilePrepareResult> PrepareRingtoneDocument(
|
||||
MTP::DcId dcId,
|
||||
const QString &filename,
|
||||
const QString &filemime,
|
||||
const QByteArray &content) {
|
||||
const auto id = base::RandomValue<DocumentId>();
|
||||
auto attributes = QVector<MTPDocumentAttribute>(
|
||||
1,
|
||||
MTP_documentAttributeFilename(MTP_string(filename)));
|
||||
const auto id = base::RandomValue<DocumentId>();
|
||||
const auto document = MTP_document(
|
||||
|
||||
auto result = MakePreparedFile({
|
||||
.id = id,
|
||||
.type = SendMediaType::File,
|
||||
});
|
||||
result->filename = filename;
|
||||
result->content = content;
|
||||
result->filesize = content.size();
|
||||
result->setFileData(content);
|
||||
result->document = MTP_document(
|
||||
MTP_flags(0),
|
||||
MTP_long(id),
|
||||
MTP_long(0),
|
||||
|
@ -45,21 +54,7 @@ SendMediaReady PrepareRingtoneDocument(
|
|||
MTPVector<MTPVideoSize>(),
|
||||
MTP_int(dcId),
|
||||
MTP_vector<MTPDocumentAttribute>(std::move(attributes)));
|
||||
|
||||
return SendMediaReady(
|
||||
SendMediaType::File,
|
||||
QString(), // filepath
|
||||
filename,
|
||||
content.size(),
|
||||
content,
|
||||
id,
|
||||
0,
|
||||
QString(),
|
||||
PeerId(),
|
||||
MTP_photoEmpty(MTP_long(0)),
|
||||
PreparedPhotoThumbs(),
|
||||
document,
|
||||
QByteArray());
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -102,7 +97,7 @@ void Ringtones::upload(
|
|||
_uploads.erase(already);
|
||||
}
|
||||
_uploads.emplace(fakeId, uploadedData);
|
||||
_session->uploader().uploadMedia(fakeId, ready);
|
||||
_session->uploader().upload(fakeId, ready);
|
||||
}
|
||||
|
||||
void Ringtones::ready(const FullMsgId &msgId, const MTPInputFile &file) {
|
||||
|
|
|
@ -353,7 +353,7 @@ void FillMessagePostFlags(
|
|||
|
||||
void SendConfirmedFile(
|
||||
not_null<Main::Session*> session,
|
||||
const std::shared_ptr<FileLoadResult> &file) {
|
||||
const std::shared_ptr<FilePrepareResult> &file) {
|
||||
const auto isEditing = (file->type != SendMediaType::Audio)
|
||||
&& (file->to.replaceMediaOf != 0);
|
||||
const auto newId = FullMsgId(
|
||||
|
|
|
@ -14,7 +14,7 @@ class Session;
|
|||
class History;
|
||||
class PhotoData;
|
||||
class DocumentData;
|
||||
struct FileLoadResult;
|
||||
struct FilePrepareResult;
|
||||
|
||||
namespace Api {
|
||||
|
||||
|
@ -40,6 +40,6 @@ void FillMessagePostFlags(
|
|||
|
||||
void SendConfirmedFile(
|
||||
not_null<Main::Session*> session,
|
||||
const std::shared_ptr<FileLoadResult> &file);
|
||||
const std::shared_ptr<FilePrepareResult> &file);
|
||||
|
||||
} // namespace Api
|
||||
|
|
|
@ -813,7 +813,7 @@ QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) {
|
|||
const auto storyId = story->fullId();
|
||||
const auto peer = story->peer();
|
||||
const auto fallback = [&] {
|
||||
const auto base = peer->userName();
|
||||
const auto base = peer->username();
|
||||
const auto story = QString::number(storyId.story);
|
||||
const auto query = base + "/s/" + story;
|
||||
return session().createInternalLinkFull(query);
|
||||
|
|
|
@ -593,11 +593,11 @@ void BackgroundPreviewBox::uploadForPeer(bool both) {
|
|||
const auto ready = Window::Theme::PrepareWallPaper(
|
||||
session->mainDcId(),
|
||||
_paper.localThumbnail()->original());
|
||||
const auto documentId = ready.id;
|
||||
const auto documentId = ready->id;
|
||||
_uploadId = FullMsgId(
|
||||
session->userPeerId(),
|
||||
session->data().nextLocalMessageId());
|
||||
session->uploader().uploadMedia(_uploadId, ready);
|
||||
session->uploader().upload(_uploadId, ready);
|
||||
if (_uploadLifetime) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1044,3 +1044,33 @@ inviteForbiddenTitle: FlatLabel(boxTitle) {
|
|||
inviteForbiddenTitlePadding: margins(32px, 4px, 32px, 0px);
|
||||
inviteForbiddenLockBg: dialogsUnreadBgMuted;
|
||||
inviteForbiddenLockIcon: icon {{ "emoji/premium_lock", dialogsUnreadFg }};
|
||||
|
||||
collectibleIconDiameter: 72px;
|
||||
collectibleIcon: 64px;
|
||||
collectibleIconPadding: margins(24px, 32px, 24px, 12px);
|
||||
collectibleHeader: FlatLabel(boxTitle) {
|
||||
minWidth: 120px;
|
||||
maxHeight: 0px;
|
||||
align: align(top);
|
||||
}
|
||||
collectibleHeaderPadding: margins(24px, 16px, 24px, 12px);
|
||||
collectibleOwnerPadding: margins(24px, 4px, 24px, 8px);
|
||||
collectibleInfo: inviteForbiddenInfo;
|
||||
collectibleInfoPadding: margins(24px, 12px, 24px, 12px);
|
||||
collectibleInfoTonMargins: margins(0px, 3px, 0px, 0px);
|
||||
collectibleMore: RoundButton(defaultActiveButton) {
|
||||
height: 36px;
|
||||
textTop: 9px;
|
||||
radius: 6px;
|
||||
}
|
||||
collectibleMorePadding: margins(24px, 12px, 24px, 0px);
|
||||
collectibleCopy: RoundButton(defaultLightButton) {
|
||||
height: 36px;
|
||||
textTop: 9px;
|
||||
radius: 6px;
|
||||
}
|
||||
collectibleBox: Box(defaultBox) {
|
||||
buttonPadding: margins(24px, 12px, 24px, 12px);
|
||||
buttonHeight: 36px;
|
||||
button: collectibleCopy;
|
||||
}
|
||||
|
|
|
@ -159,10 +159,7 @@ public:
|
|||
-> rpl::producer<RowSelectionChange>;
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::unique_ptr<PeerListRow> createRow() const;
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
bool _premiums = false;
|
||||
|
||||
rpl::event_stream<> _selectionChanged;
|
||||
rpl::event_stream<RowSelectionChange> _rowSelectionChanges;
|
||||
|
@ -209,8 +206,7 @@ bool PremiumsRow::useForumLikeUserpic() const {
|
|||
TypesController::TypesController(
|
||||
not_null<Main::Session*> session,
|
||||
bool premiums)
|
||||
: _session(session)
|
||||
, _premiums(premiums) {
|
||||
: _session(session) {
|
||||
}
|
||||
|
||||
Main::Session &TypesController::session() const {
|
||||
|
@ -331,6 +327,9 @@ auto PrivacyExceptionsBoxController::preparePremiumsRowList()
|
|||
|
||||
_deselectOption = [=](PeerListRowId itemId) {
|
||||
if (const auto row = _typesDelegate->peerListFindRow(itemId)) {
|
||||
if (itemId == kPremiumsRowId) {
|
||||
_selected.premiums = false;
|
||||
}
|
||||
_typesDelegate->peerListSetRowChecked(row, false);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1039,11 +1039,9 @@ void PeerListContent::changeCheckState(
|
|||
not_null<PeerListRow*> row,
|
||||
bool checked,
|
||||
anim::type animated) {
|
||||
row->setChecked(
|
||||
checked,
|
||||
_st.item.checkbox,
|
||||
animated,
|
||||
[=] { updateRow(row); });
|
||||
row->setChecked(checked, _st.item.checkbox, animated, [=] {
|
||||
updateRow(row);
|
||||
});
|
||||
}
|
||||
|
||||
void PeerListContent::setRowHidden(not_null<PeerListRow*> row, bool hidden) {
|
||||
|
@ -1789,10 +1787,10 @@ crl::time PeerListContent::paintRow(
|
|||
if (row->isSearchResult()
|
||||
&& !_mentionHighlight.isEmpty()
|
||||
&& peer
|
||||
&& peer->userName().startsWith(
|
||||
&& peer->username().startsWith(
|
||||
_mentionHighlight,
|
||||
Qt::CaseInsensitive)) {
|
||||
const auto username = peer->userName();
|
||||
const auto username = peer->username();
|
||||
const auto availableWidth = statusw;
|
||||
auto highlightedPart = '@' + username.mid(0, _mentionHighlight.size());
|
||||
auto grayedPart = username.mid(_mentionHighlight.size());
|
||||
|
|
|
@ -100,7 +100,7 @@ void Controller::prepare() {
|
|||
return;
|
||||
}
|
||||
auto row = std::make_unique<PeerListRow>(chat);
|
||||
const auto username = chat->userName();
|
||||
const auto username = chat->username();
|
||||
row->setCustomStatus(!username.isEmpty()
|
||||
? ('@' + username)
|
||||
: (chat->isChannel() && !chat->isMegagroup())
|
||||
|
|
|
@ -207,7 +207,7 @@ void ProcessFullPhoto(
|
|||
| UpdateFlag::Birthday)
|
||||
) | rpl::map([=] {
|
||||
const auto user = peer->asUser();
|
||||
const auto username = peer->userName();
|
||||
const auto username = peer->username();
|
||||
return PeerShortInfoFields{
|
||||
.name = peer->name(),
|
||||
.phone = user ? Ui::FormatPhone(user->phone()) : QString(),
|
||||
|
|
|
@ -322,7 +322,7 @@ void PublicsController::prepare() {
|
|||
auto &owner = _navigation->session().data();
|
||||
for (const auto &chat : chats) {
|
||||
if (const auto peer = owner.processChat(chat)) {
|
||||
if (!peer->isChannel() || peer->userName().isEmpty()) {
|
||||
if (!peer->isChannel() || peer->username().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
appendRow(peer);
|
||||
|
@ -346,7 +346,7 @@ void PublicsController::rowRightActionClicked(not_null<PeerListRow*> row) {
|
|||
const auto text = textMethod(
|
||||
tr::now,
|
||||
lt_link,
|
||||
peer->session().createInternalLink(peer->userName()),
|
||||
peer->session().createInternalLink(peer->username()),
|
||||
lt_group,
|
||||
peer->name());
|
||||
const auto confirmText = tr::lng_channels_too_much_public_revoke(
|
||||
|
@ -389,7 +389,7 @@ std::unique_ptr<PeerListRow> PublicsController::createRow(
|
|||
auto result = std::make_unique<PeerListRowWithLink>(peer);
|
||||
result->setActionLink(tr::lng_channels_too_much_public_revoke(tr::now));
|
||||
result->setCustomStatus(
|
||||
_navigation->session().createInternalLink(peer->userName()));
|
||||
_navigation->session().createInternalLink(peer->username()));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -652,8 +652,8 @@ void StickersBox::prepare() {
|
|||
}
|
||||
if (const auto featured = _featured.widget()) {
|
||||
featured->setInstallSetCallback([=](uint64 setId) {
|
||||
markAsInstalledCallback(setId);
|
||||
installCallback(setId);
|
||||
markAsInstalledCallback(setId);
|
||||
});
|
||||
featured->setRemoveSetCallback(markAsRemovedCallback);
|
||||
}
|
||||
|
@ -1760,8 +1760,11 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
|
|||
const auto &st = installedSet
|
||||
? st::stickersTrendingInstalled
|
||||
: st::stickersTrendingAdd;
|
||||
const auto buttonTextWidth = installedSet
|
||||
? _installedWidth
|
||||
: _addWidth;
|
||||
auto rippleMask = Ui::RippleAnimation::RoundRectMask(
|
||||
QSize(_addWidth - st.width, st.height),
|
||||
QSize(buttonTextWidth - st.width, st.height),
|
||||
st::roundRadiusLarge);
|
||||
ensureRipple(
|
||||
st.ripple,
|
||||
|
@ -1902,9 +1905,19 @@ void StickersBox::Inner::updateSelected() {
|
|||
selected = selectedIndex;
|
||||
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
|
||||
const auto row = _rows[selectedIndex].get();
|
||||
if (!_megagroupSet && (_isInstalledTab || (_section == Section::Featured) || !row->isInstalled() || row->isArchived() || row->removed)) {
|
||||
if (!_megagroupSet
|
||||
&& (_isInstalledTab
|
||||
|| (_section == Section::Featured)
|
||||
|| !row->isInstalled()
|
||||
|| row->isArchived()
|
||||
|| row->removed)) {
|
||||
auto removeButton = (_isInstalledTab && !row->removed);
|
||||
auto rect = myrtlrect(relativeButtonRect(removeButton, false));
|
||||
|
||||
const auto installedSetButton = !_isInstalledTab
|
||||
&& row->isInstalled()
|
||||
&& !row->isArchived()
|
||||
&& !row->removed;
|
||||
auto rect = myrtlrect(relativeButtonRect(removeButton, installedSetButton));
|
||||
actionSel = rect.contains(local) ? selectedIndex : -1;
|
||||
} else {
|
||||
actionSel = -1;
|
||||
|
@ -1957,12 +1970,19 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
|||
|
||||
_mouse = e->globalPos();
|
||||
updateSelected();
|
||||
if (_actionDown == _actionSel && _actionSel >= 0) {
|
||||
const auto callback = _rows[_actionDown]->removed
|
||||
? _installSetCallback
|
||||
: _removeSetCallback;
|
||||
const auto down = _actionDown;
|
||||
setActionDown(-1);
|
||||
if (down == _actionSel && _actionSel >= 0) {
|
||||
const auto row = _rows[down].get();
|
||||
const auto installedSet = row->isInstalled()
|
||||
&& !row->isArchived()
|
||||
&& !row->removed;
|
||||
const auto callback = installedSet
|
||||
? _removeSetCallback
|
||||
: _installSetCallback;
|
||||
if (callback) {
|
||||
callback(_rows[_actionDown]->set->id);
|
||||
row->ripple.reset();
|
||||
callback(row->set->id);
|
||||
}
|
||||
} else if (_dragging >= 0) {
|
||||
_rows[_dragging]->yadd.start(0.);
|
||||
|
@ -1973,7 +1993,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
|||
}
|
||||
|
||||
_dragging = _started = -1;
|
||||
} else if (pressed == _selected && _actionSel < 0 && _actionDown < 0) {
|
||||
} else if (pressed == _selected && _actionSel < 0 && down < 0) {
|
||||
const auto selectedIndex = [&] {
|
||||
if (auto index = std::get_if<int>(&_selected)) {
|
||||
return *index;
|
||||
|
@ -1997,7 +2017,6 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
|||
showSetByRow(*_megagroupSelectedSet);
|
||||
}
|
||||
}
|
||||
setActionDown(-1);
|
||||
}
|
||||
|
||||
void StickersBox::Inner::saveGroupSet(Fn<void()> done) {
|
||||
|
|
|
@ -612,9 +612,9 @@ void MembersRow::paintComplexStatusText(
|
|||
x += skip;
|
||||
availableWidth -= skip;
|
||||
const auto &font = st::normalFont;
|
||||
const auto useAbout = (style == MembersRowStyle::Video)
|
||||
? false
|
||||
: ((_state == State::RaisedHand && !_raisedHandStatus)
|
||||
const auto useAbout = !_about.isEmpty()
|
||||
&& (style != MembersRowStyle::Video)
|
||||
&& ((_state == State::RaisedHand && !_raisedHandStatus)
|
||||
|| (_state != State::RaisedHand && !_speaking));
|
||||
if (!useAbout
|
||||
&& _state != State::Invited
|
||||
|
|
|
@ -587,11 +587,9 @@ void ChooseSourceProcess::setupGeometryWithParent(
|
|||
not_null<QWidget*> parent) {
|
||||
_window->createWinId();
|
||||
const auto parentScreen = [&] {
|
||||
if (!::Platform::IsWayland()) {
|
||||
if (const auto screen = QGuiApplication::screenAt(
|
||||
if (const auto screen = QGuiApplication::screenAt(
|
||||
parent->geometry().center())) {
|
||||
return screen;
|
||||
}
|
||||
return screen;
|
||||
}
|
||||
return parent->screen();
|
||||
}();
|
||||
|
|
|
@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_changes.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
|
@ -51,10 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
// AyuGram includes
|
||||
#include "data/data_file_origin.h"
|
||||
#include "qapplication.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
namespace ChatHelpers {
|
||||
namespace {
|
||||
|
@ -62,6 +60,7 @@ namespace {
|
|||
constexpr auto kCollapsedRows = 3;
|
||||
constexpr auto kAppearDuration = 0.3;
|
||||
constexpr auto kCustomSearchLimit = 256;
|
||||
constexpr auto kColorPickerDelay = crl::time(500);
|
||||
|
||||
using Core::RecentEmojiId;
|
||||
using Core::RecentEmojiDocument;
|
||||
|
@ -630,6 +629,15 @@ void EmojiListWidget::applyNextSearchQuery() {
|
|||
}
|
||||
}
|
||||
|
||||
void EmojiListWidget::showPreview() {
|
||||
if (const auto over = std::get_if<OverEmoji>(&_pressed)) {
|
||||
if (const auto custom = lookupCustomEmoji(over)) {
|
||||
_show->showMediaPreview(custom->stickerSetOrigin(), custom);
|
||||
_previewShown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<EmojiPtr> EmojiListWidget::collectPlainSearchResults() {
|
||||
return SearchEmoji(_searchQuery, _searchEmoji);
|
||||
}
|
||||
|
@ -1125,7 +1133,7 @@ void EmojiListWidget::fillRecentMenu(
|
|||
const auto addAction = Ui::Menu::CreateAddActionCallback(menu);
|
||||
const auto over = OverEmoji{ section, index };
|
||||
const auto emoji = lookupOverEmoji(&over);
|
||||
const auto custom = lookupCustomEmoji(index, section);
|
||||
const auto custom = lookupCustomEmoji(&over);
|
||||
if (custom && custom->sticker()) {
|
||||
const auto sticker = custom->sticker();
|
||||
const auto emoji = sticker->alt;
|
||||
|
@ -1498,6 +1506,11 @@ bool EmojiListWidget::checkPickerHide() {
|
|||
return false;
|
||||
}
|
||||
|
||||
DocumentData *EmojiListWidget::lookupCustomEmoji(
|
||||
const OverEmoji *over) const {
|
||||
return over ? lookupCustomEmoji(over->index, over->section) : nullptr;
|
||||
}
|
||||
|
||||
DocumentData *EmojiListWidget::lookupCustomEmoji(
|
||||
int index,
|
||||
int section) const {
|
||||
|
@ -1600,12 +1613,14 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) {
|
|||
if (!Core::App().settings().hasChosenEmojiVariant(emoji)) {
|
||||
showPicker();
|
||||
} else {
|
||||
_showPickerTimer.callOnce(500);
|
||||
_previewTimer.cancel();
|
||||
_showPickerTimer.callOnce(kColorPickerDelay);
|
||||
}
|
||||
} else if (lookupCustomEmoji(over)) {
|
||||
_showPickerTimer.cancel();
|
||||
_previewTimer.callOnce(QApplication::startDragTime());
|
||||
}
|
||||
}
|
||||
|
||||
_previewTimer.callOnce(QApplication::startDragTime());
|
||||
}
|
||||
|
||||
void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
|
@ -1613,11 +1628,6 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
|||
|
||||
auto pressed = _pressed;
|
||||
setPressed(v::null);
|
||||
if (_previewShown) {
|
||||
_previewShown = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_lastMousePos = e->globalPos();
|
||||
if (!_picker->isHidden()) {
|
||||
if (_picker->rect().contains(_picker->mapFromGlobal(_lastMousePos))) {
|
||||
|
@ -1640,7 +1650,10 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
|||
_picker->hide();
|
||||
}
|
||||
|
||||
if (v::is_null(_selected) || _selected != pressed) {
|
||||
if (_previewShown) {
|
||||
_previewShown = false;
|
||||
return;
|
||||
} else if (v::is_null(_selected) || _selected != pressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1659,7 +1672,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
|||
return;
|
||||
}
|
||||
selectEmoji(lookupChosen(emoji, over));
|
||||
} else if (const auto custom = lookupCustomEmoji(index, section)) {
|
||||
} else if (const auto custom = lookupCustomEmoji(over)) {
|
||||
selectCustom(lookupChosen(custom, over));
|
||||
}
|
||||
} else if (const auto set = std::get_if<OverSet>(&pressed)) {
|
||||
|
@ -2492,8 +2505,10 @@ bool EmojiListWidget::eventHook(QEvent *e) {
|
|||
}
|
||||
|
||||
void EmojiListWidget::updateSelected() {
|
||||
if ((!v::is_null(_pressed) || !v::is_null(_pickerSelected)) && !_previewShown) {
|
||||
return;
|
||||
if (!v::is_null(_pressed) || !v::is_null(_pickerSelected)) {
|
||||
if (!_previewShown) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto newSelected = OverState{ v::null };
|
||||
|
@ -2542,7 +2557,7 @@ void EmojiListWidget::setSelected(OverState newSelected) {
|
|||
|
||||
const auto hasSelection = !v::is_null(_selected);
|
||||
if (hasSelection && Core::App().settings().suggestEmoji()) {
|
||||
Ui::Tooltip::Show(350, this);
|
||||
Ui::Tooltip::Show(1000, this);
|
||||
}
|
||||
|
||||
setCursor(hasSelection ? style::cur_pointer : style::cur_default);
|
||||
|
@ -2552,34 +2567,16 @@ void EmojiListWidget::setSelected(OverState newSelected) {
|
|||
} else {
|
||||
_picker->showAnimated();
|
||||
}
|
||||
}
|
||||
|
||||
if (_previewShown && _pressed != _selected) {
|
||||
} else if (_previewShown && _pressed != _selected) {
|
||||
if (const auto over = std::get_if<OverEmoji>(&_selected)) {
|
||||
_pressed = _selected;
|
||||
|
||||
const auto section = over ? over->section : -1;
|
||||
const auto index = over ? over->index : -1;
|
||||
|
||||
if (const auto document = lookupCustomEmoji(index, section)) {
|
||||
_show->showMediaPreview(document->stickerSetOrigin(), document);
|
||||
if (const auto custom = lookupCustomEmoji(over)) {
|
||||
_pressed = _selected;
|
||||
_show->showMediaPreview(custom->stickerSetOrigin(), custom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiListWidget::showPreview() {
|
||||
if (const auto over = std::get_if<OverEmoji>(&_selected)) {
|
||||
const auto section = over ? over->section : -1;
|
||||
const auto index = over ? over->index : -1;
|
||||
|
||||
if (const auto document = lookupCustomEmoji(index, section)) {
|
||||
_show->showMediaPreview(document->stickerSetOrigin(), document);
|
||||
_previewShown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiListWidget::setPressed(OverState newPressed) {
|
||||
if (auto button = std::get_if<OverButton>(&_pressed)) {
|
||||
Assert(hasColorButton(button->section)
|
||||
|
|
|
@ -287,6 +287,8 @@ private:
|
|||
int index);
|
||||
|
||||
[[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const;
|
||||
[[nodiscard]] DocumentData *lookupCustomEmoji(
|
||||
const OverEmoji *over) const;
|
||||
[[nodiscard]] DocumentData *lookupCustomEmoji(
|
||||
int index,
|
||||
int section) const;
|
||||
|
@ -371,6 +373,8 @@ private:
|
|||
DocumentId documentId,
|
||||
uint64 setId);
|
||||
|
||||
void showPreview();
|
||||
|
||||
void applyNextSearchQuery();
|
||||
|
||||
void showPreview();
|
||||
|
@ -442,6 +446,8 @@ private:
|
|||
|
||||
object_ptr<EmojiColorPicker> _picker;
|
||||
base::Timer _showPickerTimer;
|
||||
base::Timer _previewTimer;
|
||||
bool _previewShown = false;
|
||||
|
||||
rpl::event_stream<EmojiChosen> _chosen;
|
||||
rpl::event_stream<FileChosen> _customChosen;
|
||||
|
|
|
@ -46,6 +46,7 @@ struct ClickHandlerContext {
|
|||
bool mayShowConfirmation = false;
|
||||
bool skipBotAutoLogin = false;
|
||||
bool botStartAutoSubmit = false;
|
||||
bool ignoreIv = false;
|
||||
// Is filled from peer info.
|
||||
PeerData *peer = nullptr;
|
||||
};
|
||||
|
|
|
@ -159,7 +159,7 @@ void Launch(const QString &filepath) {
|
|||
void ShowInFolder(const QString &filepath) {
|
||||
crl::on_main([=] {
|
||||
Ui::PreventDelayedActivation();
|
||||
if (Platform::IsLinux()) {
|
||||
if (Platform::IsX11()) {
|
||||
// Hide mediaview to make other apps visible.
|
||||
Core::App().hideMediaView();
|
||||
}
|
||||
|
|
|
@ -567,6 +567,7 @@ bool ResolveUsernameOrPhone(
|
|||
.phone = phone,
|
||||
.messageId = post,
|
||||
.storyId = storyId,
|
||||
.text = params.value(u"text"_q),
|
||||
.repliesInfo = commentId
|
||||
? Window::RepliesByLinkInfo{
|
||||
Window::CommentId{ commentId }
|
||||
|
@ -901,6 +902,34 @@ bool ShowEditPersonalChannel(
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ShowCollectiblePhone(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context) {
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
const auto phone = match->captured(1);
|
||||
const auto peerId = PeerId(match->captured(2).toULongLong());
|
||||
controller->resolveCollectible(
|
||||
peerId,
|
||||
phone.startsWith('+') ? phone : '+' + phone);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShowCollectibleUsername(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context) {
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
const auto username = match->captured(1);
|
||||
const auto peerId = PeerId(match->captured(2).toULongLong());
|
||||
controller->resolveCollectible(peerId, username);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExportTestChatTheme(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<const Data::CloudTheme*> theme) {
|
||||
|
@ -1311,6 +1340,14 @@ const std::vector<LocalUrlHandler> &InternalUrlHandlers() {
|
|||
u"^edit_personal_channel$"_q,
|
||||
ShowEditPersonalChannel,
|
||||
},
|
||||
{
|
||||
u"^collectible_phone/([\\+0-9\\-\\s]+)@([0-9]+)$"_q,
|
||||
ShowCollectiblePhone,
|
||||
},
|
||||
{
|
||||
u"^collectible_username/([a-zA-Z0-9\\-\\_\\.]+)@([0-9]+)$"_q,
|
||||
ShowCollectibleUsername,
|
||||
},
|
||||
};
|
||||
return Result;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <QtGui/QSessionManager>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtGui/qpa/qplatformscreen.h>
|
||||
#include <ksandbox.h>
|
||||
|
||||
namespace Core {
|
||||
namespace {
|
||||
|
@ -518,10 +517,8 @@ void Sandbox::refreshGlobalProxy() {
|
|||
|| proxy.type == MTP::ProxyData::Type::Http) {
|
||||
QNetworkProxy::setApplicationProxy(
|
||||
MTP::ToNetworkProxy(MTP::ToDirectIpProxy(proxy)));
|
||||
} else if ((!Core::IsAppLaunched()
|
||||
|| Core::App().settings().proxy().isSystem())
|
||||
// this works stable only in sandboxed environment where it works through portal
|
||||
&& (!Platform::IsLinux() || KSandbox::isInside() || cDebugMode())) {
|
||||
} else if (!Core::IsAppLaunched()
|
||||
|| Core::App().settings().proxy().isSystem()) {
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
} else {
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);
|
||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_sponsored_messages.h"
|
||||
#include "iv/iv_instance.h"
|
||||
#include "ui/text/text_custom_emoji.h"
|
||||
#include "ui/basic_click_handlers.h"
|
||||
#include "ui/emoji_config.h"
|
||||
|
@ -241,6 +242,13 @@ bool UiIntegration::handleUrlClick(
|
|||
} else if (local.startsWith(u"internal:"_q, Qt::CaseInsensitive)) {
|
||||
Core::App().openInternalUrl(local, context);
|
||||
return true;
|
||||
} else if (Iv::PreferForUri(url)
|
||||
&& !context.value<ClickHandlerContext>().ignoreIv) {
|
||||
const auto my = context.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
Core::App().iv().openWithIvPreferred(controller, url, context);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (AyuUrlHandlers::TryHandleSpotify(url)) {
|
||||
|
|
|
@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
|
|||
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
||||
constexpr auto AppName = "AyuGram Desktop"_cs;
|
||||
constexpr auto AppFile = "AyuGram"_cs;
|
||||
constexpr auto AppVersion = 4016000;
|
||||
constexpr auto AppVersionStr = "4.16";
|
||||
constexpr auto AppVersion = 4016006;
|
||||
constexpr auto AppVersionStr = "4.16.6";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
|
|
@ -125,8 +125,10 @@ void Chatbots::togglePaused(not_null<PeerData*> peer, bool paused) {
|
|||
return;
|
||||
} else if (const auto settings = peer->barSettings()) {
|
||||
peer->setBarSettings(paused
|
||||
? (*settings | PeerBarSetting::BusinessBotPaused)
|
||||
: (*settings & ~PeerBarSetting::BusinessBotPaused));
|
||||
? ((*settings | PeerBarSetting::BusinessBotPaused)
|
||||
& ~PeerBarSetting::BusinessBotCanReply)
|
||||
: ((*settings & ~PeerBarSetting::BusinessBotPaused)
|
||||
| PeerBarSetting::BusinessBotCanReply));
|
||||
} else {
|
||||
api->requestPeerSettings(peer);
|
||||
}
|
||||
|
|
|
@ -71,6 +71,13 @@ auto RecipientsFlags(const BusinessRecipients &data) {
|
|||
|
||||
} // namespace
|
||||
|
||||
BusinessRecipients BusinessRecipients::MakeValid(BusinessRecipients value) {
|
||||
if (value.included.empty()) {
|
||||
value.allButExcluded = true;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
MTPInputBusinessRecipients ForMessagesToMTP(const BusinessRecipients &data) {
|
||||
using Flag = MTPDinputBusinessRecipients::Flag;
|
||||
const auto &chats = data.allButExcluded ? data.excluded : data.included;
|
||||
|
|
|
@ -44,6 +44,9 @@ struct BusinessRecipients {
|
|||
BusinessChats excluded;
|
||||
bool allButExcluded = false;
|
||||
|
||||
[[nodiscard]] static BusinessRecipients MakeValid(
|
||||
BusinessRecipients value);
|
||||
|
||||
friend inline bool operator==(
|
||||
const BusinessRecipients &a,
|
||||
const BusinessRecipients &b) = default;
|
||||
|
|
|
@ -127,4 +127,3 @@ rpl::producer<bool> IsBirthdayTodayValue(Birthday date) {
|
|||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
|
|
|
@ -139,6 +139,10 @@ const std::vector<QString> &ChannelData::usernames() const {
|
|||
return _username.usernames();
|
||||
}
|
||||
|
||||
bool ChannelData::isUsernameEditable(QString username) const {
|
||||
return _username.isEditable(username);
|
||||
}
|
||||
|
||||
void ChannelData::setAccessHash(uint64 accessHash) {
|
||||
access = accessHash;
|
||||
input = MTP_inputPeerChannel(
|
||||
|
|
|
@ -182,6 +182,7 @@ public:
|
|||
[[nodiscard]] QString username() const;
|
||||
[[nodiscard]] QString editableUsername() const;
|
||||
[[nodiscard]] const std::vector<QString> &usernames() const;
|
||||
[[nodiscard]] bool isUsernameEditable(QString username) const;
|
||||
|
||||
[[nodiscard]] int membersCount() const {
|
||||
return std::max(_membersCount, 1);
|
||||
|
|
|
@ -1123,13 +1123,14 @@ rpl::producer<Ui::DownloadBarContent> MakeDownloadBarContent() {
|
|||
state->thumbnail = Images::Prepare(embed->original(), 0, {
|
||||
.options = Images::Option::Blur,
|
||||
});
|
||||
} else if (!state->downloadTaskLifetime) {
|
||||
state->document->session().downloaderTaskFinished(
|
||||
) | rpl::filter([=] {
|
||||
return self(self);
|
||||
}) | rpl::start_with_next(
|
||||
state->push,
|
||||
state->downloadTaskLifetime);
|
||||
}
|
||||
state->document->session().downloaderTaskFinished(
|
||||
) | rpl::filter([=] {
|
||||
return self(self);
|
||||
}) | rpl::start_with_next(
|
||||
state->push,
|
||||
state->downloadTaskLifetime);
|
||||
return !state->thumbnail.isNull();
|
||||
};
|
||||
const auto resolveThumbnail = [=] {
|
||||
|
|
|
@ -940,7 +940,7 @@ const QString &PeerData::shortName() const {
|
|||
return _name;
|
||||
}
|
||||
|
||||
QString PeerData::userName() const {
|
||||
QString PeerData::username() const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->username();
|
||||
} else if (const auto channel = asChannel()) {
|
||||
|
@ -949,6 +949,34 @@ QString PeerData::userName() const {
|
|||
return QString();
|
||||
}
|
||||
|
||||
QString PeerData::editableUsername() const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->editableUsername();
|
||||
} else if (const auto channel = asChannel()) {
|
||||
return channel->editableUsername();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
const std::vector<QString> &PeerData::usernames() const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->usernames();
|
||||
} else if (const auto channel = asChannel()) {
|
||||
return channel->usernames();
|
||||
}
|
||||
static const auto kEmpty = std::vector<QString>();
|
||||
return kEmpty;
|
||||
}
|
||||
|
||||
bool PeerData::isUsernameEditable(QString username) const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->isUsernameEditable(username);
|
||||
} else if (const auto channel = asChannel()) {
|
||||
return channel->isUsernameEditable(username);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PeerData::changeColorIndex(uint8 index) {
|
||||
index %= Ui::kColorIndexCount;
|
||||
if (_colorIndexCloud && _colorIndex == index) {
|
||||
|
|
|
@ -279,7 +279,11 @@ public:
|
|||
[[nodiscard]] const QString &name() const;
|
||||
[[nodiscard]] const QString &shortName() const;
|
||||
[[nodiscard]] const QString &topBarNameText() const;
|
||||
[[nodiscard]] QString userName() const;
|
||||
|
||||
[[nodiscard]] QString username() const;
|
||||
[[nodiscard]] QString editableUsername() const;
|
||||
[[nodiscard]] const std::vector<QString> &usernames() const;
|
||||
[[nodiscard]] bool isUsernameEditable(QString username) const;
|
||||
|
||||
[[nodiscard]] const base::flat_set<QString> &nameWords() const {
|
||||
return _nameWords;
|
||||
|
|
|
@ -1240,7 +1240,7 @@ PeerData *Session::peerByUsername(const QString &username) const {
|
|||
const auto uname = username.trimmed();
|
||||
for (const auto &[peerId, peer] : _peers) {
|
||||
if (peer->isLoaded()
|
||||
&& !peer->userName().compare(uname, Qt::CaseInsensitive)) {
|
||||
&& !peer->username().compare(uname, Qt::CaseInsensitive)) {
|
||||
return peer.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ void SponsoredMessages::append(
|
|||
? _session->data().processBotApp(peerId, *data.vapp())
|
||||
: nullptr;
|
||||
result.botLinkInfo = Window::PeerByLinkInfo{
|
||||
.usernameOrId = user->userName(),
|
||||
.usernameOrId = user->username(),
|
||||
.resolveType = botAppData
|
||||
? Window::ResolveType::BotApp
|
||||
: data.vstart_param()
|
||||
|
|
|
@ -450,7 +450,7 @@ bool Story::hasDirectLink() const {
|
|||
if (!_privacyPublic || (!_pinned && expired())) {
|
||||
return false;
|
||||
}
|
||||
return !_peer->userName().isEmpty();
|
||||
return !_peer->username().isEmpty();
|
||||
}
|
||||
|
||||
std::optional<QString> Story::errorTextForForward(
|
||||
|
|
|
@ -486,6 +486,10 @@ const std::vector<QString> &UserData::usernames() const {
|
|||
return _username.usernames();
|
||||
}
|
||||
|
||||
bool UserData::isUsernameEditable(QString username) const {
|
||||
return _username.isEditable(username);
|
||||
}
|
||||
|
||||
const QString &UserData::phone() const {
|
||||
return _phone;
|
||||
}
|
||||
|
|
|
@ -150,15 +150,11 @@ public:
|
|||
// a full check by canShareThisContact() call.
|
||||
[[nodiscard]] bool canShareThisContactFast() const;
|
||||
|
||||
MTPInputUser inputUser = MTP_inputUserEmpty();
|
||||
|
||||
QString firstName;
|
||||
QString lastName;
|
||||
[[nodiscard]] const QString &phone() const;
|
||||
[[nodiscard]] QString username() const;
|
||||
[[nodiscard]] QString editableUsername() const;
|
||||
[[nodiscard]] const std::vector<QString> &usernames() const;
|
||||
QString nameOrPhone;
|
||||
[[nodiscard]] bool isUsernameEditable(QString username) const;
|
||||
|
||||
enum class ContactStatus : char {
|
||||
Unknown,
|
||||
|
@ -186,8 +182,6 @@ public:
|
|||
void setBirthday(Data::Birthday value);
|
||||
void setBirthday(const tl::conditional<MTPBirthday> &value);
|
||||
|
||||
std::unique_ptr<BotInfo> botInfo;
|
||||
|
||||
void setUnavailableReasons(
|
||||
std::vector<Data::UnavailableReason> &&reasons);
|
||||
|
||||
|
@ -209,6 +203,14 @@ public:
|
|||
[[nodiscard]] MsgId personalChannelMessageId() const;
|
||||
void setPersonalChannel(ChannelId channelId, MsgId messageId);
|
||||
|
||||
MTPInputUser inputUser = MTP_inputUserEmpty();
|
||||
|
||||
QString firstName;
|
||||
QString lastName;
|
||||
QString nameOrPhone;
|
||||
|
||||
std::unique_ptr<BotInfo> botInfo;
|
||||
|
||||
private:
|
||||
auto unavailableReasons() const
|
||||
-> const std::vector<Data::UnavailableReason> & override;
|
||||
|
|
|
@ -80,4 +80,10 @@ const std::vector<QString> &UsernamesInfo::usernames() const {
|
|||
return _usernames;
|
||||
}
|
||||
|
||||
bool UsernamesInfo::isEditable(const QString &username) const {
|
||||
return (_indexEditableUsername >= 0)
|
||||
&& (_indexEditableUsername < _usernames.size())
|
||||
&& (_usernames[_indexEditableUsername] == username);
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
[[nodiscard]] QString username() const;
|
||||
[[nodiscard]] QString editableUsername() const;
|
||||
[[nodiscard]] const std::vector<QString> &usernames() const;
|
||||
[[nodiscard]] bool isEditable(const QString &username) const;
|
||||
|
||||
private:
|
||||
std::vector<QString> _usernames;
|
||||
|
|
|
@ -1087,7 +1087,7 @@ void InnerWidget::paintPeerSearchResult(
|
|||
|
||||
QRect tr(context.st->textLeft, context.st->textTop, namewidth, st::dialogsTextFont->height);
|
||||
p.setFont(st::dialogsTextFont);
|
||||
QString username = peer->userName();
|
||||
QString username = peer->username();
|
||||
if (!context.active && username.startsWith(_peerSearchQuery, Qt::CaseInsensitive)) {
|
||||
auto first = '@' + username.mid(0, _peerSearchQuery.size());
|
||||
auto second = username.mid(_peerSearchQuery.size());
|
||||
|
@ -4021,7 +4021,7 @@ void InnerWidget::setupShortcuts() {
|
|||
const auto history = thread->owningHistory();
|
||||
const auto isArchived = history->folder()
|
||||
&& (history->folder()->id() == Data::Folder::kId);
|
||||
|
||||
|
||||
Window::ToggleHistoryArchived(
|
||||
_controller->uiShow(),
|
||||
history,
|
||||
|
|
|
@ -496,7 +496,7 @@ auto GenerateParticipantString(
|
|||
data,
|
||||
});
|
||||
}
|
||||
const auto username = peer->userName();
|
||||
const auto username = peer->username();
|
||||
if (username.isEmpty()) {
|
||||
return name;
|
||||
}
|
||||
|
|
|
@ -4536,7 +4536,7 @@ void HistoryWidget::chooseAttach(
|
|||
}
|
||||
|
||||
const auto filter = (overrideSendImagesAsPhotos == true)
|
||||
? FileDialog::ImagesOrAllFilter()
|
||||
? FileDialog::PhotoVideoFilesFilter()
|
||||
: FileDialog::AllOrImagesFilter();
|
||||
|
||||
FileDialog::GetOpenPaths(this, tr::lng_choose_files(tr::now), filter, crl::guard(this, [=](
|
||||
|
@ -6284,8 +6284,7 @@ void HistoryWidget::startItemRevealAnimations() {
|
|||
void HistoryWidget::startMessageSendingAnimation(
|
||||
not_null<HistoryItem*> item) {
|
||||
auto &sendingAnimation = controller()->sendingAnimation();
|
||||
if (!sendingAnimation.hasLocalMessage(item->fullId().msg)
|
||||
|| !sendingAnimation.checkExpectedType(item)) {
|
||||
if (!sendingAnimation.checkExpectedType(item)) {
|
||||
return;
|
||||
}
|
||||
Assert(item->mainView() != nullptr);
|
||||
|
@ -6297,14 +6296,16 @@ void HistoryWidget::startMessageSendingAnimation(
|
|||
geometryValue() | rpl::to_empty,
|
||||
_scroll->geometryValue() | rpl::to_empty,
|
||||
_list->geometryValue() | rpl::to_empty
|
||||
) | rpl::map([=] {
|
||||
) | rpl::map([=]() -> std::optional<QPoint> {
|
||||
const auto view = item->mainView();
|
||||
const auto top = view ? _list->itemTop(view) : -1;
|
||||
if (top < 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto additional = (_list->height() == _scroll->height())
|
||||
? view->height()
|
||||
: 0;
|
||||
return _list->mapToGlobal(QPoint(
|
||||
0,
|
||||
_list->itemTop(view) - additional));
|
||||
return _list->mapToGlobal(QPoint(0, top - additional));
|
||||
});
|
||||
|
||||
sendingAnimation.startAnimation({
|
||||
|
|
|
@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
struct FileLoadResult;
|
||||
enum class SendMediaType;
|
||||
class MessageLinksParser;
|
||||
struct InlineBotQuery;
|
||||
|
|
|
@ -1973,6 +1973,7 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
|||
const auto guard = gsl::finally([&] {
|
||||
updateSendButtonType();
|
||||
updateReplaceMediaButton();
|
||||
updateFieldPlaceholder();
|
||||
updateControlsVisibility();
|
||||
updateControlsGeometry(_wrap->size());
|
||||
});
|
||||
|
|
|
@ -902,15 +902,15 @@ void BusinessBotStatus::Bar::showState(State state) {
|
|||
_userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
_userpic->show();
|
||||
_name->setText(state.bot->name());
|
||||
_status->setText(!state.canReply
|
||||
? tr::lng_chatbot_status_views(tr::now)
|
||||
: state.paused
|
||||
_status->setText(state.paused
|
||||
? tr::lng_chatbot_status_paused(tr::now)
|
||||
: tr::lng_chatbot_status_can_reply(tr::now));
|
||||
: state.canReply
|
||||
? tr::lng_chatbot_status_can_reply(tr::now)
|
||||
: tr::lng_chatbot_status_views(tr::now));
|
||||
_togglePaused->setText(state.paused
|
||||
? tr::lng_chatbot_button_resume()
|
||||
: tr::lng_chatbot_button_pause());
|
||||
_togglePaused->setVisible(state.canReply);
|
||||
_togglePaused->setVisible(state.canReply || state.paused);
|
||||
_paused = state.paused;
|
||||
resizeToWidth(width());
|
||||
}
|
||||
|
|
|
@ -1839,16 +1839,18 @@ void ListWidget::startItemRevealAnimations() {
|
|||
void ListWidget::startMessageSendingAnimation(
|
||||
not_null<HistoryItem*> item) {
|
||||
auto &sendingAnimation = controller()->sendingAnimation();
|
||||
if (!sendingAnimation.hasLocalMessage(item->fullId().msg)
|
||||
|| !sendingAnimation.checkExpectedType(item)) {
|
||||
if (!sendingAnimation.checkExpectedType(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto globalEndTopLeft = rpl::merge(
|
||||
session().data().newItemAdded() | rpl::to_empty,
|
||||
geometryValue() | rpl::to_empty
|
||||
) | rpl::map([=] {
|
||||
) | rpl::map([=]() -> std::optional<QPoint> {
|
||||
const auto view = viewForItem(item);
|
||||
if (!view) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto additional = !_visibleTop ? view->height() : 0;
|
||||
return mapToGlobal(QPoint(0, itemTop(view) - additional));
|
||||
});
|
||||
|
|
|
@ -875,7 +875,7 @@ void RepliesWidget::chooseAttach(
|
|||
}
|
||||
|
||||
const auto filter = (overrideSendImagesAsPhotos == true)
|
||||
? FileDialog::ImagesOrAllFilter()
|
||||
? FileDialog::PhotoVideoFilesFilter()
|
||||
: FileDialog::AllOrImagesFilter();
|
||||
FileDialog::GetOpenPaths(this, tr::lng_choose_files(tr::now), filter, crl::guard(this, [=](
|
||||
FileDialog::OpenResult &&result) {
|
||||
|
|
|
@ -62,11 +62,19 @@ namespace HistoryView {
|
|||
ScheduledMemento::ScheduledMemento(not_null<History*> history)
|
||||
: _history(history)
|
||||
, _forumTopic(nullptr) {
|
||||
const auto list = _history->owner().scheduledMessages().list(_history);
|
||||
if (!list.ids.empty()) {
|
||||
_list.setScrollTopState({ .item = { .fullId = list.ids.front() } });
|
||||
}
|
||||
}
|
||||
|
||||
ScheduledMemento::ScheduledMemento(not_null<Data::ForumTopic*> forumTopic)
|
||||
: _history(forumTopic->owningHistory())
|
||||
, _forumTopic(forumTopic) {
|
||||
const auto list = _history->owner().scheduledMessages().list(_forumTopic);
|
||||
if (!list.ids.empty()) {
|
||||
_list.setScrollTopState({ .item = { .fullId = list.ids.front() } });
|
||||
}
|
||||
}
|
||||
|
||||
object_ptr<Window::SectionWidget> ScheduledMemento::createWidget(
|
||||
|
@ -1162,9 +1170,7 @@ Context ScheduledWidget::listContext() {
|
|||
}
|
||||
|
||||
bool ScheduledWidget::listScrollTo(int top, bool syntetic) {
|
||||
top = (top == ScrollMax && syntetic)
|
||||
? 0
|
||||
: std::clamp(top, 0, _scroll->scrollTopMax());
|
||||
top = std::clamp(top, 0, _scroll->scrollTopMax());
|
||||
if (_scroll->scrollTop() == top) {
|
||||
updateInnerVisibleArea();
|
||||
return false;
|
||||
|
|
|
@ -280,7 +280,7 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class ScheduledMemento : public Window::SectionMemento {
|
||||
class ScheduledMemento final : public Window::SectionMemento {
|
||||
public:
|
||||
ScheduledMemento(not_null<History*> history);
|
||||
ScheduledMemento(not_null<Data::ForumTopic*> forumTopic);
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Info::ChannelEarn {
|
|||
using EarnInt = Data::EarnInt;
|
||||
|
||||
constexpr auto kMinorPartLength = 9;
|
||||
constexpr auto kMaxChoppedZero = kMinorPartLength - 2;
|
||||
constexpr auto kZero = QChar('0');
|
||||
constexpr auto kDot = QChar('.');
|
||||
|
||||
|
@ -35,7 +36,7 @@ QString MinorPart(EarnInt value) {
|
|||
auto ch = end - 1;
|
||||
auto zeroCount = 0;
|
||||
while (ch != begin) {
|
||||
if ((*ch) == kZero) {
|
||||
if (((*ch) == kZero) && (zeroCount < kMaxChoppedZero)) {
|
||||
zeroCount++;
|
||||
} else {
|
||||
break;
|
||||
|
|
|
@ -77,7 +77,7 @@ void ShowMenu(not_null<Ui::GenericBox*> box, const QString &text) {
|
|||
[[nodiscard]] ClickHandlerPtr LearnMoreCurrencyLink(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::GenericBox*> box) {
|
||||
const auto url = u"https://telegram.org/blog/monetization-for-channels"_q;
|
||||
const auto url = tr::lng_channel_earn_learn_coin_link(tr::now);
|
||||
|
||||
using Resolver = HistoryView::Controls::WebpageResolver;
|
||||
const auto resolver = box->lifetime().make_state<Resolver>(
|
||||
|
@ -217,7 +217,18 @@ void AddRecipient(not_null<Ui::GenericBox*> box, const TextWithEntities &t) {
|
|||
}
|
||||
#endif
|
||||
|
||||
[[nodiscard]] QImage IconCurrency(
|
||||
[[nodiscard]] QString FormatDate(const QDateTime &date) {
|
||||
return tr::lng_group_call_starts_short_date(
|
||||
tr::now,
|
||||
lt_date,
|
||||
langDayOfMonth(date.date()),
|
||||
lt_time,
|
||||
QLocale().toString(date.time(), QLocale::ShortFormat));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QImage IconCurrency(
|
||||
const style::FlatLabel &label,
|
||||
const QColor &c) {
|
||||
const auto s = Size(label.style.font->ascent);
|
||||
|
@ -234,17 +245,6 @@ void AddRecipient(not_null<Ui::GenericBox*> box, const TextWithEntities &t) {
|
|||
return image;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString FormatDate(const QDateTime &date) {
|
||||
return tr::lng_group_call_starts_short_date(
|
||||
tr::now,
|
||||
lt_date,
|
||||
langDayOfMonth(date.date()),
|
||||
lt_time,
|
||||
QLocale().toString(date.time(), QLocale::ShortFormat));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
InnerWidget::InnerWidget(
|
||||
QWidget *parent,
|
||||
not_null<Controller*> controller,
|
||||
|
@ -372,6 +372,7 @@ void InnerWidget::fill() {
|
|||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = Painter(widget);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::activeButtonBg);
|
||||
p.drawEllipse(rect);
|
||||
|
|
|
@ -23,6 +23,10 @@ namespace Info::ChannelEarn {
|
|||
|
||||
class Memento;
|
||||
|
||||
[[nodiscard]] QImage IconCurrency(
|
||||
const style::FlatLabel &label,
|
||||
const QColor &c);
|
||||
|
||||
class InnerWidget final : public Ui::VerticalLayout {
|
||||
public:
|
||||
struct ShowRequest final {
|
||||
|
|
|
@ -123,21 +123,25 @@ base::options::toggle ShowPeerIdBelowAbout({
|
|||
|
||||
[[nodiscard]] Fn<void(QString)> UsernamesLinkCallback(
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<Window::SessionController*> controller,
|
||||
const QString &addToLink) {
|
||||
const auto weak = base::make_weak(controller);
|
||||
return [=](QString link) {
|
||||
auto settings = &AyuSettings::getInstance();
|
||||
if (!settings->copyUsernameAsLink) {
|
||||
link = '@' + link.mid(13);
|
||||
} else {
|
||||
if (!link.startsWith(u"https://"_q)) {
|
||||
link = peer->session().createInternalLinkFull(peer->userName())
|
||||
+ addToLink;
|
||||
}
|
||||
if (link.startsWith(u"internal:"_q)) {
|
||||
Core::App().openInternalUrl(link,
|
||||
QVariant::fromValue(ClickHandlerContext{
|
||||
.sessionWindow = weak,
|
||||
}));
|
||||
return;
|
||||
} else if (!link.startsWith(u"https://"_q)) {
|
||||
link = peer->session().createInternalLinkFull(peer->username())
|
||||
+ addToLink;
|
||||
}
|
||||
if (!link.isEmpty()) {
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
show->showToast(tr::lng_username_copied(tr::now));
|
||||
if (const auto window = weak.get()) {
|
||||
window->showToast(tr::lng_username_copied(tr::now));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1055,16 +1059,13 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
|||
UsernameValue(user, true) | rpl::map([=](TextWithEntities u) {
|
||||
return u.text.isEmpty()
|
||||
? TextWithEntities()
|
||||
: Ui::Text::Link(
|
||||
u,
|
||||
user->session().createInternalLinkFull(
|
||||
u.text.mid(1)));
|
||||
: Ui::Text::Link(u, UsernameUrl(user, u.text.mid(1)));
|
||||
}),
|
||||
QString(),
|
||||
st::infoProfileLabeledUsernamePadding);
|
||||
const auto callback = UsernamesLinkCallback(
|
||||
_peer,
|
||||
controller->uiShow(),
|
||||
controller,
|
||||
QString());
|
||||
const auto hook = [=](Ui::FlatLabel::ContextMenuRequest request) {
|
||||
if (!request.link) {
|
||||
|
@ -1108,7 +1109,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
|||
}, copyUsername->lifetime());
|
||||
copyUsername->setClickedCallback([=] {
|
||||
const auto link = user->session().createInternalLinkFull(
|
||||
user->userName());
|
||||
user->username());
|
||||
if (!link.isEmpty()) {
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
controller->showToast(tr::lng_username_copied(tr::now));
|
||||
|
@ -1180,14 +1181,15 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
|||
auto linkText = LinkValue(
|
||||
_peer,
|
||||
true
|
||||
) | rpl::map([=](const QString &link) {
|
||||
return link.isEmpty()
|
||||
) | rpl::map([=](const LinkWithUrl &link) {
|
||||
const auto text = link.text;
|
||||
return text.isEmpty()
|
||||
? TextWithEntities()
|
||||
: Ui::Text::Link(
|
||||
(link.startsWith(u"https://"_q)
|
||||
? link.mid(u"https://"_q.size())
|
||||
: link) + addToLink,
|
||||
link + addToLink);
|
||||
(text.startsWith(u"https://"_q)
|
||||
? text.mid(u"https://"_q.size())
|
||||
: text) + addToLink,
|
||||
(addToLink.isEmpty() ? link.url : (text + addToLink)));
|
||||
});
|
||||
auto linkLine = addInfoOneLine(
|
||||
(topicRootId
|
||||
|
@ -1198,7 +1200,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
|
|||
const auto controller = _controller->parentController();
|
||||
const auto linkCallback = UsernamesLinkCallback(
|
||||
_peer,
|
||||
controller->uiShow(),
|
||||
controller,
|
||||
addToLink);
|
||||
linkLine.text->overrideLinkClickHandler(linkCallback);
|
||||
linkLine.subtext->overrideLinkClickHandler(linkCallback);
|
||||
|
|
|
@ -112,23 +112,22 @@ int TextItem::contentHeight() const {
|
|||
|
||||
} // namespace
|
||||
|
||||
void AddPhoneMenu(not_null<Ui::PopupMenu*> menu, not_null<UserData*> user) {
|
||||
if (user->isSelf()) {
|
||||
return;
|
||||
}
|
||||
bool IsCollectiblePhone(not_null<UserData*> user) {
|
||||
using Strings = std::vector<QString>;
|
||||
const auto prefixes = user->session().appConfig().get<Strings>(
|
||||
u"fragment_prefixes"_q,
|
||||
std::vector<QString>());
|
||||
{
|
||||
const auto proj = [&phone = user->phone()](const QString &p) {
|
||||
return phone.startsWith(p);
|
||||
};
|
||||
if (ranges::none_of(prefixes, proj)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (const auto url = AppConfig::FragmentLink(&user->session())) {
|
||||
Strings{ u"888"_q });
|
||||
const auto phone = user->phone();
|
||||
const auto proj = [&](const QString &p) {
|
||||
return phone.startsWith(p);
|
||||
};
|
||||
return ranges::any_of(prefixes, proj);
|
||||
}
|
||||
|
||||
void AddPhoneMenu(not_null<Ui::PopupMenu*> menu, not_null<UserData*> user) {
|
||||
if (user->isSelf() || !IsCollectiblePhone(user)) {
|
||||
return;
|
||||
} else if (const auto url = AppConfig::FragmentLink(&user->session())) {
|
||||
menu->addSeparator(&st::expandedMenuSeparator);
|
||||
const auto link = Ui::Text::Link(
|
||||
tr::lng_info_mobile_context_menu_fragment_about_link(tr::now),
|
||||
|
|
|
@ -16,6 +16,8 @@ class PopupMenu;
|
|||
namespace Info {
|
||||
namespace Profile {
|
||||
|
||||
[[nodiscard]] bool IsCollectiblePhone(not_null<UserData*> user);
|
||||
|
||||
void AddPhoneMenu(not_null<Ui::PopupMenu*> menu, not_null<UserData*> user);
|
||||
|
||||
} // namespace Profile
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "api/api_chat_participants.h"
|
||||
#include "apiwrap.h"
|
||||
#include "info/profile/info_profile_phone_menu.h"
|
||||
#include "info/profile/info_profile_badge.h"
|
||||
#include "core/application.h"
|
||||
#include "core/click_handler_types.h"
|
||||
|
@ -55,7 +56,7 @@ auto PlainUsernameValue(not_null<PeerData*> peer) {
|
|||
peer->session().changes().peerFlagsValue(peer, UpdateFlag::Username),
|
||||
peer->session().changes().peerFlagsValue(peer, UpdateFlag::Usernames)
|
||||
) | rpl::map([=] {
|
||||
return peer->userName();
|
||||
return peer->username();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -136,14 +137,19 @@ rpl::producer<TextWithEntities> PhoneOrHiddenValue(not_null<UserData*> user) {
|
|||
PlainUsernameValue(user),
|
||||
PlainAboutValue(user),
|
||||
tr::lng_info_mobile_hidden()
|
||||
) | rpl::map([](
|
||||
) | rpl::map([user](
|
||||
const TextWithEntities &phone,
|
||||
const QString &username,
|
||||
const QString &about,
|
||||
const QString &hidden) {
|
||||
return (phone.text.isEmpty() && username.isEmpty() && about.isEmpty())
|
||||
? Ui::Text::WithEntities(hidden)
|
||||
: phone;
|
||||
if (phone.text.isEmpty() && username.isEmpty() && about.isEmpty()) {
|
||||
return Ui::Text::WithEntities(hidden);
|
||||
} else if (IsCollectiblePhone(user)) {
|
||||
return Ui::Text::Link(phone, u"internal:collectible_phone/"_q
|
||||
+ user->phone() + '@' + QString::number(user->id.value));
|
||||
} else {
|
||||
return phone;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -160,15 +166,22 @@ rpl::producer<TextWithEntities> UsernameValue(
|
|||
}) | Ui::Text::ToWithEntities();
|
||||
}
|
||||
|
||||
QString UsernameUrl(not_null<PeerData*> peer, const QString &username) {
|
||||
return peer->isUsernameEditable(username)
|
||||
? peer->session().createInternalLinkFull(username)
|
||||
: (u"internal:collectible_username/"_q
|
||||
+ username
|
||||
+ "@"
|
||||
+ QString::number(peer->id.value));
|
||||
}
|
||||
|
||||
rpl::producer<std::vector<TextWithEntities>> UsernamesValue(
|
||||
not_null<PeerData*> peer) {
|
||||
const auto map = [=](const std::vector<QString> &usernames) {
|
||||
return ranges::views::all(
|
||||
usernames
|
||||
) | ranges::views::transform([&](const QString &u) {
|
||||
return Ui::Text::Link(
|
||||
u,
|
||||
peer->session().createInternalLinkFull(u));
|
||||
return Ui::Text::Link(u, UsernameUrl(peer, u));
|
||||
}) | ranges::to_vector;
|
||||
};
|
||||
auto value = rpl::merge(
|
||||
|
@ -219,14 +232,19 @@ rpl::producer<TextWithEntities> AboutValue(not_null<PeerData*> peer) {
|
|||
});
|
||||
}
|
||||
|
||||
rpl::producer<QString> LinkValue(not_null<PeerData*> peer, bool primary) {
|
||||
rpl::producer<LinkWithUrl> LinkValue(not_null<PeerData*> peer, bool primary) {
|
||||
return (primary
|
||||
? PlainPrimaryUsernameValue(peer)
|
||||
: PlainUsernameValue(peer) | rpl::type_erased()
|
||||
) | rpl::map([=](QString &&username) {
|
||||
return username.isEmpty()
|
||||
? QString()
|
||||
: peer->session().createInternalLinkFull(username);
|
||||
return LinkWithUrl{
|
||||
.text = (username.isEmpty()
|
||||
? QString()
|
||||
: peer->session().createInternalLinkFull(username)),
|
||||
.url = (username.isEmpty()
|
||||
? QString()
|
||||
: UsernameUrl(peer, username)),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -61,14 +61,23 @@ rpl::producer<not_null<PeerData*>> MigratedOrMeValue(
|
|||
bool primary = false);
|
||||
[[nodiscard]] rpl::producer<std::vector<TextWithEntities>> UsernamesValue(
|
||||
not_null<PeerData*> peer);
|
||||
[[nodiscard]] QString UsernameUrl(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &username);
|
||||
[[nodiscard]] TextWithEntities AboutWithEntities(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &value);
|
||||
[[nodiscard]] rpl::producer<TextWithEntities> AboutValue(
|
||||
not_null<PeerData*> peer);
|
||||
[[nodiscard]] rpl::producer<QString> LinkValue(
|
||||
|
||||
struct LinkWithUrl {
|
||||
QString text;
|
||||
QString url;
|
||||
};
|
||||
[[nodiscard]] rpl::producer<LinkWithUrl> LinkValue(
|
||||
not_null<PeerData*> peer,
|
||||
bool primary = false);
|
||||
|
||||
[[nodiscard]] rpl::producer<const ChannelLocation*> LocationValue(
|
||||
not_null<ChannelData*> channel);
|
||||
[[nodiscard]] rpl::producer<bool> NotificationsEnabledValue(
|
||||
|
|
|
@ -17,10 +17,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_web_page.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_domain.h"
|
||||
#include "storage/storage_domain.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "iv/iv_instance.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/chat/attach/attach_bot_webview.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
|
@ -653,6 +655,15 @@ void AttachWebView::botHandleMenuButton(Ui::BotWebView::MenuButton button) {
|
|||
}
|
||||
}
|
||||
|
||||
void AttachWebView::botOpenIvLink(QString uri) {
|
||||
const auto window = _context ? _context->controller.get() : nullptr;
|
||||
if (window) {
|
||||
Core::App().iv().openWithIvPreferred(window, uri);
|
||||
} else {
|
||||
Core::App().iv().openWithIvPreferred(_session, uri);
|
||||
}
|
||||
}
|
||||
|
||||
void AttachWebView::botSendData(QByteArray data) {
|
||||
if (!_context
|
||||
|| _context->fromSwitch
|
||||
|
|
|
@ -165,6 +165,7 @@ private:
|
|||
bool botHandleLocalUri(QString uri, bool keepOpen) override;
|
||||
void botHandleInvoice(QString slug) override;
|
||||
void botHandleMenuButton(Ui::BotWebView::MenuButton button) override;
|
||||
void botOpenIvLink(QString uri) override;
|
||||
void botSendData(QByteArray data) override;
|
||||
void botSwitchInlineQuery(
|
||||
std::vector<QString> chatTypes,
|
||||
|
|
|
@ -25,7 +25,7 @@ QByteArray GeoPointId(Geo point) {
|
|||
const auto lon = int(point.lon * 1000000);
|
||||
const auto combined = (std::uint64_t(std::uint32_t(lat)) << 32)
|
||||
| std::uint64_t(std::uint32_t(lon));
|
||||
return QByteArray::number(combined)
|
||||
return QByteArray::number(quint64(combined))
|
||||
+ ','
|
||||
+ QByteArray::number(point.access);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "core/application.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_cloud_file.h"
|
||||
|
@ -63,7 +64,7 @@ class Shown final : public base::has_weak_ptr {
|
|||
public:
|
||||
Shown(
|
||||
not_null<Delegate*> delegate,
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<Main::Session*> session,
|
||||
not_null<Data*> data,
|
||||
QString hash);
|
||||
|
||||
|
@ -169,12 +170,11 @@ private:
|
|||
|
||||
Shown::Shown(
|
||||
not_null<Delegate*> delegate,
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<Main::Session*> session,
|
||||
not_null<Data*> data,
|
||||
QString hash)
|
||||
: _delegate(delegate)
|
||||
, _session(&show->session())
|
||||
, _show(show) {
|
||||
, _session(session) {
|
||||
prepare(data, hash);
|
||||
}
|
||||
|
||||
|
@ -818,7 +818,13 @@ void Instance::show(
|
|||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<Data*> data,
|
||||
QString hash) {
|
||||
const auto session = &show->session();
|
||||
this->show(&show->session(), data, hash);
|
||||
}
|
||||
|
||||
void Instance::show(
|
||||
not_null<Main::Session*> session,
|
||||
not_null<Data*> data,
|
||||
QString hash) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (data->partial()) {
|
||||
requestFull(session, data->id());
|
||||
|
@ -828,10 +834,13 @@ void Instance::show(
|
|||
_shown->moveTo(data, hash);
|
||||
return;
|
||||
}
|
||||
_shown = std::make_unique<Shown>(_delegate, show, data, hash);
|
||||
_shown = std::make_unique<Shown>(_delegate, session, data, hash);
|
||||
_shownSession = session;
|
||||
_shown->events() | rpl::start_with_next([=](Controller::Event event) {
|
||||
using Type = Controller::Event::Type;
|
||||
const auto lower = event.url.toLower();
|
||||
const auto urlChecked = lower.startsWith("http://")
|
||||
|| lower.startsWith("https://");
|
||||
switch (event.type) {
|
||||
case Type::Close:
|
||||
_shown = nullptr;
|
||||
|
@ -846,7 +855,9 @@ void Instance::show(
|
|||
processJoinChannel(event.context);
|
||||
break;
|
||||
case Type::OpenLinkExternal:
|
||||
File::OpenUrl(event.url);
|
||||
if (urlChecked) {
|
||||
File::OpenUrl(event.url);
|
||||
}
|
||||
closeAll();
|
||||
break;
|
||||
case Type::OpenMedia:
|
||||
|
@ -885,6 +896,9 @@ void Instance::show(
|
|||
break;
|
||||
case Type::OpenPage:
|
||||
case Type::OpenLink:
|
||||
if (!urlChecked) {
|
||||
break;
|
||||
}
|
||||
_shownSession->api().request(MTPmessages_GetWebPage(
|
||||
MTP_string(event.url),
|
||||
MTP_int(0)
|
||||
|
@ -896,7 +910,7 @@ void Instance::show(
|
|||
if (page && page->iv) {
|
||||
const auto parts = event.url.split('#');
|
||||
const auto hash = (parts.size() > 1) ? parts[1] : u""_q;
|
||||
this->show(show, page->iv.get(), hash);
|
||||
this->show(_shownSession, page->iv.get(), hash);
|
||||
} else {
|
||||
UrlClickHandler::Open(event.url);
|
||||
}
|
||||
|
@ -921,20 +935,104 @@ void Instance::show(
|
|||
}
|
||||
}, _shown->lifetime());
|
||||
|
||||
if (!_tracking.contains(session)) {
|
||||
_tracking.emplace(session);
|
||||
session->lifetime().add([=] {
|
||||
_tracking.remove(session);
|
||||
_joining.remove(session);
|
||||
_fullRequested.remove(session);
|
||||
if (_shownSession == session) {
|
||||
_shownSession = nullptr;
|
||||
}
|
||||
if (_shown && _shown->showingFrom(session)) {
|
||||
_shown = nullptr;
|
||||
}
|
||||
});
|
||||
trackSession(session);
|
||||
}
|
||||
|
||||
void Instance::trackSession(not_null<Main::Session*> session) {
|
||||
if (!_tracking.emplace(session).second) {
|
||||
return;
|
||||
}
|
||||
session->lifetime().add([=] {
|
||||
_tracking.remove(session);
|
||||
_joining.remove(session);
|
||||
_fullRequested.remove(session);
|
||||
_ivCache.remove(session);
|
||||
if (_ivRequestSession == session) {
|
||||
session->api().request(_ivRequestId).cancel();
|
||||
_ivRequestSession = nullptr;
|
||||
_ivRequestUri = QString();
|
||||
_ivRequestId = 0;
|
||||
}
|
||||
if (_shownSession == session) {
|
||||
_shownSession = nullptr;
|
||||
}
|
||||
if (_shown && _shown->showingFrom(session)) {
|
||||
_shown = nullptr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Instance::openWithIvPreferred(
|
||||
not_null<Window::SessionController*> controller,
|
||||
QString uri,
|
||||
QVariant context) {
|
||||
auto my = context.value<ClickHandlerContext>();
|
||||
my.sessionWindow = controller;
|
||||
openWithIvPreferred(
|
||||
&controller->session(),
|
||||
uri,
|
||||
QVariant::fromValue(my));
|
||||
}
|
||||
|
||||
void Instance::openWithIvPreferred(
|
||||
not_null<Main::Session*> session,
|
||||
QString uri,
|
||||
QVariant context) {
|
||||
const auto openExternal = [=] {
|
||||
auto my = context.value<ClickHandlerContext>();
|
||||
my.ignoreIv = true;
|
||||
UrlClickHandler::Open(uri, QVariant::fromValue(my));
|
||||
};
|
||||
const auto parts = uri.split('#');
|
||||
if (parts.isEmpty() || parts[0].isEmpty()) {
|
||||
return;
|
||||
} else if (!ShowButton()) {
|
||||
return openExternal();
|
||||
}
|
||||
trackSession(session);
|
||||
const auto hash = (parts.size() > 1) ? parts[1] : u""_q;
|
||||
const auto url = parts[0];
|
||||
auto &cache = _ivCache[session];
|
||||
if (const auto i = cache.find(url); i != end(cache)) {
|
||||
const auto page = i->second;
|
||||
if (page && page->iv) {
|
||||
auto my = context.value<ClickHandlerContext>();
|
||||
if (const auto window = my.sessionWindow.get()) {
|
||||
show(window, page->iv.get(), hash);
|
||||
} else {
|
||||
show(session, page->iv.get(), hash);
|
||||
}
|
||||
} else {
|
||||
openExternal();
|
||||
}
|
||||
return;
|
||||
} else if (_ivRequestSession == session.get() && _ivRequestUri == uri) {
|
||||
return;
|
||||
} else if (_ivRequestId) {
|
||||
_ivRequestSession->api().request(_ivRequestId).cancel();
|
||||
}
|
||||
const auto finish = [=](WebPageData *page) {
|
||||
Expects(_ivRequestSession == session);
|
||||
|
||||
_ivRequestId = 0;
|
||||
_ivRequestUri = QString();
|
||||
_ivRequestSession = nullptr;
|
||||
_ivCache[session][url] = page;
|
||||
openWithIvPreferred(session, uri, context);
|
||||
};
|
||||
_ivRequestSession = session;
|
||||
_ivRequestUri = uri;
|
||||
_ivRequestId = session->api().request(MTPmessages_GetWebPage(
|
||||
MTP_string(url),
|
||||
MTP_int(0)
|
||||
)).done([=](const MTPmessages_WebPage &result) {
|
||||
const auto &data = result.data();
|
||||
session->data().processUsers(data.vusers());
|
||||
session->data().processChats(data.vchats());
|
||||
finish(session->data().processWebpage(data.vwebpage()));
|
||||
}).fail([=] {
|
||||
finish(nullptr);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void Instance::requestFull(
|
||||
|
@ -1028,4 +1126,17 @@ void Instance::closeAll() {
|
|||
_shown = nullptr;
|
||||
}
|
||||
|
||||
bool PreferForUri(const QString &uri) {
|
||||
const auto url = QUrl(uri);
|
||||
const auto host = url.host().toLower();
|
||||
const auto path = url.path().toLower();
|
||||
return (host == u"telegra.ph"_q)
|
||||
|| (host == u"te.legra.ph"_q)
|
||||
|| (host == u"graph.org"_q)
|
||||
|| (host == u"telegram.org"_q
|
||||
&& (path.startsWith(u"/faq"_q)
|
||||
|| path.startsWith(u"/privacy"_q)
|
||||
|| path.startsWith(u"/blog"_q)));
|
||||
}
|
||||
|
||||
} // namespace Iv
|
||||
|
|
|
@ -36,6 +36,19 @@ public:
|
|||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<Data*> data,
|
||||
QString hash);
|
||||
void show(
|
||||
not_null<Main::Session*> session,
|
||||
not_null<Data*> data,
|
||||
QString hash);
|
||||
|
||||
void openWithIvPreferred(
|
||||
not_null<Window::SessionController*> controller,
|
||||
QString uri,
|
||||
QVariant context = {});
|
||||
void openWithIvPreferred(
|
||||
not_null<Main::Session*> session,
|
||||
QString uri,
|
||||
QVariant context = {});
|
||||
|
||||
[[nodiscard]] bool hasActiveWindow(
|
||||
not_null<Main::Session*> session) const;
|
||||
|
@ -52,6 +65,8 @@ private:
|
|||
void processJoinChannel(const QString &context);
|
||||
void requestFull(not_null<Main::Session*> session, const QString &id);
|
||||
|
||||
void trackSession(not_null<Main::Session*> session);
|
||||
|
||||
const not_null<Delegate*> _delegate;
|
||||
|
||||
std::unique_ptr<Shown> _shown;
|
||||
|
@ -64,8 +79,18 @@ private:
|
|||
not_null<Main::Session*>,
|
||||
base::flat_set<QString>> _fullRequested;
|
||||
|
||||
base::flat_map<
|
||||
not_null<Main::Session*>,
|
||||
base::flat_map<QString, WebPageData*>> _ivCache;
|
||||
Main::Session *_ivRequestSession = nullptr;
|
||||
QString _ivRequestUri;
|
||||
mtpRequestId _ivRequestId = 0;
|
||||
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] bool PreferForUri(const QString &uri);
|
||||
|
||||
} // namespace Iv
|
||||
|
|
|
@ -51,7 +51,7 @@ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
|
|||
return Number(base::SafeRound(value * 10000.) / 100.);
|
||||
};
|
||||
|
||||
[[nodiscard]] QByteArray EscapeAttr(QByteArray value) {
|
||||
[[nodiscard]] QByteArray Escape(QByteArray value) {
|
||||
auto result = QByteArray();
|
||||
result.reserve(value.size());
|
||||
for (const auto &ch : value) {
|
||||
|
@ -67,8 +67,8 @@ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
|
|||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QByteArray EscapeText(QByteArray value) {
|
||||
return EscapeAttr(value);
|
||||
[[nodiscard]] QByteArray Date(TimeId date) {
|
||||
return Escape(langDateTimeFull(base::unixtime::parse(date)).toUtf8());
|
||||
}
|
||||
|
||||
class Parser final {
|
||||
|
@ -391,9 +391,7 @@ QByteArray Parser::block(const MTPDpageBlockSubtitle &data) {
|
|||
QByteArray Parser::block(const MTPDpageBlockAuthorDate &data) {
|
||||
auto inner = rich(data.vauthor());
|
||||
if (const auto date = data.vpublished_date().v) {
|
||||
const auto parsed = base::unixtime::parse(date);
|
||||
inner += " \xE2\x80\xA2 "
|
||||
+ tag("time", EscapeText(langDateTimeFull(parsed).toUtf8()));
|
||||
inner += " \xE2\x80\xA2 " + tag("time", Date(date));
|
||||
}
|
||||
return tag("address", inner);
|
||||
}
|
||||
|
@ -412,7 +410,7 @@ QByteArray Parser::block(const MTPDpageBlockParagraph &data) {
|
|||
|
||||
QByteArray Parser::block(const MTPDpageBlockPreformatted &data) {
|
||||
auto list = Attributes();
|
||||
const auto language = EscapeAttr(utf(data.vlanguage()));
|
||||
const auto language = utf(data.vlanguage());
|
||||
if (!language.isEmpty()) {
|
||||
list.push_back({ "data-language", language });
|
||||
list.push_back({ "class", "lang-" + language });
|
||||
|
@ -430,7 +428,7 @@ QByteArray Parser::block(const MTPDpageBlockDivider &data) {
|
|||
}
|
||||
|
||||
QByteArray Parser::block(const MTPDpageBlockAnchor &data) {
|
||||
return tag("a", { { "name", EscapeAttr(utf(data.vname())) } });
|
||||
return tag("a", { { "name", utf(data.vname()) } });
|
||||
}
|
||||
|
||||
QByteArray Parser::block(const MTPDpageBlockList &data) {
|
||||
|
@ -507,15 +505,13 @@ QByteArray Parser::block(
|
|||
};
|
||||
auto result = tag("div", attributes, inner);
|
||||
|
||||
const auto href = data.vurl()
|
||||
? utf(*data.vurl())
|
||||
: photoFullUrl(photo);
|
||||
const auto href = data.vurl() ? utf(*data.vurl()) : photoFullUrl(photo);
|
||||
const auto id = Number(photo.id);
|
||||
result = tag("a", {
|
||||
{ "href", href },
|
||||
{ "oncontextmenu", data.vurl() ? QByteArray() : "return false;" },
|
||||
{ "data-context", data.vurl() ? QByteArray() : "viewer-photo" + id },
|
||||
}, result);
|
||||
}, result);
|
||||
if (!slideshow) {
|
||||
result += caption(data.vcaption());
|
||||
}
|
||||
|
@ -621,9 +617,9 @@ QByteArray Parser::block(const MTPDpageBlockEmbed &data) {
|
|||
attributes.push_back({ "height", iframeHeight });
|
||||
if (const auto url = data.vurl()) {
|
||||
if (!autosize) {
|
||||
attributes.push_back({ "src", EscapeAttr(utf(url)) });
|
||||
attributes.push_back({ "src", utf(url) });
|
||||
} else {
|
||||
attributes.push_back({ "srcdoc", EscapeAttr(utf(url)) });
|
||||
attributes.push_back({ "srcdoc", utf(url) });
|
||||
}
|
||||
} else if (const auto html = data.vhtml()) {
|
||||
attributes.push_back({ "src", embedUrl(html->v) });
|
||||
|
@ -661,12 +657,10 @@ QByteArray Parser::block(const MTPDpageBlockEmbedPost &data) {
|
|||
address += tag(
|
||||
"a",
|
||||
{ { "rel", "author" }, { "onclick", "return false;" } },
|
||||
EscapeText(utf(data.vauthor())));
|
||||
utf(data.vauthor()));
|
||||
if (const auto date = data.vdate().v) {
|
||||
const auto parsed = base::unixtime::parse(date);
|
||||
address += tag(
|
||||
"time",
|
||||
EscapeText(langDateTimeFull(parsed).toUtf8()));
|
||||
address += tag("time", Date(date));
|
||||
}
|
||||
const auto inner = tag("address", address) + list(data.vblocks());
|
||||
result = tag("blockquote", { { "class", "embed-post" } }, inner);
|
||||
|
@ -675,7 +669,7 @@ QByteArray Parser::block(const MTPDpageBlockEmbedPost &data) {
|
|||
const auto inner = tag("strong", utf(data.vauthor()))
|
||||
+ tag(
|
||||
"small",
|
||||
tag("a", { { "href", EscapeAttr(url) } }, EscapeText(url)));
|
||||
tag("a", { { "href", url } }, url));
|
||||
result = tag("section", { { "class", "embed-post" } }, inner);
|
||||
}
|
||||
result += caption(data.vcaption());
|
||||
|
@ -732,7 +726,7 @@ QByteArray Parser::block(const MTPDpageBlockChannel &data) {
|
|||
: "https://t.me/" + username;
|
||||
result = tag(
|
||||
"a",
|
||||
{ { "href", EscapeAttr(link) }, { "data-context", "channel" + id } },
|
||||
{ { "href", link }, { "data-context", "channel" + id } },
|
||||
result);
|
||||
_result.channelIds.emplace(id);
|
||||
return tag("section", {
|
||||
|
@ -839,23 +833,21 @@ QByteArray Parser::block(const MTPDpageRelatedArticle &data) {
|
|||
inner += tag(
|
||||
"span",
|
||||
{ { "class", "related-link-title" } },
|
||||
EscapeText(utf(*title)));
|
||||
utf(*title));
|
||||
}
|
||||
if (description) {
|
||||
inner += tag(
|
||||
"span",
|
||||
{ { "class", "related-link-desc" } },
|
||||
EscapeText(utf(*description)));
|
||||
utf(*description));
|
||||
}
|
||||
if (author || published) {
|
||||
const auto separator = (author && published) ? ", " : "";
|
||||
const auto parsed = base::unixtime::parse(published->v);
|
||||
inner += tag(
|
||||
"span",
|
||||
{ { "class", "related-link-source" } },
|
||||
EscapeText((author ? utf(*author) : QByteArray())
|
||||
+ separator
|
||||
+ langDateTimeFull(parsed).toUtf8()));
|
||||
((author ? utf(*author) : QByteArray())
|
||||
+ ((author && published) ? ", " : QByteArray())
|
||||
+ (published ? Date(published->v) : QByteArray())));
|
||||
}
|
||||
result += tag("span", {
|
||||
{ "class", "related-link-content" },
|
||||
|
@ -912,22 +904,25 @@ QByteArray Parser::block(const MTPDpageListItemBlocks &data) {
|
|||
}
|
||||
|
||||
QByteArray Parser::block(const MTPDpageListOrderedItemText &data) {
|
||||
return tag("li", { { "value", utf(data.vnum()) } }, rich(data.vtext()));
|
||||
return tag(
|
||||
"li",
|
||||
{ { "value", utf(data.vnum()) } },
|
||||
rich(data.vtext()));
|
||||
}
|
||||
|
||||
QByteArray Parser::block(const MTPDpageListOrderedItemBlocks &data) {
|
||||
return tag(
|
||||
"li",
|
||||
{ { "value", EscapeAttr(utf(data.vnum())) } },
|
||||
{ { "value", utf(data.vnum()) } },
|
||||
list(data.vblocks()));
|
||||
}
|
||||
|
||||
QByteArray Parser::utf(const MTPstring &text) {
|
||||
return text.v;
|
||||
return Escape(text.v);
|
||||
}
|
||||
|
||||
QByteArray Parser::utf(const tl::conditional<MTPstring> &text) {
|
||||
return text ? text->v : QByteArray();
|
||||
return text ? utf(*text) : QByteArray();
|
||||
}
|
||||
|
||||
QByteArray Parser::tag(
|
||||
|
@ -965,7 +960,7 @@ QByteArray Parser::rich(const MTPRichText &text) {
|
|||
{ "\xE2\x81\xA8", "<span dir=\"auto\">" },
|
||||
{ "\xE2\x81\xA9", "</span>" },
|
||||
};
|
||||
auto text = EscapeText(utf(data.vtext()));
|
||||
auto text = utf(data.vtext());
|
||||
for (const auto &[from, to] : replacements) {
|
||||
text.replace(from, to);
|
||||
}
|
||||
|
@ -1016,8 +1011,8 @@ QByteArray Parser::rich(const MTPRichText &text) {
|
|||
}, rich(data.vtext()));
|
||||
}, [&](const MTPDtextEmail &data) {
|
||||
return tag("a", {
|
||||
{ "href", "mailto:" + EscapeAttr(utf(data.vemail())) },
|
||||
}, rich(data.vtext()));
|
||||
{ "href", "mailto:" + utf(data.vemail()) },
|
||||
}, rich(data.vtext()));
|
||||
}, [&](const MTPDtextSubscript &data) {
|
||||
return tag("sub", rich(data.vtext()));
|
||||
}, [&](const MTPDtextSuperscript &data) {
|
||||
|
@ -1026,11 +1021,11 @@ QByteArray Parser::rich(const MTPRichText &text) {
|
|||
return tag("mark", rich(data.vtext()));
|
||||
}, [&](const MTPDtextPhone &data) {
|
||||
return tag("a", {
|
||||
{ "href", "tel:" + EscapeAttr(utf(data.vphone())) },
|
||||
{ "href", "tel:" + utf(data.vphone()) },
|
||||
}, rich(data.vtext()));
|
||||
}, [&](const MTPDtextAnchor &data) {
|
||||
const auto inner = rich(data.vtext());
|
||||
const auto name = EscapeAttr(utf(data.vname()));
|
||||
const auto name = utf(data.vname());
|
||||
return inner.isEmpty()
|
||||
? tag("a", { { "name", name } })
|
||||
: tag(
|
||||
|
|
|
@ -32,11 +32,13 @@ Domain::Domain(const QString &dataName)
|
|||
: _dataName(dataName)
|
||||
, _local(std::make_unique<Storage::Domain>(this, dataName)) {
|
||||
_active.changes(
|
||||
) | rpl::take(1) | rpl::start_with_next([] {
|
||||
) | rpl::take(1) | rpl::start_with_next([=] {
|
||||
// In case we had a legacy passcoded app we start settings here.
|
||||
Core::App().startSettingsAndBackground();
|
||||
|
||||
Core::App().notifications().createManager();
|
||||
crl::on_main(this, [=] {
|
||||
Core::App().notifications().createManager();
|
||||
});
|
||||
}, _lifetime);
|
||||
|
||||
_active.changes(
|
||||
|
@ -51,7 +53,7 @@ Domain::Domain(const QString &dataName)
|
|||
: rpl::never<Data::PeerUpdate>();
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::start_with_next([](const Data::PeerUpdate &update) {
|
||||
CrashReports::SetAnnotation("Username", update.peer->userName());
|
||||
CrashReports::SetAnnotation("Username", update.peer->username());
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "base/timer.h"
|
||||
#include "base/weak_ptr.h"
|
||||
|
||||
namespace Storage {
|
||||
class Domain;
|
||||
|
@ -23,7 +24,7 @@ namespace Main {
|
|||
class Account;
|
||||
class Session;
|
||||
|
||||
class Domain final {
|
||||
class Domain final : public base::has_weak_ptr {
|
||||
public:
|
||||
struct AccountWithIndex {
|
||||
int index = 0;
|
||||
|
|
|
@ -16,7 +16,6 @@ struct HistoryMessageMarkupButton;
|
|||
class MainWindow;
|
||||
class HistoryWidget;
|
||||
class StackItem;
|
||||
struct FileLoadResult;
|
||||
class History;
|
||||
class Image;
|
||||
|
||||
|
|
|
@ -933,6 +933,13 @@ void Controller::show(
|
|||
peer->updateFull();
|
||||
}
|
||||
|
||||
void Controller::jumpTo(
|
||||
not_null<Data::Story*> story,
|
||||
Data::StoriesContext context) {
|
||||
show(story, std::move(context));
|
||||
_delegate->storiesRedisplay(story);
|
||||
}
|
||||
|
||||
bool Controller::changeShown(Data::Story *story) {
|
||||
const auto id = story ? story->fullId() : FullStoryId();
|
||||
const auto session = story ? &story->session() : nullptr;
|
||||
|
|
|
@ -141,6 +141,7 @@ public:
|
|||
-> HistoryView::Reactions::CachedIconFactory &;
|
||||
|
||||
void show(not_null<Data::Story*> story, Data::StoriesContext context);
|
||||
void jumpTo(not_null<Data::Story*> story, Data::StoriesContext context);
|
||||
void ready();
|
||||
|
||||
void updateVideoPlayback(const Player::TrackState &state);
|
||||
|
|
|
@ -431,7 +431,7 @@ void ReplyArea::chooseAttach(
|
|||
}
|
||||
|
||||
const auto filter = (overrideSendImagesAsPhotos == true)
|
||||
? FileDialog::ImagesOrAllFilter()
|
||||
? FileDialog::PhotoVideoFilesFilter()
|
||||
: FileDialog::AllOrImagesFilter();
|
||||
const auto weak = make_weak(&_shownPeerGuard);
|
||||
const auto callback = [=](FileDialog::OpenResult &&result) {
|
||||
|
|
|
@ -184,7 +184,7 @@ RepostClickHandler RepostView::lookupHandler(QPoint position) {
|
|||
const auto of = owner->stories().lookup({ peer->id, id });
|
||||
if (of) {
|
||||
using namespace Data;
|
||||
_controller->show(*of, { StoriesContextSingle() });
|
||||
_controller->jumpTo(*of, { StoriesContextSingle() });
|
||||
} else {
|
||||
_controller->uiShow()->show(PrepareShortInfoBox(peer));
|
||||
}
|
||||
|
|
|
@ -3262,7 +3262,7 @@ not_null<QWidget*> OverlayWidget::widget() const {
|
|||
|
||||
void OverlayWidget::hide() {
|
||||
clearBeforeHide();
|
||||
//applyHideWindowWorkaround();
|
||||
applyHideWindowWorkaround();
|
||||
_window->hide();
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ void AboutBox(
|
|||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = Painter(widget);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::activeButtonBg);
|
||||
p.drawEllipse(rect);
|
||||
|
|
|
@ -40,10 +40,6 @@ constexpr ShiftedDcId groupCallStreamDcId(DcId dcId) {
|
|||
return ShiftDcId(dcId, kGroupCallStreamDcShift);
|
||||
}
|
||||
|
||||
constexpr auto kUploadSessionsCount = 2;
|
||||
|
||||
constexpr auto kUploadSessionsCountMax = 8;
|
||||
|
||||
namespace details {
|
||||
|
||||
constexpr ShiftedDcId downloadDcId(DcId dcId, int index) {
|
||||
|
@ -94,7 +90,6 @@ inline DcId getTemporaryIdFromRealDcId(ShiftedDcId shiftedDcId) {
|
|||
namespace details {
|
||||
|
||||
constexpr ShiftedDcId uploadDcId(DcId dcId, int index) {
|
||||
static_assert(kUploadSessionsCountMax < kMaxMediaDcCount, "Too large MTPUploadSessionsCount!");
|
||||
return ShiftDcId(dcId, kBaseUploadDcShift + index);
|
||||
};
|
||||
|
||||
|
@ -103,14 +98,14 @@ constexpr ShiftedDcId uploadDcId(DcId dcId, int index) {
|
|||
// send(req, callbacks, MTP::uploadDcId(index)) - for upload shifted dc id
|
||||
// uploading always to the main dc so BareDcId(result) == 0
|
||||
inline ShiftedDcId uploadDcId(int index) {
|
||||
Expects(index >= 0 && index < kUploadSessionsCountMax);
|
||||
Expects(index >= 0 && index < kMaxMediaDcCount);
|
||||
|
||||
return details::uploadDcId(0, index);
|
||||
};
|
||||
|
||||
constexpr bool isUploadDcId(ShiftedDcId shiftedDcId) {
|
||||
return (shiftedDcId >= details::uploadDcId(0, 0))
|
||||
&& (shiftedDcId < details::uploadDcId(0, kUploadSessionsCountMax - 1) + kDcShift);
|
||||
&& (shiftedDcId < details::uploadDcId(0, kMaxMediaDcCount - 1) + kDcShift);
|
||||
}
|
||||
|
||||
inline ShiftedDcId destroyKeyNextDcId(ShiftedDcId shiftedDcId) {
|
||||
|
|
|
@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "storage/localstorage.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "main/main_account.h" // Account::configUpdated.
|
||||
#include "apiwrap.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "lang/lang_instance.h"
|
||||
|
|
|
@ -60,6 +60,8 @@ constexpr auto kSendStateRequestWaiting = crl::time(1000);
|
|||
// How much time to wait for some more requests, when sending msg acks.
|
||||
constexpr auto kAckSendWaiting = 10 * crl::time(1000);
|
||||
|
||||
constexpr auto kCutContainerOnSize = 16 * 1024;
|
||||
|
||||
auto SyncTimeRequestDuration = kFastRequestDuration;
|
||||
|
||||
using namespace details;
|
||||
|
@ -696,7 +698,8 @@ void SessionPrivate::tryToSend() {
|
|||
initSize = initSizeInInts * sizeof(mtpPrime);
|
||||
}
|
||||
|
||||
bool needAnyResponse = false;
|
||||
auto needAnyResponse = false;
|
||||
auto someSkipped = false;
|
||||
SerializedRequest toSendRequest;
|
||||
{
|
||||
QWriteLocker locker1(_sessionData->toSendMutex());
|
||||
|
@ -711,15 +714,33 @@ void SessionPrivate::tryToSend() {
|
|||
locker1.unlock();
|
||||
}
|
||||
|
||||
uint32 toSendCount = toSend.size();
|
||||
if (pingRequest) ++toSendCount;
|
||||
if (ackRequest) ++toSendCount;
|
||||
if (resendRequest) ++toSendCount;
|
||||
if (stateRequest) ++toSendCount;
|
||||
if (httpWaitRequest) ++toSendCount;
|
||||
if (bindDcKeyRequest) ++toSendCount;
|
||||
auto totalSending = int(toSend.size());
|
||||
auto sendingFrom = begin(toSend);
|
||||
auto sendingTill = end(toSend);
|
||||
auto combinedLength = 0;
|
||||
for (auto i = sendingFrom; i != sendingTill; ++i) {
|
||||
combinedLength += i->second->size();
|
||||
if (combinedLength >= kCutContainerOnSize) {
|
||||
++i;
|
||||
if (const auto skipping = int(sendingTill - i)) {
|
||||
sendingTill = i;
|
||||
totalSending -= skipping;
|
||||
Assert(totalSending > 0);
|
||||
someSkipped = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto sendingRange = ranges::make_subrange(sendingFrom, sendingTill);
|
||||
const auto sendingCount = totalSending;
|
||||
if (pingRequest) ++totalSending;
|
||||
if (ackRequest) ++totalSending;
|
||||
if (resendRequest) ++totalSending;
|
||||
if (stateRequest) ++totalSending;
|
||||
if (httpWaitRequest) ++totalSending;
|
||||
if (bindDcKeyRequest) ++totalSending;
|
||||
|
||||
if (!toSendCount) {
|
||||
if (!totalSending) {
|
||||
return; // nothing to send
|
||||
}
|
||||
|
||||
|
@ -735,11 +756,11 @@ void SessionPrivate::tryToSend() {
|
|||
? httpWaitRequest
|
||||
: bindDcKeyRequest
|
||||
? bindDcKeyRequest
|
||||
: toSend.begin()->second;
|
||||
if (toSendCount == 1 && !first->forceSendInContainer) {
|
||||
: sendingRange.begin()->second;
|
||||
if (totalSending == 1 && !first->forceSendInContainer) {
|
||||
toSendRequest = first;
|
||||
if (sendAll) {
|
||||
toSend.clear();
|
||||
toSend.erase(sendingFrom, sendingTill);
|
||||
locker1.unlock();
|
||||
}
|
||||
|
||||
|
@ -808,7 +829,7 @@ void SessionPrivate::tryToSend() {
|
|||
if (stateRequest) containerSize += stateRequest.messageSize();
|
||||
if (httpWaitRequest) containerSize += httpWaitRequest.messageSize();
|
||||
if (bindDcKeyRequest) containerSize += bindDcKeyRequest.messageSize();
|
||||
for (const auto &[requestId, request] : toSend) {
|
||||
for (const auto &[requestId, request] : sendingRange) {
|
||||
containerSize += request.messageSize();
|
||||
if (needsLayer && request->needsLayer) {
|
||||
containerSize += initSizeInInts;
|
||||
|
@ -825,9 +846,9 @@ void SessionPrivate::tryToSend() {
|
|||
// prepare container + each in invoke after
|
||||
toSendRequest = SerializedRequest::Prepare(
|
||||
containerSize,
|
||||
containerSize + 3 * toSend.size());
|
||||
containerSize + 3 * sendingCount);
|
||||
toSendRequest->push_back(mtpc_msg_container);
|
||||
toSendRequest->push_back(toSendCount);
|
||||
toSendRequest->push_back(totalSending);
|
||||
|
||||
// check for a valid container
|
||||
auto bigMsgId = base::unixtime::mtproto_msg_id();
|
||||
|
@ -839,7 +860,7 @@ void SessionPrivate::tryToSend() {
|
|||
// prepare sent container
|
||||
auto sentIdsWrap = SentContainer();
|
||||
sentIdsWrap.sent = crl::now();
|
||||
sentIdsWrap.messages.reserve(toSendCount);
|
||||
sentIdsWrap.messages.reserve(totalSending);
|
||||
|
||||
if (bindDcKeyRequest) {
|
||||
_bindMsgId = placeToContainer(
|
||||
|
@ -848,6 +869,7 @@ void SessionPrivate::tryToSend() {
|
|||
false,
|
||||
bindDcKeyRequest);
|
||||
_bindMessageSent = crl::now();
|
||||
sentIdsWrap.messages.push_back(_bindMsgId);
|
||||
needAnyResponse = true;
|
||||
}
|
||||
if (pingRequest) {
|
||||
|
@ -856,10 +878,11 @@ void SessionPrivate::tryToSend() {
|
|||
bigMsgId,
|
||||
forceNewMsgId,
|
||||
pingRequest);
|
||||
sentIdsWrap.messages.push_back(_pingMsgId);
|
||||
needAnyResponse = true;
|
||||
}
|
||||
|
||||
for (auto &[requestId, request] : toSend) {
|
||||
for (auto &[requestId, request] : sendingRange) {
|
||||
const auto msgId = prepareToSend(
|
||||
request,
|
||||
bigMsgId,
|
||||
|
@ -904,7 +927,7 @@ void SessionPrivate::tryToSend() {
|
|||
memcpy(toSendRequest->data() + from, request->constData() + 4, len * sizeof(mtpPrime));
|
||||
}
|
||||
}
|
||||
toSend.clear();
|
||||
toSend.erase(sendingFrom, sendingTill);
|
||||
|
||||
if (stateRequest) {
|
||||
const auto msgId = placeToContainer(
|
||||
|
@ -951,6 +974,11 @@ void SessionPrivate::tryToSend() {
|
|||
}
|
||||
}
|
||||
sendSecureRequest(std::move(toSendRequest), needAnyResponse);
|
||||
if (someSkipped) {
|
||||
InvokeQueued(this, [=] {
|
||||
tryToSend();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void SessionPrivate::retryByTimer() {
|
||||
|
@ -1108,9 +1136,6 @@ void SessionPrivate::onSentSome(uint64 size) {
|
|||
DEBUG_LOG(("Checking connect for request with size %1 bytes, delay will be %2").arg(size).arg(remain));
|
||||
}
|
||||
}
|
||||
if (isUploadDcId(_shiftedDcId)) {
|
||||
remain *= kUploadSessionsCount;
|
||||
}
|
||||
_waitForReceivedTimer.callOnce(remain);
|
||||
}
|
||||
if (!_firstSentAt) {
|
||||
|
@ -1577,13 +1602,22 @@ SessionPrivate::HandleResult SessionPrivate::handleOneReceived(
|
|||
correctUnixtimeWithBadLocal(info.serverTime);
|
||||
info.badTime = false;
|
||||
}
|
||||
if (_bindMsgId) {
|
||||
LOG(("Message Info: bad message notification received"
|
||||
" while binding temp key, restarting."));
|
||||
return HandleResult::RestartConnection;
|
||||
}
|
||||
LOG(("Message Info: bad message notification received, msgId %1, error_code %2").arg(data.vbad_msg_id().v).arg(errorCode));
|
||||
return HandleResult::ResetSession;
|
||||
}
|
||||
} else { // fatal (except 48, but it must not get here)
|
||||
const auto badMsgId = mtpMsgId(data.vbad_msg_id().v);
|
||||
const auto requestId = wasSent(resendId);
|
||||
if (requestId) {
|
||||
if (_bindMsgId) {
|
||||
LOG(("Message Error: fatal bad message notification received"
|
||||
" while binding temp key, restarting."));
|
||||
return HandleResult::RestartConnection;
|
||||
} else if (requestId) {
|
||||
LOG(("Message Error: "
|
||||
"fatal bad message notification received, "
|
||||
"msgId %1, error_code %2, requestId: %3"
|
||||
|
@ -1628,7 +1662,9 @@ SessionPrivate::HandleResult SessionPrivate::handleOneReceived(
|
|||
}
|
||||
|
||||
_sessionSalt = data.vnew_server_salt().v;
|
||||
correctUnixtimeWithBadLocal(info.serverTime);
|
||||
|
||||
// Don't force time update here.
|
||||
base::unixtime::update(info.serverTime);
|
||||
|
||||
if (_bindMsgId) {
|
||||
LOG(("Message Info: bad_server_salt received while binding temp key, restarting."));
|
||||
|
@ -2056,7 +2092,7 @@ void SessionPrivate::correctUnixtimeByFastRequest(
|
|||
locker.unlock();
|
||||
|
||||
SyncTimeRequestDuration = duration;
|
||||
base::unixtime::update(serverTime, true);
|
||||
base::unixtime::update(serverTime);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1581,14 +1581,10 @@ void FormController::uploadEncryptedFile(
|
|||
&session(),
|
||||
std::make_unique<UploadScanData>(std::move(data)));
|
||||
|
||||
auto prepared = std::make_shared<FileLoadResult>(
|
||||
TaskId(),
|
||||
file.uploadData->fileId,
|
||||
FileLoadTo(PeerId(), Api::SendOptions(), FullReplyTo(), MsgId()),
|
||||
TextWithTags(),
|
||||
false,
|
||||
std::shared_ptr<SendingAlbum>(nullptr));
|
||||
prepared->type = SendMediaType::Secure;
|
||||
auto prepared = MakePreparedFile({
|
||||
.id = file.uploadData->fileId,
|
||||
.type = SendMediaType::Secure,
|
||||
});
|
||||
prepared->content = QByteArray::fromRawData(
|
||||
reinterpret_cast<char*>(file.uploadData->bytes.data()),
|
||||
file.uploadData->bytes.size());
|
||||
|
|
|
@ -37,6 +37,7 @@ namespace Notifications {
|
|||
namespace {
|
||||
|
||||
using namespace gi::repository;
|
||||
namespace GObject = gi::repository::GObject;
|
||||
|
||||
constexpr auto kService = "org.freedesktop.Notifications";
|
||||
constexpr auto kObjectPath = "/org/freedesktop/Notifications";
|
||||
|
|
|
@ -510,7 +510,7 @@ QString SingleInstanceLocalServerName(const QString &hash) {
|
|||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
|
||||
std::optional<bool> IsDarkMode() {
|
||||
const auto result = base::Platform::XDP::ReadSetting(
|
||||
auto result = base::Platform::XDP::ReadSetting(
|
||||
"org.freedesktop.appearance",
|
||||
"color-scheme");
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ void AwayMessage::setupContent(
|
|||
|
||||
_recipients = disabled
|
||||
? Data::BusinessRecipients{ .allButExcluded = true }
|
||||
: current.recipients;
|
||||
: Data::BusinessRecipients::MakeValid(current.recipients);
|
||||
auto initialSchedule = disabled ? AwaySchedule{
|
||||
.type = AwayScheduleType::Always,
|
||||
} : current.schedule;
|
||||
|
|
|
@ -160,7 +160,7 @@ private:
|
|||
object_ptr<Ui::InputField>(
|
||||
container,
|
||||
st::settingsChatIntroField,
|
||||
tr::lng_chat_intro_enter_title(),
|
||||
std::move(placeholder),
|
||||
current),
|
||||
st::settingsChatIntroFieldMargins);
|
||||
field->setMaxLength(limit);
|
||||
|
|
|
@ -408,7 +408,7 @@ void Chatbots::setupContent(
|
|||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
const auto current = controller->session().data().chatbots().current();
|
||||
|
||||
_recipients = current.recipients;
|
||||
_recipients = Data::BusinessRecipients::MakeValid(current.recipients);
|
||||
_repliesAllowed = current.repliesAllowed;
|
||||
|
||||
AddDividerTextWithLottie(content, {
|
||||
|
|
|
@ -125,7 +125,7 @@ void Greeting::setupContent(
|
|||
|
||||
_recipients = disabled
|
||||
? Data::BusinessRecipients{ .allButExcluded = true }
|
||||
: current.recipients;
|
||||
: Data::BusinessRecipients::MakeValid(current.recipients);
|
||||
_noActivityDays = disabled
|
||||
? kDefaultNoActivityDays
|
||||
: current.noActivityDays;
|
||||
|
|
|
@ -1428,7 +1428,7 @@ void ShortcutMessages::chooseAttach(
|
|||
_choosingAttach = false;
|
||||
|
||||
const auto filter = (overrideSendImagesAsPhotos == true)
|
||||
? FileDialog::ImagesOrAllFilter()
|
||||
? FileDialog::PhotoVideoFilesFilter()
|
||||
: FileDialog::AllOrImagesFilter();
|
||||
FileDialog::GetOpenPaths(this, tr::lng_choose_files(tr::now), filter, crl::guard(this, [=](
|
||||
FileDialog::OpenResult &&result) {
|
||||
|
|
|
@ -11,10 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "settings/settings_main.h"
|
||||
#include "settings/settings_chat.h"
|
||||
#include "settings/settings_codes.h"
|
||||
#include "ui/basic_click_handlers.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
|
@ -104,7 +104,14 @@ object_ptr<Ui::RpWidget> CreateIntroSettings(
|
|||
|
||||
Ui::AddDivider(result);
|
||||
Ui::AddSkip(result);
|
||||
SetupFaq(result, false);
|
||||
|
||||
AddButtonWithIcon(
|
||||
result,
|
||||
tr::lng_settings_faq(),
|
||||
st::settingsButtonNoIcon
|
||||
)->addClickHandler([] {
|
||||
OpenFaq(nullptr);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "settings/settings_main.h"
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "settings/settings_business.h"
|
||||
#include "settings/settings_codes.h"
|
||||
#include "settings/settings_chat.h"
|
||||
|
@ -26,13 +27,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/basic_click_handlers.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/new_badges.h"
|
||||
|
@ -43,9 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_session.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_peer_values.h" // Data::AmPremiumValue
|
||||
#include "lang/lang_cloud_manager.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "lang/lang_instance.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "main/main_session.h"
|
||||
|
@ -62,13 +57,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "info/profile/info_profile_values.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/application.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
|
@ -229,7 +220,7 @@ void Cover::initViewers() {
|
|||
}, lifetime());
|
||||
|
||||
_username->overrideLinkClickHandler([=] {
|
||||
const auto username = _user->userName();
|
||||
const auto username = _user->username();
|
||||
if (username.isEmpty()) {
|
||||
_controller->show(Box(UsernamesBox, _user));
|
||||
} else {
|
||||
|
@ -625,26 +616,20 @@ void SetupInterfaceScale(
|
|||
}
|
||||
}
|
||||
|
||||
void OpenFaq() {
|
||||
File::OpenUrl(telegramFaqLink());
|
||||
}
|
||||
|
||||
void SetupFaq(not_null<Ui::VerticalLayout*> container, bool icon) {
|
||||
AddButtonWithIcon(
|
||||
container,
|
||||
tr::lng_settings_faq(),
|
||||
icon ? st::settingsButton : st::settingsButtonNoIcon,
|
||||
{ icon ? &st::menuIconFaq : nullptr }
|
||||
)->addClickHandler(OpenFaq);
|
||||
}
|
||||
|
||||
void SetupHelp(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Ui::VerticalLayout*> container) {
|
||||
Ui::AddDivider(container);
|
||||
Ui::AddSkip(container);
|
||||
|
||||
SetupFaq(container);
|
||||
AddButtonWithIcon(
|
||||
container,
|
||||
tr::lng_settings_faq(),
|
||||
st::settingsButton,
|
||||
{ &st::menuIconFaq }
|
||||
)->addClickHandler([=] {
|
||||
OpenFaq(controller);
|
||||
});
|
||||
|
||||
AddButtonWithIcon(
|
||||
container,
|
||||
|
@ -688,7 +673,10 @@ void SetupHelp(
|
|||
auto box = Ui::MakeConfirmBox({
|
||||
.text = tr::lng_settings_ask_sure(),
|
||||
.confirmed = sure,
|
||||
.cancelled = OpenFaq,
|
||||
.cancelled = [=](Fn<void()> close) {
|
||||
OpenFaq(controller);
|
||||
close();
|
||||
},
|
||||
.confirmText = tr::lng_settings_ask_ok(),
|
||||
.cancelText = tr::lng_settings_faq_button(),
|
||||
.strictCancel = true,
|
||||
|
@ -767,4 +755,12 @@ void Main::setupContent(not_null<Window::SessionController*> controller) {
|
|||
controller->session().data().cloudThemes().refresh();
|
||||
}
|
||||
|
||||
void OpenFaq(base::weak_ptr<Window::SessionController> weak) {
|
||||
UrlClickHandler::Open(
|
||||
tr::lng_settings_faq_link(tr::now),
|
||||
QVariant::fromValue(ClickHandlerContext{
|
||||
.sessionWindow = weak,
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
|
|
|
@ -28,9 +28,8 @@ void SetupInterfaceScale(
|
|||
not_null<Window::Controller*> window,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
bool icon = true);
|
||||
void SetupFaq(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
bool icon = true);
|
||||
|
||||
void OpenFaq(base::weak_ptr<Window::SessionController> weak);
|
||||
|
||||
class Main : public Section<Main> {
|
||||
public:
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue