Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c04f68f25c | ||
|
|
e4902efefc | ||
|
|
1267bcd255 | ||
|
|
0659ccc3f0 | ||
|
|
bd2ae03ab4 | ||
|
|
244696ae24 | ||
|
|
1438046dd4 | ||
|
|
5c62ba0835 | ||
|
|
20fadfef7f | ||
|
|
eed9541f9f | ||
|
|
1594afa389 | ||
|
|
9d74d93ed7 | ||
|
|
e4e2f47f8e | ||
|
|
be53bec9b7 | ||
|
|
bb32c546d4 | ||
|
|
ecb4ceec7b | ||
|
|
c080bd4c4d | ||
|
|
39780f49bf | ||
|
|
73349c3c89 | ||
|
|
42a70ff7d0 |
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="5.8.0.0" />
|
||||
Version="5.8.2.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 5,8,0,0
|
||||
PRODUCTVERSION 5,8,0,0
|
||||
FILEVERSION 5,8,2,0
|
||||
PRODUCTVERSION 5,8,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "5.8.0.0"
|
||||
VALUE "FileVersion", "5.8.2.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "5.8.0.0"
|
||||
VALUE "ProductVersion", "5.8.2.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5,8,0,0
|
||||
PRODUCTVERSION 5,8,0,0
|
||||
FILEVERSION 5,8,2,0
|
||||
PRODUCTVERSION 5,8,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "5.8.0.0"
|
||||
VALUE "FileVersion", "5.8.2.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "5.8.0.0"
|
||||
VALUE "ProductVersion", "5.8.2.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -40,6 +40,7 @@ void HandleWithdrawalButton(
|
||||
std::shared_ptr<Ui::Show> show) {
|
||||
Expects(receiver.currencyReceiver
|
||||
|| (receiver.creditsReceiver && receiver.creditsAmount));
|
||||
|
||||
struct State {
|
||||
rpl::lifetime lifetime;
|
||||
bool loading = false;
|
||||
@@ -58,8 +59,7 @@ void HandleWithdrawalButton(
|
||||
const auto processOut = [=] {
|
||||
if (state->loading) {
|
||||
return;
|
||||
}
|
||||
if (peer && !receiver.creditsAmount()) {
|
||||
} else if (peer && !receiver.creditsAmount()) {
|
||||
return;
|
||||
}
|
||||
state->loading = true;
|
||||
|
||||
@@ -4028,13 +4028,17 @@ void ApiWrap::sendInlineResult(
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
done(true);
|
||||
if (done) {
|
||||
done(true);
|
||||
}
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
sendMessageFail(error, peer, randomId, newId);
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
done(false);
|
||||
if (done) {
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
finishForwarding(action);
|
||||
}
|
||||
|
||||
@@ -240,7 +240,8 @@ int LocalStorageBox::Row::resizeGetHeight(int newWidth) {
|
||||
}
|
||||
|
||||
void LocalStorageBox::Row::paintEvent(QPaintEvent *e) {
|
||||
if (!_progress || true) {
|
||||
#if 0 // not used
|
||||
if (!_progress) {
|
||||
return;
|
||||
}
|
||||
auto p = QPainter(this);
|
||||
@@ -254,6 +255,7 @@ void LocalStorageBox::Row::paintEvent(QPaintEvent *e) {
|
||||
st::proxyCheckingPosition.y() + bottom
|
||||
},
|
||||
width());
|
||||
#endif
|
||||
}
|
||||
|
||||
QString LocalStorageBox::Row::titleText(const Database::TaggedSummary &data) const {
|
||||
|
||||
@@ -822,7 +822,7 @@ void StickersListFooter::mousePressEvent(QMouseEvent *e) {
|
||||
if (e->button() != Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
|
||||
_iconsMousePos = e->globalPos();
|
||||
updateSelected();
|
||||
|
||||
if (_selected == SpecialOver::Settings) {
|
||||
|
||||
@@ -192,9 +192,7 @@ std::unique_ptr<Lottie::SinglePlayer> LottieThumbnail(
|
||||
};
|
||||
const auto session = thumb
|
||||
? &thumb->owner()->session()
|
||||
: media
|
||||
? &media->owner()->session()
|
||||
: nullptr;
|
||||
: &media->owner()->session();
|
||||
return LottieCachedFromContent(
|
||||
method,
|
||||
baseKey,
|
||||
|
||||
@@ -189,6 +189,7 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const {
|
||||
const auto game = media ? media->game() : nullptr;
|
||||
if (url.startsWith(u"tg://"_q, Qt::CaseInsensitive) || !_bot || !game) {
|
||||
openLink();
|
||||
return;
|
||||
}
|
||||
const auto bot = _bot;
|
||||
const auto title = game->title;
|
||||
|
||||
@@ -340,12 +340,12 @@ void PhoneClickHandler::onClick(ClickContext context) const {
|
||||
if (Trim(phone) != Trim(controller->session().user()->phone())) {
|
||||
menu->addAction(
|
||||
tr::lng_info_add_as_contact(tr::now),
|
||||
[=, raw = resolvePhoneAction.get()] {
|
||||
[=, raw = Ui::MakeWeak(resolvePhoneAction.get())] {
|
||||
controller->show(
|
||||
Box<AddContactBox>(
|
||||
_session,
|
||||
raw->firstName(),
|
||||
raw->lastName(),
|
||||
&controller->session(),
|
||||
raw ? raw->firstName() : QString(),
|
||||
raw ? raw->lastName() : QString(),
|
||||
Trim(phone)));
|
||||
},
|
||||
&st::menuIconInvite);
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
|
||||
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
|
||||
constexpr auto AppName = "Telegram Desktop"_cs;
|
||||
constexpr auto AppFile = "Telegram"_cs;
|
||||
constexpr auto AppVersion = 5008000;
|
||||
constexpr auto AppVersionStr = "5.8";
|
||||
constexpr auto AppVersion = 5008002;
|
||||
constexpr auto AppVersionStr = "5.8.2";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -126,7 +126,6 @@ void RecentPeers::applyLocal(QByteArray serialized) {
|
||||
).arg(count));
|
||||
DEBUG_LOG(("Failed bytes: %1.").arg(
|
||||
QString::fromUtf8(serialized.mid(streamPosition).toHex())));
|
||||
_list.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,22 +206,20 @@ void Stickers::incrementSticker(not_null<DocumentData*> document) {
|
||||
auto &sets = setsRef();
|
||||
auto it = sets.find(Data::Stickers::CloudRecentSetId);
|
||||
if (it == sets.cend()) {
|
||||
if (it == sets.cend()) {
|
||||
it = sets.emplace(
|
||||
it = sets.emplace(
|
||||
Data::Stickers::CloudRecentSetId,
|
||||
std::make_unique<Data::StickersSet>(
|
||||
&session().data(),
|
||||
Data::Stickers::CloudRecentSetId,
|
||||
std::make_unique<Data::StickersSet>(
|
||||
&session().data(),
|
||||
Data::Stickers::CloudRecentSetId,
|
||||
uint64(0), // accessHash
|
||||
uint64(0), // hash
|
||||
tr::lng_recent_stickers(tr::now),
|
||||
QString(),
|
||||
0, // count
|
||||
SetFlag::Special,
|
||||
TimeId(0))).first;
|
||||
} else {
|
||||
it->second->title = tr::lng_recent_stickers(tr::now);
|
||||
}
|
||||
uint64(0), // accessHash
|
||||
uint64(0), // hash
|
||||
tr::lng_recent_stickers(tr::now),
|
||||
QString(),
|
||||
0, // count
|
||||
SetFlag::Special,
|
||||
TimeId(0))).first;
|
||||
} else {
|
||||
it->second->title = tr::lng_recent_stickers(tr::now);
|
||||
}
|
||||
const auto set = it->second.get();
|
||||
auto removedFromEmoji = std::vector<not_null<EmojiPtr>>();
|
||||
|
||||
@@ -438,8 +438,6 @@ void ApiWrap::startExport(
|
||||
}
|
||||
if (_settings->types & Settings::Type::AnyChatsMask) {
|
||||
_startProcess->steps.push_back(Step::SplitRanges);
|
||||
}
|
||||
if (_settings->types & Settings::Type::AnyChatsMask) {
|
||||
_startProcess->steps.push_back(Step::DialogsCount);
|
||||
}
|
||||
if (_settings->types & Settings::Type::GroupsChannelsMask) {
|
||||
|
||||
@@ -869,7 +869,7 @@ HistoryWidget::HistoryWidget(
|
||||
}
|
||||
if (flags & PeerUpdateFlag::FullInfo) {
|
||||
fullInfoUpdated();
|
||||
if (const auto channel = _peer ? _peer->asChannel() : nullptr) {
|
||||
if (const auto channel = _peer->asChannel()) {
|
||||
if (channel->allowedReactions().paidEnabled) {
|
||||
session().credits().load();
|
||||
}
|
||||
@@ -3440,7 +3440,7 @@ void HistoryWidget::messagesFailed(const MTP::Error &error, int requestId) {
|
||||
closeCurrent();
|
||||
const auto wasAccount = not_null(&was->account());
|
||||
if (const auto primary = Core::App().windowFor(wasAccount)) {
|
||||
primary->showToast((was && was->isMegagroup())
|
||||
primary->showToast(was->isMegagroup()
|
||||
? tr::lng_group_not_accessible(tr::now)
|
||||
: tr::lng_channel_not_accessible(tr::now));
|
||||
}
|
||||
@@ -5111,7 +5111,7 @@ bool HistoryWidget::updateCmdStartShown() {
|
||||
const auto textSmall = _fieldCharsCountManager.count() > kSmallMenuAfter;
|
||||
const auto textChanged = _botMenu.button
|
||||
&& ((_botMenu.text != bot->botInfo->botMenuButtonText)
|
||||
|| (_botMenu.small != textSmall));
|
||||
|| (_botMenu.small != textSmall));
|
||||
if (textChanged) {
|
||||
_botMenu.text = bot->botInfo->botMenuButtonText;
|
||||
if ((_botMenu.small = textSmall)) {
|
||||
|
||||
@@ -381,7 +381,7 @@ void FieldHeader::init() {
|
||||
return;
|
||||
}
|
||||
const auto e = static_cast<QMouseEvent*>(event.get());
|
||||
const auto pos = e ? e->pos() : mapFromGlobal(QCursor::pos());
|
||||
const auto pos = e->pos();
|
||||
const auto inPreviewRect = _clickableRect.contains(pos);
|
||||
const auto inPhotoEdit = _shownMessageHasPreview
|
||||
&& _photoEditAllowed
|
||||
@@ -1191,9 +1191,7 @@ void ComposeControls::showStarted() {
|
||||
if (_attachBotsMenu) {
|
||||
_attachBotsMenu->hideFast();
|
||||
}
|
||||
if (_voiceRecordBar) {
|
||||
_voiceRecordBar->hideFast();
|
||||
}
|
||||
_voiceRecordBar->hideFast();
|
||||
if (_autocomplete) {
|
||||
_autocomplete->hideFast();
|
||||
}
|
||||
@@ -1213,9 +1211,7 @@ void ComposeControls::showFinished() {
|
||||
if (_attachBotsMenu) {
|
||||
_attachBotsMenu->hideFast();
|
||||
}
|
||||
if (_voiceRecordBar) {
|
||||
_voiceRecordBar->hideFast();
|
||||
}
|
||||
_voiceRecordBar->hideFast();
|
||||
if (_autocomplete) {
|
||||
_autocomplete->hideFast();
|
||||
}
|
||||
|
||||
@@ -474,7 +474,7 @@ TTLButton::TTLButton(
|
||||
) | rpl::start_with_next([=](bool toHide) {
|
||||
const auto isFirstTooltip
|
||||
= !Core::App().settings().ttlVoiceClickTooltipHidden();
|
||||
if (isFirstTooltip || (!isFirstTooltip && toHide)) {
|
||||
if (isFirstTooltip || toHide) {
|
||||
_tooltip->toggleAnimated(!toHide);
|
||||
}
|
||||
}, _tooltip->lifetime());
|
||||
|
||||
@@ -916,8 +916,9 @@ void RepliesWidget::setupSwipeReply() {
|
||||
result.callback = [=, itemId = view->data()->fullId()] {
|
||||
const auto still = show->session().data().message(itemId);
|
||||
const auto view = _inner->viewByPosition(still->position());
|
||||
const auto selected = view->selectedQuote(
|
||||
_inner->getSelectedTextRange(still));
|
||||
const auto selected = view
|
||||
? view->selectedQuote(_inner->getSelectedTextRange(still))
|
||||
: SelectedQuote();
|
||||
const auto replyToItemId = (selected.item
|
||||
? selected.item
|
||||
: still)->fullId();
|
||||
|
||||
@@ -91,7 +91,8 @@ ThemeDocument::ThemeDocument(
|
||||
: File(parent, parent->data())
|
||||
, _data(document)
|
||||
, _serviceWidth(serviceWidth) {
|
||||
Expects(params.has_value() || _data->hasThumbnail() || _data->isTheme());
|
||||
Expects(params.has_value()
|
||||
|| (_data && (_data->hasThumbnail() || _data->isTheme())));
|
||||
|
||||
if (params) {
|
||||
_background = params->backgroundColors();
|
||||
|
||||
@@ -1232,8 +1232,9 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
|
||||
.position = QPoint(
|
||||
inner.x() + (inner.width() - _openButton.maxWidth()) / 2,
|
||||
end + st::historyPageButtonPadding.top()),
|
||||
.availableWidth = paintw,
|
||||
.availableWidth = inner.width(),
|
||||
.now = context.now,
|
||||
.elisionLines = 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ void InnerWidget::fill() {
|
||||
),
|
||||
rpl::duplicate(availableBalanceValue),
|
||||
rpl::duplicate(dateValue),
|
||||
std::move(dateValue) | rpl::map([=](const QDateTime &dt) {
|
||||
rpl::duplicate(dateValue) | rpl::map([=](const QDateTime &dt) {
|
||||
return !dt.isNull() || (!_state.isWithdrawalEnabled);
|
||||
}),
|
||||
rpl::duplicate(availableBalanceValue) | rpl::map([=](uint64 v) {
|
||||
|
||||
@@ -1366,7 +1366,7 @@ void WebViewInstance::show(ShowArgs &&args) {
|
||||
: attached->inMainMenu
|
||||
? Button::RemoveFromMainMenu
|
||||
: Button::RemoveFromMenu);
|
||||
const auto allowClipboardRead = v::is<WebViewSourceAttachMenu>(_source)
|
||||
const auto allowClipboardRead = v::is<WebViewSourceMainMenu>(_source)
|
||||
|| v::is<WebViewSourceAttachMenu>(_source)
|
||||
|| (attached != end(bots)
|
||||
&& (attached->inAttachMenu || attached->inMainMenu));
|
||||
|
||||
@@ -806,10 +806,9 @@ void Mixer::externalSoundProgress(const AudioMsgId &audio) {
|
||||
}
|
||||
|
||||
bool Mixer::checkCurrentALError(AudioMsgId::Type type) {
|
||||
if (!Audio::PlaybackErrorHappened()) return true;
|
||||
|
||||
const auto data = trackForType(type);
|
||||
if (!data) {
|
||||
if (!Audio::PlaybackErrorHappened()) {
|
||||
return true;
|
||||
} else if (const auto data = trackForType(type)) {
|
||||
setStoppedState(data, State::StoppedAtError);
|
||||
onError(data->state.id);
|
||||
}
|
||||
|
||||
@@ -1234,7 +1234,8 @@ void OverlayWidget::documentUpdated(not_null<DocumentData*> document) {
|
||||
if (_document != document) {
|
||||
return;
|
||||
} else if (documentBubbleShown()) {
|
||||
if ((_document->loading() && _docCancel->isHidden()) || (!_document->loading() && !_docCancel->isHidden())) {
|
||||
if ((_document->loading() && _docCancel->isHidden())
|
||||
|| (!_document->loading() && !_docCancel->isHidden())) {
|
||||
updateControls();
|
||||
} else if (_document->loading()) {
|
||||
updateDocSize();
|
||||
|
||||
@@ -399,12 +399,10 @@ int32 Session::getState() const {
|
||||
|
||||
if (_private) {
|
||||
const auto s = _private->getState();
|
||||
if (s == ConnectedState) {
|
||||
if (s == ConnectedState
|
||||
|| s == ConnectingState
|
||||
|| s == DisconnectedState) {
|
||||
return s;
|
||||
} else if (s == ConnectingState || s == DisconnectedState) {
|
||||
if (result < 0) {
|
||||
return s;
|
||||
}
|
||||
} else if (s < 0) {
|
||||
if (result < 0 && s > result) {
|
||||
result = s;
|
||||
|
||||
@@ -375,7 +375,9 @@ bool Get(
|
||||
dialog.setFileMode(QFileDialog::AnyFile);
|
||||
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
}
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
dialog.show();
|
||||
#endif // Qt < 6.0.0
|
||||
|
||||
auto realLastPath = [=] {
|
||||
// If we're given some non empty path containing a folder - use it.
|
||||
|
||||
@@ -774,7 +774,7 @@ void TopBarUser::updateTitle(
|
||||
{ EntityType::CustomEmoji, 0, 1, entityEmojiData },
|
||||
Ui::Text::Link(text, linkIndex).entities.front(),
|
||||
};
|
||||
auto title = (setId != coloredId)
|
||||
auto title = (setId == coloredId)
|
||||
? tr::lng_premium_emoji_status_title_colored(
|
||||
tr::now,
|
||||
lt_user,
|
||||
|
||||
@@ -1059,9 +1059,7 @@ bool ReadSetting(
|
||||
auto id = Ui::Emoji::IdFromOldKey(static_cast<uint64>(i.key()));
|
||||
if (!id.isEmpty()) {
|
||||
auto index = Ui::Emoji::ColorIndexFromOldKey(i.value());
|
||||
if (index >= 0) {
|
||||
variants.insert(id, index);
|
||||
}
|
||||
variants.insert(id, index);
|
||||
}
|
||||
}
|
||||
Core::App().settings().setLegacyEmojiVariants(std::move(variants));
|
||||
|
||||
@@ -117,8 +117,7 @@ void ShowOrPremiumBox(
|
||||
tr::lng_lastseen_shown_toast(tr::now),
|
||||
&st::showOrIconLastSeen,
|
||||
}
|
||||
: (type == ShowOrPremium::ReadTime)
|
||||
? Skin{
|
||||
: Skin{
|
||||
tr::lng_readtime_show_title(),
|
||||
tr::lng_readtime_show_about(
|
||||
lt_user,
|
||||
@@ -134,8 +133,7 @@ void ShowOrPremiumBox(
|
||||
tr::lng_readtime_premium_button(),
|
||||
tr::lng_readtime_shown_toast(tr::now),
|
||||
&st::showOrIconReadTime,
|
||||
}
|
||||
: Skin();
|
||||
};
|
||||
|
||||
box->setStyle(st::showOrBox);
|
||||
box->setWidth(st::boxWideWidth);
|
||||
|
||||
@@ -394,6 +394,11 @@ Panel::Panel(Args &&args)
|
||||
sendContentSafeArea();
|
||||
}, _widget->lifetime());
|
||||
|
||||
_widget->fullScreenValue(
|
||||
) | rpl::start_with_next([=](bool fullscreen) {
|
||||
_fullscreen = fullscreen;
|
||||
}, _widget->lifetime());
|
||||
|
||||
_widget->closeRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
if (_closeNeedConfirmation) {
|
||||
@@ -783,6 +788,8 @@ void Panel::createWebviewBottom() {
|
||||
_webviewBottom.get(),
|
||||
_bottomText.value(),
|
||||
st::paymentsWebviewBottom);
|
||||
_webviewBottomLabel = label;
|
||||
|
||||
const auto height = padding.top()
|
||||
+ label->heightNoMargins()
|
||||
+ padding.bottom();
|
||||
@@ -840,6 +847,7 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) {
|
||||
}
|
||||
}
|
||||
if (_webviewBottom.get() == bottom) {
|
||||
_webviewBottomLabel = nullptr;
|
||||
_webviewBottom = nullptr;
|
||||
_secondaryButton = nullptr;
|
||||
_mainButton = nullptr;
|
||||
@@ -849,6 +857,12 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) {
|
||||
if (!raw->widget()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_MAC
|
||||
_widget->allowChildFullScreenControls(
|
||||
!raw->widget()->inherits("QWindowContainer"));
|
||||
#endif // !Q_OS_WIN && !Q_OS_MAC
|
||||
|
||||
QObject::connect(raw->widget(), &QObject::destroyed, [=] {
|
||||
const auto parent = _webviewParent.data();
|
||||
if (!_webview
|
||||
@@ -1596,20 +1610,45 @@ void Panel::processHeaderColor(const QJsonObject &args) {
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::overrideBodyColor(std::optional<QColor> color) {
|
||||
_widget->overrideBodyColor(color);
|
||||
const auto raw = _webviewBottomLabel.data();
|
||||
if (!raw) {
|
||||
return;
|
||||
} else if (!color) {
|
||||
raw->setTextColorOverride(std::nullopt);
|
||||
return;
|
||||
}
|
||||
const auto contrast = 2.5;
|
||||
const auto luminance = 0.2126 * color->redF()
|
||||
+ 0.7152 * color->greenF()
|
||||
+ 0.0722 * color->blueF();
|
||||
const auto textColor = (luminance > 0.5)
|
||||
? QColor(0, 0, 0)
|
||||
: QColor(255, 255, 255);
|
||||
const auto textLuminance = (luminance > 0.5) ? 0 : 1;
|
||||
const auto adaptiveOpacity = (luminance - textLuminance + contrast)
|
||||
/ contrast;
|
||||
const auto opacity = std::clamp(adaptiveOpacity, 0.5, 0.64);
|
||||
auto buttonColor = textColor;
|
||||
buttonColor.setAlphaF(opacity);
|
||||
raw->setTextColorOverride(buttonColor);
|
||||
}
|
||||
|
||||
void Panel::processBackgroundColor(const QJsonObject &args) {
|
||||
_bodyColorReceived = true;
|
||||
if (const auto color = ParseColor(args["color"].toString())) {
|
||||
_widget->overrideBodyColor(color);
|
||||
overrideBodyColor(*color);
|
||||
_bodyColorLifetime.destroy();
|
||||
} else if (const auto color = LookupNamedColor(
|
||||
args["color_key"].toString())) {
|
||||
_widget->overrideBodyColor((*color)->c);
|
||||
overrideBodyColor((*color)->c);
|
||||
_bodyColorLifetime = style::PaletteChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
_widget->overrideBodyColor((*color)->c);
|
||||
overrideBodyColor((*color)->c);
|
||||
});
|
||||
} else {
|
||||
_widget->overrideBodyColor(std::nullopt);
|
||||
overrideBodyColor(std::nullopt);
|
||||
_bodyColorLifetime.destroy();
|
||||
}
|
||||
if (const auto raw = _bottomButtonsBg.get()) {
|
||||
@@ -1923,7 +1962,7 @@ void Panel::updateColorOverrides(const Webview::ThemeParams ¶ms) {
|
||||
_widget->overrideTitleColor(params.titleBg);
|
||||
}
|
||||
if (!_bodyColorReceived && params.bodyBg.alpha() == 255) {
|
||||
_widget->overrideBodyColor(params.bodyBg);
|
||||
overrideBodyColor(params.bodyBg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ class QJsonObject;
|
||||
class QJsonValue;
|
||||
|
||||
namespace Ui {
|
||||
class FlatLabel;
|
||||
class BoxContent;
|
||||
class RpWidget;
|
||||
class SeparatePanel;
|
||||
@@ -196,6 +197,7 @@ private:
|
||||
void sendFullScreen();
|
||||
|
||||
void updateColorOverrides(const Webview::ThemeParams ¶ms);
|
||||
void overrideBodyColor(std::optional<QColor> color);
|
||||
|
||||
using EventData = std::variant<QString, QJsonObject>;
|
||||
void postEvent(const QString &event);
|
||||
@@ -216,6 +218,7 @@ private:
|
||||
std::unique_ptr<SeparatePanel> _widget;
|
||||
std::unique_ptr<WebviewWithLifetime> _webview;
|
||||
std::unique_ptr<RpWidget> _webviewBottom;
|
||||
QPointer<FlatLabel> _webviewBottomLabel;
|
||||
rpl::variable<QString> _bottomText;
|
||||
QPointer<RpWidget> _webviewParent;
|
||||
std::unique_ptr<RpWidget> _bottomButtonsBg;
|
||||
|
||||
@@ -180,6 +180,9 @@ void ChatsFiltersTabsReorder::mousePress(
|
||||
}
|
||||
}
|
||||
cancelCurrent();
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
_currentWidget = widget->section;
|
||||
_currentShiftedWidget = widget;
|
||||
_currentStart = globalPosition.x();
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace {
|
||||
struct State final {
|
||||
Ui::Animations::Simple animation;
|
||||
std::optional<FilterId> lastFilterId = std::nullopt;
|
||||
rpl::lifetime unreadLifetime;
|
||||
rpl::lifetime rebuildLifetime;
|
||||
base::unique_qptr<Ui::PopupMenu> menu;
|
||||
|
||||
Api::RemoveComplexChatFilter removeApi;
|
||||
@@ -293,6 +293,7 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
|
||||
if ((list.size() <= 1 && !slider->width()) || state->ignoreRefresh) {
|
||||
return;
|
||||
}
|
||||
state->rebuildLifetime.destroy();
|
||||
auto sections = ranges::views::all(
|
||||
list
|
||||
) | ranges::views::transform([](const Data::ChatFilter &filter) {
|
||||
@@ -312,7 +313,7 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
|
||||
: premiumFrom);
|
||||
slider->lockedClicked() | rpl::start_with_next([=] {
|
||||
controller->show(Box(FiltersLimitBox, session, std::nullopt));
|
||||
}, slider->lifetime());
|
||||
}, state->rebuildLifetime);
|
||||
if (state->reorder) {
|
||||
state->reorder->cancel();
|
||||
state->reorder->clearPinnedIntervals();
|
||||
@@ -326,7 +327,6 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
|
||||
}
|
||||
if (trackActiveFilterAndUnreadAndReorder) {
|
||||
auto includeMuted = Data::IncludeMutedCounterFoldersValue();
|
||||
state->unreadLifetime.destroy();
|
||||
for (auto i = 0; i < list.size(); i++) {
|
||||
rpl::combine(
|
||||
Data::UnreadStateValue(session, list[i].id()),
|
||||
@@ -340,7 +340,7 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
|
||||
const auto isMuted = includeMuted && (count == muted);
|
||||
slider->setUnreadCount(i, count, isMuted);
|
||||
slider->fitWidthToSections();
|
||||
}, state->unreadLifetime);
|
||||
}, state->rebuildLifetime);
|
||||
}
|
||||
}
|
||||
[&] {
|
||||
@@ -379,7 +379,7 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
|
||||
}
|
||||
}
|
||||
state->reorder->finishReordering();
|
||||
}, slider->lifetime());
|
||||
}, state->rebuildLifetime);
|
||||
}
|
||||
rpl::single(-1) | rpl::then(
|
||||
slider->sectionActivated()
|
||||
@@ -394,7 +394,7 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
|
||||
scrollToIndex(index, anim::type::normal);
|
||||
}
|
||||
applyFilter(filter);
|
||||
}, wrap->lifetime());
|
||||
}, state->rebuildLifetime);
|
||||
slider->contextMenuRequested() | rpl::start_with_next([=](int index) {
|
||||
if (trackActiveFilterAndUnreadAndReorder) {
|
||||
ShowMenu(wrap, controller, state, index);
|
||||
@@ -406,7 +406,7 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
|
||||
slider->activeSection(),
|
||||
[=](int i) { slider->setActiveSection(i); });
|
||||
}
|
||||
}, slider->lifetime());
|
||||
}, state->rebuildLifetime);
|
||||
wrap->toggle((list.size() > 1), anim::type::instant);
|
||||
|
||||
if (state->reorder) {
|
||||
|
||||
@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/filter_icons.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/vertical_layout_reorder.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/ui_utility.h"
|
||||
@@ -356,18 +357,11 @@ void FiltersMenu::showMenu(QPoint position, FilterId id) {
|
||||
_popupMenu = base::make_unique_q<Ui::PopupMenu>(
|
||||
i->second.get(),
|
||||
st::popupMenuWithIcons);
|
||||
const auto addAction = Window::PeerMenuCallback([&](
|
||||
Window::PeerMenuCallback::Args args) {
|
||||
return _popupMenu->addAction(
|
||||
args.text,
|
||||
crl::guard(&_outer, std::move(args.handler)),
|
||||
args.icon);
|
||||
});
|
||||
|
||||
const auto addAction = Ui::Menu::CreateAddActionCallback(_popupMenu);
|
||||
if (id) {
|
||||
addAction(
|
||||
tr::lng_filters_context_edit(tr::now),
|
||||
[=] { EditExistingFilter(_session, id); },
|
||||
crl::guard(&_outer, [=] { EditExistingFilter(_session, id); }),
|
||||
&st::menuIconEdit);
|
||||
|
||||
auto filteredChats = [=] {
|
||||
@@ -380,9 +374,9 @@ void FiltersMenu::showMenu(QPoint position, FilterId id) {
|
||||
|
||||
addAction({
|
||||
.text = tr::lng_filters_context_remove(tr::now),
|
||||
.handler = [=, this] {
|
||||
.handler = crl::guard(&_outer, [=, this] {
|
||||
_removeApi.request(Ui::MakeWeak(&_outer), _session, id);
|
||||
},
|
||||
}),
|
||||
.icon = &st::menuIconDeleteAttention,
|
||||
.isAttention = true,
|
||||
});
|
||||
@@ -401,7 +395,7 @@ void FiltersMenu::showMenu(QPoint position, FilterId id) {
|
||||
|
||||
addAction(
|
||||
tr::lng_filters_setup_menu(tr::now),
|
||||
[=] { openFiltersSettings(); },
|
||||
crl::guard(&_outer, [=] { openFiltersSettings(); }),
|
||||
&st::menuIconEdit);
|
||||
}
|
||||
if (_popupMenu->empty()) {
|
||||
|
||||
@@ -2157,6 +2157,163 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||
not_null<Controller*> controller;
|
||||
base::unique_qptr<Ui::PopupMenu> menu;
|
||||
};
|
||||
|
||||
const auto applyFilter = [=](not_null<PeerListBox*> box, FilterId id) {
|
||||
box->scrollToY(0);
|
||||
auto &filters = session->data().chatsFilters();
|
||||
const auto &list = filters.list();
|
||||
if (list.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
const auto pinnedList = [&](
|
||||
not_null<Dialogs::MainList*> list,
|
||||
bool foundSelf) {
|
||||
const auto pinned = list->pinned()->order();
|
||||
auto peers = std::vector<not_null<PeerData*>>();
|
||||
peers.reserve(pinned.size());
|
||||
for (const auto &pin : pinned) {
|
||||
if (!foundSelf && pin.peer()->isSelf()) {
|
||||
peers.insert(peers.begin(), pin.peer());
|
||||
foundSelf = true;
|
||||
} else {
|
||||
peers.push_back(pin.peer());
|
||||
}
|
||||
}
|
||||
if (!foundSelf) {
|
||||
peers.insert(peers.begin(), session->user());
|
||||
}
|
||||
return peers;
|
||||
};
|
||||
const auto folder = session->data().folderLoaded(
|
||||
Data::Folder::kId);
|
||||
const auto pinned = pinnedList(
|
||||
id
|
||||
? filters.chatsList(id)
|
||||
: session->data().chatsList(nullptr),
|
||||
!!id);
|
||||
const auto pinnedInFolder = (!id && folder)
|
||||
? pinnedList(folder->chatsList(), true)
|
||||
: std::vector<not_null<PeerData*>>();
|
||||
box->peerListSortRows([&](
|
||||
const PeerListRow &r1,
|
||||
const PeerListRow &r2) {
|
||||
{ // Pinned to top.
|
||||
auto it1 = pinned.end();
|
||||
auto it2 = pinned.end();
|
||||
for (auto it = pinned.begin(); it != pinned.end(); ++it) {
|
||||
if ((*it) == r1.peer()) {
|
||||
it1 = it;
|
||||
}
|
||||
if ((*it) == r2.peer()) {
|
||||
it2 = it;
|
||||
}
|
||||
if (it1 != pinned.end() && it2 != pinned.end()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (it1 == pinned.end() && it2 != pinned.end()) {
|
||||
return false;
|
||||
} else if (it2 == pinned.end() && it1 != pinned.end()) {
|
||||
return true;
|
||||
} else if (it1 != pinned.end() && it2 != pinned.end()) {
|
||||
return it1 < it2;
|
||||
}
|
||||
}
|
||||
{ // Pinned to bottom.
|
||||
const auto &indexed = session->data().contactsNoChatsList();
|
||||
auto it1 = indexed->end();
|
||||
auto it2 = indexed->end();
|
||||
for (auto it = indexed->begin(); it != indexed->end(); ++it) {
|
||||
if (it->get()->key().peer() == r1.peer()) {
|
||||
it1 = it;
|
||||
}
|
||||
if (it->get()->key().peer() == r2.peer()) {
|
||||
it2 = it;
|
||||
}
|
||||
if (it1 != indexed->end() && it2 != indexed->end()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (it1 == indexed->end() && it2 != indexed->end()) {
|
||||
return true;
|
||||
} else if (it2 == indexed->end() && it1 != indexed->end()) {
|
||||
return false;
|
||||
} else if (it1 != indexed->end() && it2 != indexed->end()) {
|
||||
return it1 > it2;
|
||||
}
|
||||
}
|
||||
if (folder) {
|
||||
const auto pinned1 = ranges::find(pinnedInFolder, r1.peer());
|
||||
const auto pinned2 = ranges::find(pinnedInFolder, r2.peer());
|
||||
const auto isPinned1 = pinned1 != pinnedInFolder.end();
|
||||
const auto isPinned2 = pinned2 != pinnedInFolder.end();
|
||||
if (isPinned1 && isPinned2) {
|
||||
return pinned1 < pinned2;
|
||||
}
|
||||
|
||||
const auto &indexed = folder->chatsList()->indexed();
|
||||
auto it1 = indexed->end();
|
||||
auto it2 = indexed->end();
|
||||
for (auto it = indexed->begin(); it != indexed->end(); ++it) {
|
||||
if (it->get()->key().peer() == r1.peer()) {
|
||||
it1 = it;
|
||||
}
|
||||
if (it->get()->key().peer() == r2.peer()) {
|
||||
it2 = it;
|
||||
}
|
||||
if (it1 != indexed->end() && it2 != indexed->end()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto isFoldered1 = it1 != indexed->end();
|
||||
const auto isFoldered2 = it2 != indexed->end();
|
||||
if (isPinned1 && !isPinned2) {
|
||||
return isFoldered2;
|
||||
}
|
||||
if (isPinned2 && !isPinned1) {
|
||||
return !isFoldered1;
|
||||
}
|
||||
if (!isPinned1 && !isPinned2) {
|
||||
if (!isFoldered1 && isFoldered2) {
|
||||
return true;
|
||||
} else if (!isFoldered2 && isFoldered1) {
|
||||
return false;
|
||||
} else if (isFoldered1 && isFoldered2) {
|
||||
return it1 < it2;
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto history1 = session->data().history(r1.peer());
|
||||
const auto history2 = session->data().history(r2.peer());
|
||||
const auto date1 = history1->lastMessage()
|
||||
? history1->lastMessage()->date()
|
||||
: TimeId(0);
|
||||
const auto date2 = history2->lastMessage()
|
||||
? history2->lastMessage()->date()
|
||||
: TimeId(0);
|
||||
return date1 > date2;
|
||||
});
|
||||
const auto filter = ranges::find(
|
||||
list,
|
||||
id,
|
||||
&Data::ChatFilter::id);
|
||||
if (filter == list.end()) {
|
||||
return;
|
||||
}
|
||||
box->peerListPartitionRows([&](const PeerListRow &row) {
|
||||
const auto rowPtr = const_cast<PeerListRow*>(&row);
|
||||
if (!filter->id()) {
|
||||
box->peerListSetRowHidden(rowPtr, false);
|
||||
} else {
|
||||
const auto result = filter->contains(
|
||||
session->data().history(row.peer()));
|
||||
box->peerListSetRowHidden(rowPtr, !result);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
box->peerListRefreshRows();
|
||||
};
|
||||
|
||||
const auto state = [&] {
|
||||
auto controller = std::make_unique<Controller>(session);
|
||||
const auto controllerRaw = controller.get();
|
||||
@@ -2164,80 +2321,10 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||
controllerRaw->setSearchNoResultsText(
|
||||
tr::lng_bot_chats_not_found(tr::now));
|
||||
box->setSpecialTabMode(true);
|
||||
auto applyFilter = [=](FilterId id) {
|
||||
box->scrollToY(0);
|
||||
auto &filters = session->data().chatsFilters();
|
||||
const auto &list = filters.list();
|
||||
if (list.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
const auto pinned = [&] {
|
||||
const auto &list = id
|
||||
? filters.chatsList(id)
|
||||
: session->data().chatsList(nullptr);
|
||||
const auto pinned = list->pinned()->order();
|
||||
auto peers = std::vector<not_null<PeerData*>>();
|
||||
peers.reserve(pinned.size());
|
||||
auto foundSelf = !!id;
|
||||
for (const auto &pin : pinned) {
|
||||
if (!foundSelf && pin.peer()->isSelf()) {
|
||||
peers.insert(peers.begin(), pin.peer());
|
||||
foundSelf = true;
|
||||
} else {
|
||||
peers.push_back(pin.peer());
|
||||
}
|
||||
}
|
||||
if (!foundSelf) {
|
||||
peers.insert(peers.begin(), session->user());
|
||||
}
|
||||
return peers;
|
||||
}();
|
||||
box->peerListSortRows([&](
|
||||
const PeerListRow &r1,
|
||||
const PeerListRow &r2) {
|
||||
const auto it1 = ranges::find(pinned, r1.peer());
|
||||
const auto it2 = ranges::find(pinned, r2.peer());
|
||||
if (it1 == pinned.end() && it2 != pinned.end()) {
|
||||
return false;
|
||||
} else if (it2 == pinned.end() && it1 != pinned.end()) {
|
||||
return true;
|
||||
} else if (it1 != pinned.end() && it2 != pinned.end()) {
|
||||
return it1 < it2;
|
||||
}
|
||||
const auto history1 = session->data().history(r1.peer());
|
||||
const auto history2 = session->data().history(r2.peer());
|
||||
const auto date1 = history1->lastMessage()
|
||||
? history1->lastMessage()->date()
|
||||
: TimeId(0);
|
||||
const auto date2 = history2->lastMessage()
|
||||
? history2->lastMessage()->date()
|
||||
: TimeId(0);
|
||||
return date1 > date2;
|
||||
});
|
||||
const auto filter = ranges::find(
|
||||
list,
|
||||
id,
|
||||
&Data::ChatFilter::id);
|
||||
if (filter == list.end()) {
|
||||
return;
|
||||
}
|
||||
box->peerListPartitionRows([&](const PeerListRow &row) {
|
||||
const auto rowPtr = const_cast<PeerListRow*>(&row);
|
||||
if (!filter->id()) {
|
||||
box->peerListSetRowHidden(rowPtr, false);
|
||||
} else {
|
||||
const auto result = filter->contains(
|
||||
session->data().history(row.peer()));
|
||||
box->peerListSetRowHidden(rowPtr, !result);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
box->peerListRefreshRows();
|
||||
};
|
||||
const auto chatsFilters = Ui::AddChatFiltersTabsStrip(
|
||||
box,
|
||||
session,
|
||||
std::move(applyFilter));
|
||||
[=](FilterId id) { applyFilter(box, id); });
|
||||
chatsFilters->lower();
|
||||
chatsFilters->heightValue() | rpl::start_with_next([box](int h) {
|
||||
box->setAddedTopScrollSkip(h);
|
||||
|
||||
@@ -572,7 +572,8 @@ void SessionNavigation::showPeerByLinkResolved(
|
||||
const auto controller = parentController();
|
||||
if (const auto forum = peer->forum()) {
|
||||
if (controller->windowId().hasChatsList()
|
||||
&& !controller->adaptive().isOneColumn()) {
|
||||
&& !controller->adaptive().isOneColumn()
|
||||
&& controller->shownForum().current() != forum) {
|
||||
controller->showForum(forum);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +279,9 @@ echo Done!
|
||||
if %BuildUWP% neq 0 (
|
||||
cd "%HomePath%"
|
||||
|
||||
mkdir "%ReleasePath%\AppX\modules\%Platform%\d3d"
|
||||
if %BuildARM% equ 0 (
|
||||
mkdir "%ReleasePath%\AppX\modules\%Platform%\d3d"
|
||||
)
|
||||
xcopy "Resources\uwp\AppX\*" "%ReleasePath%\AppX\" /E
|
||||
set "ResourcePath=%ReleasePath%\AppX\AppxManifest.xml"
|
||||
call :repl "Argument= (ProcessorArchitecture=)"ARCHITECTURE"/ $1"%Platform%"" "Filename=!ResourcePath!" || goto error
|
||||
@@ -288,7 +290,9 @@ if %BuildUWP% neq 0 (
|
||||
|
||||
xcopy "%ReleasePath%\%BinaryName%.exe" "%ReleasePath%\AppX\"
|
||||
xcopy "%ReleasePath%\StartupTask.exe" "%ReleasePath%\AppX\"
|
||||
xcopy "%ReleasePath%\modules\%Platform%\d3d\d3dcompiler_47.dll" "%ReleasePath%\AppX\modules\%Platform%\d3d\"
|
||||
if %BuildARM% equ 0 (
|
||||
xcopy "%ReleasePath%\modules\%Platform%\d3d\d3dcompiler_47.dll" "%ReleasePath%\AppX\modules\%Platform%\d3d\"
|
||||
)
|
||||
|
||||
MakeAppx.exe pack /d "%ReleasePath%\AppX" /l /p ..\out\Release\%BinaryName%.%Platform%.appx
|
||||
if %errorlevel% neq 0 goto error
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
AppVersion 5008000
|
||||
AppVersion 5008002
|
||||
AppVersionStrMajor 5.8
|
||||
AppVersionStrSmall 5.8
|
||||
AppVersionStr 5.8.0
|
||||
AppVersionStrSmall 5.8.2
|
||||
AppVersionStr 5.8.2
|
||||
BetaChannel 0
|
||||
AlphaVersion 0
|
||||
AppVersionOriginal 5.8
|
||||
AppVersionOriginal 5.8.2
|
||||
|
||||
Submodule Telegram/lib_ui updated: 46c18e2c7e...f48bc506d1
Submodule Telegram/lib_webview updated: 095babf234...3dc2f15cd4
@@ -1,3 +1,15 @@
|
||||
5.8.2 (19.11.24)
|
||||
|
||||
- Improve bottom label color in mini apps.
|
||||
- Fix some fullscreen issues in mini apps.
|
||||
- Fix miss-order of archived chats in forward box.
|
||||
- Fix file dialog for Windows on ARM.
|
||||
- Fix emoji status pack view from profile regression.
|
||||
|
||||
5.8.1 (17.11.24)
|
||||
|
||||
- Fix several possible crashes.
|
||||
|
||||
5.8 (17.11.24)
|
||||
|
||||
- Updates in Mini Apps platform.
|
||||
|
||||
Reference in New Issue
Block a user