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:
John Preston 2017-02-16 19:47:50 +03:00
parent 8d354382a4
commit 7adfe93a8d
12 changed files with 103 additions and 32 deletions

View File

@ -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>

View File

@ -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"

View File

@ -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"

View File

@ -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 {

View File

@ -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;

View File

@ -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) {

View File

@ -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() {

View File

@ -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()) {

View File

@ -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;

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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