mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-10 00:29:31 +00:00
Alpha 1.0.10: unpinning converted chat on demand.
If we want to pin a chat and we have reached the limit we now check for a deactivated (converted to supergroup) chat that is pinned and is not in the chats list and just silently unpin it if it is found. Also possible UB fix for a waveform encoding and decoding.
This commit is contained in:
parent
8d354382a4
commit
7adfe93a8d
@ -9,7 +9,7 @@
|
|||||||
<Identity Name="TelegramDesktop"
|
<Identity Name="TelegramDesktop"
|
||||||
ProcessorArchitecture="x64"
|
ProcessorArchitecture="x64"
|
||||||
Publisher="CN=Telegram Messenger LLP, O=Telegram Messenger LLP, L=London, C=GB"
|
Publisher="CN=Telegram Messenger LLP, O=Telegram Messenger LLP, L=London, C=GB"
|
||||||
Version="1.0.9.0" />
|
Version="1.0.10.0" />
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Telegram Desktop</DisplayName>
|
<DisplayName>Telegram Desktop</DisplayName>
|
||||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||||
|
@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
|||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 1,0,9,0
|
FILEVERSION 1,0,10,0
|
||||||
PRODUCTVERSION 1,0,9,0
|
PRODUCTVERSION 1,0,10,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
@ -52,10 +52,10 @@ BEGIN
|
|||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||||
VALUE "FileDescription", "Telegram Desktop official messenger"
|
VALUE "FileDescription", "Telegram Desktop official messenger"
|
||||||
VALUE "FileVersion", "1.0.9.0"
|
VALUE "FileVersion", "1.0.10.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
|
||||||
VALUE "ProductName", "Telegram Desktop"
|
VALUE "ProductName", "Telegram Desktop"
|
||||||
VALUE "ProductVersion", "1.0.9.0"
|
VALUE "ProductVersion", "1.0.10.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
|||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 1,0,9,0
|
FILEVERSION 1,0,10,0
|
||||||
PRODUCTVERSION 1,0,9,0
|
PRODUCTVERSION 1,0,10,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
@ -43,10 +43,10 @@ BEGIN
|
|||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||||
VALUE "FileVersion", "1.0.9.0"
|
VALUE "FileVersion", "1.0.10.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
|
||||||
VALUE "ProductName", "Telegram Desktop"
|
VALUE "ProductName", "Telegram Desktop"
|
||||||
VALUE "ProductVersion", "1.0.9.0"
|
VALUE "ProductVersion", "1.0.10.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
@ -1069,8 +1069,8 @@ void AppClass::checkMapVersion() {
|
|||||||
if (Local::oldMapVersion() < AppVersion) {
|
if (Local::oldMapVersion() < AppVersion) {
|
||||||
if (Local::oldMapVersion()) {
|
if (Local::oldMapVersion()) {
|
||||||
QString versionFeatures;
|
QString versionFeatures;
|
||||||
if ((cAlphaVersion() || cBetaVersion()) && Local::oldMapVersion() < 1000008) {
|
if ((cAlphaVersion() || cBetaVersion()) && Local::oldMapVersion() < 1000010) {
|
||||||
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Click and drag on waveform to play audio from a chosen moment.");
|
versionFeatures = QString::fromUtf8("\xe2\x80\x94 More different emoticons supported.\n\xe2\x80\x94 Bug fixes and other minor improvements.");
|
||||||
} else if (!(cAlphaVersion() || cBetaVersion()) && Local::oldMapVersion() < 1000005) {
|
} else if (!(cAlphaVersion() || cBetaVersion()) && Local::oldMapVersion() < 1000005) {
|
||||||
versionFeatures = langNewVersionText();
|
versionFeatures = langNewVersionText();
|
||||||
} else {
|
} else {
|
||||||
|
@ -24,7 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||||||
|
|
||||||
#define BETA_VERSION_MACRO (0ULL)
|
#define BETA_VERSION_MACRO (0ULL)
|
||||||
|
|
||||||
constexpr int AppVersion = 1000009;
|
constexpr int AppVersion = 1000010;
|
||||||
constexpr str_const AppVersionStr = "1.0.9";
|
constexpr str_const AppVersionStr = "1.0.10";
|
||||||
constexpr bool AppAlphaVersion = true;
|
constexpr bool AppAlphaVersion = true;
|
||||||
constexpr uint64 AppBetaVersion = BETA_VERSION_MACRO;
|
constexpr uint64 AppBetaVersion = BETA_VERSION_MACRO;
|
||||||
|
@ -109,15 +109,27 @@ void paintRow(Painter &p, const RippleRow *row, History *history, HistoryItem *i
|
|||||||
p.restoreTextPalette();
|
p.restoreTextPalette();
|
||||||
}
|
}
|
||||||
} else if (!item) {
|
} else if (!item) {
|
||||||
|
auto availableWidth = namewidth;
|
||||||
|
if (history->isPinnedDialog()) {
|
||||||
|
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
||||||
|
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
||||||
|
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
||||||
|
}
|
||||||
|
|
||||||
auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService);
|
auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService);
|
||||||
p.setFont(st::dialogsTextFont);
|
p.setFont(st::dialogsTextFont);
|
||||||
if (!history->paintSendAction(p, nameleft, texttop, namewidth, fullWidth, color, ms)) {
|
if (!history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
|
||||||
// Empty history
|
// Empty history
|
||||||
}
|
}
|
||||||
} else if (!item->isEmpty()) {
|
} else if (!item->isEmpty()) {
|
||||||
paintRowDate(p, date, rectForName, active, selected);
|
paintRowDate(p, date, rectForName, active, selected);
|
||||||
|
|
||||||
paintItemCallback(nameleft, namewidth, item);
|
paintItemCallback(nameleft, namewidth, item);
|
||||||
|
} else if (history->isPinnedDialog()) {
|
||||||
|
auto availableWidth = namewidth;
|
||||||
|
auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon));
|
||||||
|
icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth);
|
||||||
|
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
||||||
}
|
}
|
||||||
auto sendStateIcon = ([draft, item, active, selected]() -> const style::icon* {
|
auto sendStateIcon = ([draft, item, active, selected]() -> const style::icon* {
|
||||||
if (draft) {
|
if (draft) {
|
||||||
|
@ -643,17 +643,12 @@ void DialogsInner::savePinnedOrder() {
|
|||||||
if (newOrder.size() != _pinnedOrder.size()) {
|
if (newOrder.size() != _pinnedOrder.size()) {
|
||||||
return; // Something has changed in the set of pinned chats.
|
return; // Something has changed in the set of pinned chats.
|
||||||
}
|
}
|
||||||
|
|
||||||
auto peers = QVector<MTPInputPeer>();
|
|
||||||
peers.reserve(newOrder.size());
|
|
||||||
for_const (auto history, newOrder) {
|
for_const (auto history, newOrder) {
|
||||||
if (_pinnedOrder.indexOf(history) < 0) {
|
if (_pinnedOrder.indexOf(history) < 0) {
|
||||||
return; // Something has changed in the set of pinned chats.
|
return; // Something has changed in the set of pinned chats.
|
||||||
}
|
}
|
||||||
peers.push_back(history->peer->input);
|
|
||||||
}
|
}
|
||||||
auto flags = MTPmessages_ReorderPinnedDialogs::Flag::f_force;
|
App::histories().savePinnedToServer();
|
||||||
MTP::send(MTPmessages_ReorderPinnedDialogs(MTP_flags(qFlags(flags)), MTP_vector(peers)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsInner::finishReorderPinned() {
|
void DialogsInner::finishReorderPinned() {
|
||||||
|
@ -719,6 +719,17 @@ QList<History*> Histories::getPinnedOrder() const {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Histories::savePinnedToServer() const {
|
||||||
|
auto order = getPinnedOrder();
|
||||||
|
auto peers = QVector<MTPInputPeer>();
|
||||||
|
peers.reserve(order.size());
|
||||||
|
for_const (auto history, order) {
|
||||||
|
peers.push_back(history->peer->input);
|
||||||
|
}
|
||||||
|
auto flags = MTPmessages_ReorderPinnedDialogs::Flag::f_force;
|
||||||
|
MTP::send(MTPmessages_ReorderPinnedDialogs(MTP_flags(qFlags(flags)), MTP_vector(peers)));
|
||||||
|
}
|
||||||
|
|
||||||
HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, bool detachExistingItem) {
|
HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, bool detachExistingItem) {
|
||||||
auto msgId = MsgId(0);
|
auto msgId = MsgId(0);
|
||||||
switch (msg.type()) {
|
switch (msg.type()) {
|
||||||
|
@ -83,6 +83,7 @@ public:
|
|||||||
void clearPinned();
|
void clearPinned();
|
||||||
int pinnedCount() const;
|
int pinnedCount() const;
|
||||||
QList<History*> getPinnedOrder() const;
|
QList<History*> getPinnedOrder() const;
|
||||||
|
void savePinnedToServer() const;
|
||||||
|
|
||||||
struct SendActionAnimationUpdate {
|
struct SendActionAnimationUpdate {
|
||||||
History *history;
|
History *history;
|
||||||
|
@ -1976,7 +1976,25 @@ void MainWidget::fillPeerMenu(PeerData *peer, base::lambda<QAction*(const QStrin
|
|||||||
auto history = App::history(peer);
|
auto history = App::history(peer);
|
||||||
auto isPinned = !history->isPinnedDialog();
|
auto isPinned = !history->isPinnedDialog();
|
||||||
if (isPinned && App::histories().pinnedCount() >= Global::PinnedDialogsCountMax()) {
|
if (isPinned && App::histories().pinnedCount() >= Global::PinnedDialogsCountMax()) {
|
||||||
Ui::show(Box<InformBox>(lng_error_pinned_max(lt_count, Global::PinnedDialogsCountMax())));
|
// Some old chat, that was converted to supergroup, maybe is still pinned.
|
||||||
|
auto findWastedPin = []() -> History* {
|
||||||
|
auto order = App::histories().getPinnedOrder();
|
||||||
|
for_const (auto pinned, order) {
|
||||||
|
if (pinned->peer->isChat()
|
||||||
|
&& pinned->peer->asChat()->isDeactivated()
|
||||||
|
&& !pinned->inChatList(Dialogs::Mode::All)) {
|
||||||
|
return pinned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
if (auto wasted = findWastedPin()) {
|
||||||
|
wasted->setPinnedDialog(false);
|
||||||
|
history->setPinnedDialog(isPinned);
|
||||||
|
App::histories().savePinnedToServer();
|
||||||
|
} else {
|
||||||
|
Ui::show(Box<InformBox>(lng_error_pinned_max(lt_count, Global::PinnedDialogsCountMax())));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1659,20 +1659,54 @@ void DocumentData::notifyLayoutChanged() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit) {
|
VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit) {
|
||||||
VoiceWaveform result((encoded5bit.size() * 8) / 5, 0);
|
auto bitsCount = static_cast<int>(encoded5bit.size() * 8);
|
||||||
for (int32 i = 0, l = result.size(); i < l; ++i) { // read each 5 bit of encoded5bit as 0-31 unsigned char
|
auto valuesCount = bitsCount / 5;
|
||||||
int32 byte = (i * 5) / 8, shift = (i * 5) % 8;
|
if (!valuesCount) {
|
||||||
result[i] = (((*(uint16*)(encoded5bit.constData() + byte)) >> shift) & 0x1F);
|
return VoiceWaveform();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read each 5 bit of encoded5bit as 0-31 unsigned char.
|
||||||
|
// We count the index of the byte in which the desired 5-bit sequence starts.
|
||||||
|
// And then we read a uint16 starting from that byte to guarantee to get all of those 5 bits.
|
||||||
|
//
|
||||||
|
// BUT! if it is the last byte we have, we're not allowed to read a uint16 starting with it.
|
||||||
|
// Because it will be an overflow (we'll access one byte after the available memory).
|
||||||
|
// We see, that only the last 5 bits could start in the last available byte and be problematic.
|
||||||
|
// So we read in a general way all the entries in a general way except the last one.
|
||||||
|
auto result = VoiceWaveform(valuesCount, 0);
|
||||||
|
auto bitsData = encoded5bit.constData();
|
||||||
|
for (auto i = 0, l = valuesCount - 1; i != l; ++i) {
|
||||||
|
auto byteIndex = (i * 5) / 8;
|
||||||
|
auto bitShift = (i * 5) % 8;
|
||||||
|
auto value = *reinterpret_cast<const uint16*>(bitsData + byteIndex);
|
||||||
|
result[i] = static_cast<char>((value >> bitShift) & 0x1F);
|
||||||
|
}
|
||||||
|
auto lastByteIndex = ((valuesCount - 1) * 5) / 8;
|
||||||
|
auto lastBitShift = ((valuesCount - 1) * 5) % 8;
|
||||||
|
auto lastValue = (lastByteIndex == encoded5bit.size() - 1)
|
||||||
|
? static_cast<uint16>(*reinterpret_cast<const uchar*>(bitsData + lastByteIndex))
|
||||||
|
: *reinterpret_cast<const uint16*>(bitsData + lastByteIndex);
|
||||||
|
result[valuesCount - 1] = static_cast<char>((lastValue >> lastBitShift) & 0x1F);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform) {
|
QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform) {
|
||||||
QByteArray result((waveform.size() * 5 + 7) / 8, 0);
|
auto bitsCount = waveform.size() * 5;
|
||||||
for (int32 i = 0, l = waveform.size(); i < l; ++i) { // write each 0-31 unsigned char as 5 bit to result
|
auto bytesCount = (bitsCount + 7) / 8;
|
||||||
int32 byte = (i * 5) / 8, shift = (i * 5) % 8;
|
auto result = QByteArray(bytesCount + 1, 0);
|
||||||
(*(uint16*)(result.data() + byte)) |= (uint16(waveform.at(i) & 0x1F) << shift);
|
auto bitsData = result.data();
|
||||||
|
|
||||||
|
// Write each 0-31 unsigned char as 5 bit to result.
|
||||||
|
// We reserve one extra byte to be able to dereference any of required bytes
|
||||||
|
// as a uint16 without overflowing, even the byte with index "bytesCount - 1".
|
||||||
|
for (auto i = 0, l = waveform.size(); i < l; ++i) {
|
||||||
|
auto byteIndex = (i * 5) / 8;
|
||||||
|
auto bitShift = (i * 5) % 8;
|
||||||
|
auto value = (static_cast<uint16>(waveform[i]) & 0x1F) << bitShift;
|
||||||
|
*reinterpret_cast<uint16*>(bitsData + byteIndex) |= value;
|
||||||
}
|
}
|
||||||
|
result.resize(bytesCount);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
AppVersion 1000009
|
AppVersion 1000010
|
||||||
AppVersionStrMajor 1.0
|
AppVersionStrMajor 1.0
|
||||||
AppVersionStrSmall 1.0.9
|
AppVersionStrSmall 1.0.10
|
||||||
AppVersionStr 1.0.9
|
AppVersionStr 1.0.10
|
||||||
AlphaChannel 1
|
AlphaChannel 1
|
||||||
BetaVersion 0
|
BetaVersion 0
|
||||||
|
Loading…
Reference in New Issue
Block a user