Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0077d98f0 | ||
|
|
b5bc7a22af | ||
|
|
e60311811b | ||
|
|
8700c1b08f | ||
|
|
d8b51670e7 | ||
|
|
68c2f563c6 | ||
|
|
bf775cb4ca | ||
|
|
5c4b81434e | ||
|
|
cd1c7c56d3 | ||
|
|
427ceb9a9a | ||
|
|
2a110f0d3e | ||
|
|
d0ed75f3b5 | ||
|
|
be2abd594f | ||
|
|
75a40b83ae | ||
|
|
472a677bca | ||
|
|
dca6e10beb | ||
|
|
122ab94f3d |
@@ -1567,7 +1567,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_passport_address_agreement" = "Tenancy agreement";
|
||||
"lng_passport_address_agreement_upload" = "Upload a scan of your tenancy agreement";
|
||||
"lng_passport_address_registration" = "Passport registration";
|
||||
"lng_passport_address_registration_upload" = "Upload a scan of your passport registration";
|
||||
"lng_passport_address_registration_upload" = "Upload a scan of your passport registration page";
|
||||
"lng_passport_address_temporary" = "Temporary registration";
|
||||
"lng_passport_address_temporary_upload" = "Upload a scan of your temporary registration";
|
||||
"lng_passport_address_about" = "To confirm your address, please upload a scan or photo of the selected document (all pages).";
|
||||
@@ -1594,12 +1594,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_passport_upload_selfie" = "Upload selfie";
|
||||
"lng_passport_front_side_title" = "Front side";
|
||||
"lng_passport_front_side_name" = "Scan";
|
||||
"lng_passport_front_side_description" = "Upload front side of your document.";
|
||||
"lng_passport_upload_front_side" = "Upload front side scan";
|
||||
"lng_passport_front_side_description" = "Upload the front side of your document.";
|
||||
"lng_passport_upload_front_side" = "Upload a scan of the front side";
|
||||
"lng_passport_reverse_side_title" = "Reverse side";
|
||||
"lng_passport_reverse_side_name" = "Scan";
|
||||
"lng_passport_reverse_side_description" = "Upload reverse side of your document.";
|
||||
"lng_passport_upload_reverse_side" = "Upload reverse side scan";
|
||||
"lng_passport_reverse_side_description" = "Upload the reverse side of your document.";
|
||||
"lng_passport_upload_reverse_side" = "Upload a scan of the reverse side";
|
||||
"lng_passport_personal_details" = "Personal details";
|
||||
"lng_passport_personal_details_enter" = "Enter your personal details";
|
||||
"lng_passport_choose_image" = "Choose scan image";
|
||||
@@ -1643,15 +1643,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_passport_delete_email_sure" = "Are you sure you want to delete your email address?";
|
||||
"lng_passport_delete_phone" = "Delete phone number";
|
||||
"lng_passport_delete_phone_sure" = "Are you sure you want to delete your phone number?";
|
||||
"lng_passport_success" = "Authorization successfull!";
|
||||
"lng_passport_success" = "Authorization successful!";
|
||||
"lng_passport_stop_sure" = "Are you sure you want to stop this authorization?";
|
||||
"lng_passport_stop" = "Stop";
|
||||
"lng_passport_restart_sure" = "An unexpected error has occurred. Perhaps some changes were made from a different Telegram application. Would you like to restart this authorization?";
|
||||
"lng_passport_restart" = "Restart";
|
||||
"lng_passport_error_too_large" = "This file is too large.";
|
||||
"lng_passport_error_bad_size" = "This image has bad dimensions.";
|
||||
"lng_passport_error_too_large" = "Sorry, this file is too large.";
|
||||
"lng_passport_error_bad_size" = "Sorry, this image has wrong dimensions.";
|
||||
"lng_passport_error_cant_read" = "Can't read this file. Please choose an image.";
|
||||
"lng_passport_bad_name" = "Use latin characters only.";
|
||||
"lng_passport_bad_name" = "Please use latin characters only.";
|
||||
|
||||
// Wnd specific
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="1.3.3.0" />
|
||||
Version="1.3.7.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,3,3,0
|
||||
PRODUCTVERSION 1,3,3,0
|
||||
FILEVERSION 1,3,7,0
|
||||
PRODUCTVERSION 1,3,7,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -52,10 +52,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "1.3.3.0"
|
||||
VALUE "FileVersion", "1.3.7.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2018"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.3.3.0"
|
||||
VALUE "ProductVersion", "1.3.7.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,3,3,0
|
||||
PRODUCTVERSION 1,3,3,0
|
||||
FILEVERSION 1,3,7,0
|
||||
PRODUCTVERSION 1,3,7,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -43,10 +43,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "1.3.3.0"
|
||||
VALUE "FileVersion", "1.3.7.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2018"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.3.3.0"
|
||||
VALUE "ProductVersion", "1.3.7.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -2055,10 +2055,21 @@ void ApiWrap::saveDraftsToCloud() {
|
||||
if (!textWithTags.tags.isEmpty()) {
|
||||
flags |= MTPmessages_SaveDraft::Flag::f_entities;
|
||||
}
|
||||
auto entities = TextUtilities::EntitiesToMTP(ConvertTextTagsToEntities(textWithTags.tags), TextUtilities::ConvertOption::SkipLocal);
|
||||
auto entities = TextUtilities::EntitiesToMTP(
|
||||
ConvertTextTagsToEntities(textWithTags.tags),
|
||||
TextUtilities::ConvertOption::SkipLocal);
|
||||
|
||||
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(MTP_flags(flags), MTP_int(cloudDraft->msgId), history->peer->input, MTP_string(textWithTags.text), entities)).done([this, history](const MTPBool &result, mtpRequestId requestId) {
|
||||
if (auto cloudDraft = history->cloudDraft()) {
|
||||
history->setSentDraftText(textWithTags.text);
|
||||
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(
|
||||
MTP_flags(flags),
|
||||
MTP_int(cloudDraft->msgId),
|
||||
history->peer->input,
|
||||
MTP_string(textWithTags.text),
|
||||
entities
|
||||
)).done([=](const MTPBool &result, mtpRequestId requestId) {
|
||||
history->clearSentDraftText();
|
||||
|
||||
if (const auto cloudDraft = history->cloudDraft()) {
|
||||
if (cloudDraft->saveRequestId == requestId) {
|
||||
cloudDraft->saveRequestId = 0;
|
||||
history->draftSavedToCloud();
|
||||
@@ -2069,8 +2080,10 @@ void ApiWrap::saveDraftsToCloud() {
|
||||
_draftsSaveRequestIds.erase(history);
|
||||
checkQuitPreventFinished();
|
||||
}
|
||||
}).fail([this, history](const RPCError &error, mtpRequestId requestId) {
|
||||
if (auto cloudDraft = history->cloudDraft()) {
|
||||
}).fail([=](const RPCError &error, mtpRequestId requestId) {
|
||||
history->clearSentDraftText();
|
||||
|
||||
if (const auto cloudDraft = history->cloudDraft()) {
|
||||
if (cloudDraft->saveRequestId == requestId) {
|
||||
history->clearCloudDraft();
|
||||
}
|
||||
|
||||
@@ -50,8 +50,7 @@ namespace {
|
||||
|
||||
UserData *self = nullptr;
|
||||
|
||||
using PeersData = QHash<PeerId, PeerData*>;
|
||||
PeersData peersData;
|
||||
std::unordered_map<PeerId, std::unique_ptr<PeerData>> peersData;
|
||||
|
||||
using LocationsData = QHash<LocationCoords, LocationData*>;
|
||||
LocationsData locationsData;
|
||||
@@ -747,7 +746,7 @@ namespace App {
|
||||
} else if (chat->version <= d.vversion.v && chat->count > 0) {
|
||||
chat->version = d.vversion.v;
|
||||
auto canEdit = chat->canEdit();
|
||||
UserData *user = App::userLoaded(d.vuser_id.v);
|
||||
const auto user = App::userLoaded(d.vuser_id.v);
|
||||
if (user) {
|
||||
if (chat->participants.empty()) {
|
||||
if (chat->count > 0) {
|
||||
@@ -1074,40 +1073,43 @@ namespace App {
|
||||
}
|
||||
|
||||
PeerData *peer(const PeerId &id, PeerData::LoadedStatus restriction) {
|
||||
if (!id) return nullptr;
|
||||
if (!id) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto i = peersData.constFind(id);
|
||||
auto i = peersData.find(id);
|
||||
if (i == peersData.cend()) {
|
||||
PeerData *newData = nullptr;
|
||||
if (peerIsUser(id)) {
|
||||
newData = new UserData(id);
|
||||
} else if (peerIsChat(id)) {
|
||||
newData = new ChatData(id);
|
||||
} else if (peerIsChannel(id)) {
|
||||
newData = new ChannelData(id);
|
||||
}
|
||||
Assert(newData != nullptr);
|
||||
auto newData = [&]() -> std::unique_ptr<PeerData> {
|
||||
if (peerIsUser(id)) {
|
||||
return std::make_unique<UserData>(id);
|
||||
} else if (peerIsChat(id)) {
|
||||
return std::make_unique<ChatData>(id);
|
||||
} else if (peerIsChannel(id)) {
|
||||
return std::make_unique<ChannelData>(id);
|
||||
}
|
||||
Unexpected("Peer id type.");
|
||||
}();
|
||||
|
||||
newData->input = MTPinputPeer(MTP_inputPeerEmpty());
|
||||
i = peersData.insert(id, newData);
|
||||
i = peersData.emplace(id, std::move(newData)).first;
|
||||
}
|
||||
switch (restriction) {
|
||||
case PeerData::MinimalLoaded: {
|
||||
if (i.value()->loadedStatus == PeerData::NotLoaded) {
|
||||
if (i->second->loadedStatus == PeerData::NotLoaded) {
|
||||
return nullptr;
|
||||
}
|
||||
} break;
|
||||
case PeerData::FullLoaded: {
|
||||
if (i.value()->loadedStatus != PeerData::FullLoaded) {
|
||||
if (i->second->loadedStatus != PeerData::FullLoaded) {
|
||||
return nullptr;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return i.value();
|
||||
return i->second.get();
|
||||
}
|
||||
|
||||
void enumerateUsers(Fn<void(not_null<UserData*>)> action) {
|
||||
for_const (const auto peer, peersData) {
|
||||
for (const auto &[peerId, peer] : peersData) {
|
||||
if (const auto user = peer->asUser()) {
|
||||
action(user);
|
||||
}
|
||||
@@ -1116,9 +1118,9 @@ namespace App {
|
||||
|
||||
void enumerateChatsChannels(
|
||||
Fn<void(not_null<PeerData*>)> action) {
|
||||
for_const (const auto peer, peersData) {
|
||||
for (const auto &[peerId, peer] : peersData) {
|
||||
if (!peer->isUser()) {
|
||||
action(peer);
|
||||
action(peer.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1128,10 +1130,10 @@ namespace App {
|
||||
}
|
||||
|
||||
PeerData *peerByName(const QString &username) {
|
||||
QString uname(username.trimmed());
|
||||
for_const (PeerData *peer, peersData) {
|
||||
const auto uname = username.trimmed();
|
||||
for (const auto &[peerId, peer] : peersData) {
|
||||
if (!peer->userName().compare(uname, Qt::CaseInsensitive)) {
|
||||
return peer;
|
||||
return peer.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@@ -1246,24 +1248,16 @@ namespace App {
|
||||
void historyClearMsgs() {
|
||||
::dependentItems.clear();
|
||||
|
||||
QVector<HistoryItem*> toDelete;
|
||||
for_const (auto item, msgsData) {
|
||||
if (!item->mainView()) {
|
||||
toDelete.push_back(item);
|
||||
}
|
||||
}
|
||||
for_const (auto &chMsgsData, channelMsgsData) {
|
||||
for_const (auto item, chMsgsData) {
|
||||
if (!item->mainView()) {
|
||||
toDelete.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
msgsData.clear();
|
||||
channelMsgsData.clear();
|
||||
for_const (auto item, toDelete) {
|
||||
const auto oldData = base::take(msgsData);
|
||||
const auto oldChannelData = base::take(channelMsgsData);
|
||||
for (const auto item : oldData) {
|
||||
delete item;
|
||||
}
|
||||
for (const auto &data : oldChannelData) {
|
||||
for (const auto item : data) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
clearMousedItems();
|
||||
}
|
||||
@@ -1275,10 +1269,7 @@ namespace App {
|
||||
cSetSavedPeersByTime(SavedPeersByTime());
|
||||
cSetRecentInlineBots(RecentInlineBots());
|
||||
|
||||
for_const (auto peer, ::peersData) {
|
||||
delete peer;
|
||||
}
|
||||
::peersData.clear();
|
||||
peersData.clear();
|
||||
|
||||
if (AuthSession::Exists()) {
|
||||
Auth().api().clearWebPageRequests();
|
||||
|
||||
@@ -21,6 +21,10 @@ constexpr auto kForeverHours = 24 * 365;
|
||||
|
||||
} // namespace
|
||||
|
||||
MuteSettingsBox::MuteSettingsBox(QWidget *parent, not_null<PeerData*> peer)
|
||||
: _peer(peer) {
|
||||
}
|
||||
|
||||
void MuteSettingsBox::prepare() {
|
||||
setTitle(langFactory(lng_disable_notifications_from_tray));
|
||||
auto y = 0;
|
||||
@@ -67,15 +71,25 @@ void MuteSettingsBox::prepare() {
|
||||
- st::boxOptionListSkip
|
||||
+ st::defaultCheckbox.margin.bottom();
|
||||
|
||||
addButton(langFactory(lng_box_ok), [this, group] {
|
||||
auto muteForSeconds = group->value() * 3600;
|
||||
_save = [=] {
|
||||
const auto muteForSeconds = group->value() * 3600;
|
||||
Auth().data().updateNotifySettings(
|
||||
_peer,
|
||||
muteForSeconds);
|
||||
closeBox();
|
||||
});
|
||||
};
|
||||
addButton(langFactory(lng_box_ok), _save);
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
|
||||
setDimensions(st::boxWidth, y);
|
||||
}
|
||||
|
||||
void MuteSettingsBox::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
|
||||
if (_save) {
|
||||
_save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vi: ts=4 tw=80
|
||||
|
||||
@@ -13,17 +13,17 @@ Copyright (C) 2017, Nicholas Guriev <guriev-ns@ya.ru>
|
||||
* turning off notifications from a chat. The widget is opened by a context menu
|
||||
* in the left list of dialogues. */
|
||||
class MuteSettingsBox : public BoxContent {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MuteSettingsBox(QWidget *parent, not_null<PeerData*> peer);
|
||||
|
||||
public:
|
||||
MuteSettingsBox(QWidget *parent, not_null<PeerData*> peer)
|
||||
: _peer(peer) {
|
||||
}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
private:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
private:
|
||||
not_null<PeerData*> _peer;
|
||||
Fn<void()> _save;
|
||||
|
||||
};
|
||||
// vi: ts=4 tw=80
|
||||
|
||||
@@ -609,19 +609,21 @@ void MessageLinksParser::parse() {
|
||||
const auto markdownTagsEnd = markdownTags.end();
|
||||
const auto markdownTagsAllow = [&](int from, int length) {
|
||||
while (markdownTag != markdownTagsEnd
|
||||
&& (markdownTag->start + markdownTag->length <= from
|
||||
&& (markdownTag->adjustedStart
|
||||
+ markdownTag->adjustedLength <= from
|
||||
|| !markdownTag->closed)) {
|
||||
++markdownTag;
|
||||
continue;
|
||||
}
|
||||
if (markdownTag == markdownTagsEnd
|
||||
|| markdownTag->start >= from + length) {
|
||||
|| markdownTag->adjustedStart >= from + length) {
|
||||
return true;
|
||||
}
|
||||
// Ignore http-links that are completely inside some tags.
|
||||
// This will allow sending http://test.com/__test__/test correctly.
|
||||
return (markdownTag->start > from
|
||||
|| markdownTag->start + markdownTag->length < from + length);
|
||||
return (markdownTag->adjustedStart > from)
|
||||
|| (markdownTag->adjustedStart
|
||||
+ markdownTag->adjustedLength < from + length);
|
||||
};
|
||||
|
||||
const auto len = text.size();
|
||||
|
||||
@@ -82,9 +82,6 @@ enum {
|
||||
MaxMessageSize = 4096,
|
||||
|
||||
WriteMapTimeout = 1000,
|
||||
SaveDraftTimeout = 1000, // save draft after 1 secs of not changing text
|
||||
SaveDraftAnywayTimeout = 5000, // or save anyway each 5 secs
|
||||
SaveCloudDraftIdleTimeout = 14000, // save draft to the cloud after 14 more seconds
|
||||
|
||||
SetOnlineAfterActivity = 30, // user with hidden last seen stays online for such amount of seconds in the interface
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#define BETA_VERSION_MACRO (0ULL)
|
||||
|
||||
constexpr int AppVersion = 1003003;
|
||||
constexpr str_const AppVersionStr = "1.3.3";
|
||||
constexpr bool AppAlphaVersion = true;
|
||||
constexpr int AppVersion = 1003007;
|
||||
constexpr str_const AppVersionStr = "1.3.7";
|
||||
constexpr bool AppAlphaVersion = false;
|
||||
constexpr uint64 AppBetaVersion = BETA_VERSION_MACRO;
|
||||
|
||||
@@ -52,6 +52,9 @@ void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) {
|
||||
? TextUtilities::EntitiesFromMTP(draft.ventities.v)
|
||||
: EntitiesInText())
|
||||
};
|
||||
if (history->skipCloudDraft(textWithTags.text, draft.vdate.v)) {
|
||||
return;
|
||||
}
|
||||
auto replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : MsgId(0);
|
||||
auto cloudDraft = std::make_unique<Draft>(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage());
|
||||
cloudDraft->date = draft.vdate.v;
|
||||
|
||||
@@ -1741,7 +1741,7 @@ bool Session::notifyIsMuted(
|
||||
if (const auto until = settings.muteUntil()) {
|
||||
return resultFromUntil(*until);
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Session::notifySilentPosts(not_null<const PeerData*> peer) const {
|
||||
|
||||
@@ -51,6 +51,7 @@ constexpr auto kStatusShowClientsideChooseContact = 6000;
|
||||
constexpr auto kStatusShowClientsidePlayGame = 10000;
|
||||
constexpr auto kSetMyActionForMs = 10000;
|
||||
constexpr auto kNewBlockEachMessage = 50;
|
||||
constexpr auto kSkipCloudDraftsFor = TimeId(3);
|
||||
|
||||
void checkForSwitchInlineButton(HistoryItem *item) {
|
||||
if (item->out() || !item->hasSwitchInlineButton()) {
|
||||
@@ -98,8 +99,10 @@ not_null<History*> Histories::findOrInsert(PeerId peerId) {
|
||||
}
|
||||
|
||||
void Histories::clear() {
|
||||
for (const auto &[peerId, history] : _map) {
|
||||
history->unloadBlocks();
|
||||
}
|
||||
App::historyClearMsgs();
|
||||
|
||||
_map.clear();
|
||||
|
||||
_unreadFull = _unreadMuted = 0;
|
||||
@@ -405,6 +408,24 @@ Data::Draft *History::createCloudDraft(Data::Draft *fromDraft) {
|
||||
return cloudDraft();
|
||||
}
|
||||
|
||||
bool History::skipCloudDraft(const QString &text, TimeId date) const {
|
||||
if (_lastSentDraftText && *_lastSentDraftText == text) {
|
||||
return true;
|
||||
} else if (date <= _lastSentDraftTime + kSkipCloudDraftsFor) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void History::setSentDraftText(const QString &text) {
|
||||
_lastSentDraftText = text;
|
||||
}
|
||||
|
||||
void History::clearSentDraftText() {
|
||||
_lastSentDraftText = base::none;
|
||||
accumulate_max(_lastSentDraftTime, unixtime());
|
||||
}
|
||||
|
||||
void History::setEditDraft(std::unique_ptr<Data::Draft> &&draft) {
|
||||
_editDraft = std::move(draft);
|
||||
}
|
||||
@@ -2198,9 +2219,6 @@ void History::applyDialog(const MTPDdialog &data) {
|
||||
MTP_notifyPeer(data.vpeer),
|
||||
data.vnotify_settings);
|
||||
|
||||
// Request default notification settings for this type of chat.
|
||||
Auth().data().requestNotifySettings(peer);
|
||||
|
||||
if (data.has_draft() && data.vdraft.type() == mtpc_draftMessage) {
|
||||
Data::applyPeerCloudDraft(peer->id, data.vdraft.c_draftMessage());
|
||||
}
|
||||
|
||||
@@ -304,6 +304,9 @@ public:
|
||||
void createLocalDraftFromCloud();
|
||||
void setCloudDraft(std::unique_ptr<Data::Draft> &&draft);
|
||||
Data::Draft *createCloudDraft(Data::Draft *fromDraft);
|
||||
bool skipCloudDraft(const QString &text, TimeId date) const;
|
||||
void setSentDraftText(const QString &text);
|
||||
void clearSentDraftText();
|
||||
void setEditDraft(std::unique_ptr<Data::Draft> &&draft);
|
||||
void clearLocalDraft();
|
||||
void clearCloudDraft();
|
||||
@@ -501,6 +504,8 @@ private:
|
||||
|
||||
std::unique_ptr<Data::Draft> _localDraft, _cloudDraft;
|
||||
std::unique_ptr<Data::Draft> _editDraft;
|
||||
base::optional<QString> _lastSentDraftText;
|
||||
TimeId _lastSentDraftTime = 0;
|
||||
MessageIdsList _forwardDraft;
|
||||
|
||||
using TypingUsers = QMap<UserData*, TimeMs>;
|
||||
|
||||
@@ -91,6 +91,9 @@ constexpr auto kShowMembersDropdownTimeoutMs = 300;
|
||||
constexpr auto kDisplayEditTimeWarningMs = 300 * 1000;
|
||||
constexpr auto kFullDayInMs = 86400 * 1000;
|
||||
constexpr auto kCancelTypingActionTimeout = TimeMs(5000);
|
||||
constexpr auto kSaveDraftTimeout = 1000;
|
||||
constexpr auto kSaveDraftAnywayTimeout = 5000;
|
||||
constexpr auto kSaveCloudDraftIdleTimeout = 14000;
|
||||
|
||||
ApiWrap::RequestMessageDataCallback replyEditMessageDataCallback() {
|
||||
return [](ChannelData *channel, MsgId msgId) {
|
||||
@@ -1192,9 +1195,9 @@ void HistoryWidget::onDraftSave(bool delayed) {
|
||||
auto ms = getms();
|
||||
if (!_saveDraftStart) {
|
||||
_saveDraftStart = ms;
|
||||
return _saveDraftTimer.start(SaveDraftTimeout);
|
||||
} else if (ms - _saveDraftStart < SaveDraftAnywayTimeout) {
|
||||
return _saveDraftTimer.start(SaveDraftTimeout);
|
||||
return _saveDraftTimer.start(kSaveDraftTimeout);
|
||||
} else if (ms - _saveDraftStart < kSaveDraftAnywayTimeout) {
|
||||
return _saveDraftTimer.start(kSaveDraftTimeout);
|
||||
}
|
||||
}
|
||||
writeDrafts(nullptr, nullptr);
|
||||
@@ -1275,7 +1278,7 @@ void HistoryWidget::writeDrafts(Data::Draft **localDraft, Data::Draft **editDraf
|
||||
}
|
||||
|
||||
if (!_editMsgId && !_inlineBot) {
|
||||
_saveCloudDraftTimer.start(SaveCloudDraftIdleTimeout);
|
||||
_saveCloudDraftTimer.start(kSaveCloudDraftIdleTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -494,8 +494,9 @@ void TopBarWidget::updateControlsGeometry() {
|
||||
buttonsWidth += buttonsLeft + st::topBarActionSkip * 3;
|
||||
|
||||
auto widthLeft = qMin(width() - buttonsWidth, -2 * st::defaultActiveButton.width);
|
||||
_forward->setFullWidth(-(widthLeft / 2));
|
||||
_delete->setFullWidth(-(widthLeft / 2));
|
||||
auto buttonFullWidth = qMin(-(widthLeft / 2), 0);
|
||||
_forward->setFullWidth(buttonFullWidth);
|
||||
_delete->setFullWidth(buttonFullWidth);
|
||||
|
||||
selectedButtonsTop += (height() - _forward->height()) / 2;
|
||||
|
||||
|
||||
@@ -69,9 +69,9 @@ public:
|
||||
|
||||
void closeMain() {
|
||||
QMutexLocker lock(_logsMutex(LogDataMain));
|
||||
if (files[LogDataMain]) {
|
||||
streams[LogDataMain].setDevice(0);
|
||||
files[LogDataMain]->close();
|
||||
const auto file = files[LogDataMain].get();
|
||||
if (file && file->isOpen()) {
|
||||
file->close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +80,12 @@ public:
|
||||
}
|
||||
|
||||
QString full() {
|
||||
if (!streams[LogDataMain].device()) {
|
||||
const auto file = files[LogDataMain].get();
|
||||
if (!!file || !file->isOpen()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QFile out(files[LogDataMain]->fileName());
|
||||
QFile out(file->fileName());
|
||||
if (out.open(QIODevice::ReadOnly)) {
|
||||
return QString::fromUtf8(out.readAll());
|
||||
}
|
||||
@@ -93,27 +94,29 @@ public:
|
||||
|
||||
void write(LogDataType type, const QString &msg) {
|
||||
QMutexLocker lock(_logsMutex(type));
|
||||
if (type != LogDataMain) reopenDebug();
|
||||
if (!streams[type].device()) return;
|
||||
|
||||
streams[type] << msg;
|
||||
streams[type].flush();
|
||||
if (type != LogDataMain) {
|
||||
reopenDebug();
|
||||
}
|
||||
const auto file = files[type].get();
|
||||
if (!file || !file->isOpen()) {
|
||||
return;
|
||||
}
|
||||
file->write(msg.toUtf8());
|
||||
file->flush();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<QFile> files[LogDataCount];
|
||||
QTextStream streams[LogDataCount];
|
||||
|
||||
int32 part = -1;
|
||||
|
||||
bool reopen(LogDataType type, int32 dayIndex, const QString &postfix) {
|
||||
if (streams[type].device()) {
|
||||
if (files[type] && files[type]->isOpen()) {
|
||||
if (type == LogDataMain) {
|
||||
if (!postfix.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
streams[type].setDevice(0);
|
||||
files[type]->close();
|
||||
}
|
||||
}
|
||||
@@ -134,8 +137,6 @@ private:
|
||||
}
|
||||
if (to->open(mode | QIODevice::Append)) {
|
||||
std::swap(files[type], to);
|
||||
streams[type].setDevice(files[type].get());
|
||||
streams[type].setCodec("UTF-8");
|
||||
LOG(("Moved logging from '%1' to '%2'!").arg(to->fileName()).arg(files[type]->fileName()));
|
||||
to->remove();
|
||||
|
||||
@@ -192,17 +193,14 @@ private:
|
||||
}
|
||||
}
|
||||
if (files[type]->open(mode)) {
|
||||
streams[type].setDevice(files[type].get());
|
||||
streams[type].setCodec("UTF-8");
|
||||
|
||||
if (type != LogDataMain) {
|
||||
streams[type] << ((mode & QIODevice::Append)
|
||||
files[type]->write(((mode & QIODevice::Append)
|
||||
? qsl("\
|
||||
----------------------------------------------------------------\n\
|
||||
NEW LOGGING INSTANCE STARTED!!!\n\
|
||||
----------------------------------------------------------------\n")
|
||||
: qsl("%1\n").arg(dayIndex));
|
||||
streams[type].flush();
|
||||
: qsl("%1\n").arg(dayIndex)).toUtf8());
|
||||
files[type]->flush();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -3700,6 +3700,9 @@ void MainWidget::mtpPing() {
|
||||
}
|
||||
|
||||
void MainWidget::start(const MTPUser *self) {
|
||||
Auth().api().requestNotifySettings(MTP_inputNotifyUsers());
|
||||
Auth().api().requestNotifySettings(MTP_inputNotifyChats());
|
||||
|
||||
if (!self) {
|
||||
MTP::send(MTPusers_GetFullUser(MTP_inputUserSelf()), rpcDone(&MainWidget::startWithSelf));
|
||||
return;
|
||||
@@ -3713,7 +3716,7 @@ void MainWidget::start(const MTPUser *self) {
|
||||
|
||||
Local::readSavedPeers();
|
||||
cSetOtherOnline(0);
|
||||
if (auto user = App::feedUsers(MTP_vector<MTPUser>(1, *self))) {
|
||||
if (const auto user = App::feedUsers(MTP_vector<MTPUser>(1, *self))) {
|
||||
user->loadUserpic();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ passportErrorLabel: FlatLabel(passportPasswordLabel) {
|
||||
textFg: boxTextFgError;
|
||||
}
|
||||
passportVerifyErrorLabel: FlatLabel(passportErrorLabel) {
|
||||
minWidth: 128px;
|
||||
align: align(topleft);
|
||||
}
|
||||
|
||||
@@ -169,10 +170,11 @@ passportDetailsField: InputField(defaultInputField) {
|
||||
font: normalFont;
|
||||
}
|
||||
passportDetailsDateField: InputField(passportDetailsField) {
|
||||
textMargins: margins(2px, 8px, 2px, 0px);
|
||||
border: 0px;
|
||||
borderActive: 0px;
|
||||
heightMin: 30px;
|
||||
placeholderFont: font(semibold 14px);
|
||||
placeholderFont: semiboldFont;
|
||||
placeholderFgActive: placeholderFgActive;
|
||||
}
|
||||
passportDetailsSeparator: FlatLabel(passportPasswordLabelBold) {
|
||||
@@ -189,7 +191,7 @@ passportDetailsFieldLeft: 116px;
|
||||
passportDetailsFieldTop: 2px;
|
||||
passportDetailsFieldSkipMin: 12px;
|
||||
passportDetailsSkip: 30px;
|
||||
passportDetailsGenderSkip: 30px;
|
||||
passportDetailsGenderSkip: 20px;
|
||||
|
||||
passportRequestTypeSkip: 16px;
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||
if (value.isEmpty() || value.size() > kMaxNameSize) {
|
||||
return QString();
|
||||
} else if (!QRegularExpression(
|
||||
"^[a-zA-Z\\- ]+$"
|
||||
"^[a-zA-Z0-9\\.,/&\\-' ]+$"
|
||||
).match(value).hasMatch()) {
|
||||
return lang(lng_passport_bad_name);
|
||||
}
|
||||
@@ -539,7 +539,9 @@ void PanelController::uploadSpecialScan(
|
||||
QByteArray &&content) {
|
||||
Expects(_editScope != nullptr);
|
||||
Expects(_editDocument != nullptr);
|
||||
Expects(_editScope->selfieRequired);
|
||||
Expects(_editDocument->requiresSpecialScan(
|
||||
type,
|
||||
_editScope->selfieRequired));
|
||||
|
||||
_form->uploadSpecialScan(_editDocument, type, std::move(content));
|
||||
}
|
||||
@@ -547,7 +549,9 @@ void PanelController::uploadSpecialScan(
|
||||
void PanelController::deleteSpecialScan(SpecialFile type) {
|
||||
Expects(_editScope != nullptr);
|
||||
Expects(_editDocument != nullptr);
|
||||
Expects(_editScope->selfieRequired);
|
||||
Expects(_editDocument->requiresSpecialScan(
|
||||
type,
|
||||
_editScope->selfieRequired));
|
||||
|
||||
_form->deleteSpecialScan(_editDocument, type);
|
||||
}
|
||||
@@ -555,7 +559,9 @@ void PanelController::deleteSpecialScan(SpecialFile type) {
|
||||
void PanelController::restoreSpecialScan(SpecialFile type) {
|
||||
Expects(_editScope != nullptr);
|
||||
Expects(_editDocument != nullptr);
|
||||
Expects(_editScope->selfieRequired);
|
||||
Expects(_editDocument->requiresSpecialScan(
|
||||
type,
|
||||
_editScope->selfieRequired));
|
||||
|
||||
_form->restoreSpecialScan(_editDocument, type);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ public:
|
||||
AbstractTextRow(
|
||||
QWidget *parent,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value,
|
||||
int limit);
|
||||
|
||||
@@ -101,6 +102,7 @@ public:
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value);
|
||||
|
||||
rpl::producer<QString> value() const override;
|
||||
@@ -152,7 +154,11 @@ private:
|
||||
|
||||
class DateRow : public PanelDetailsRow {
|
||||
public:
|
||||
DateRow(QWidget *parent, const QString &label, const QString &value);
|
||||
DateRow(
|
||||
QWidget *parent,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value);
|
||||
|
||||
bool setFocusFast() override;
|
||||
rpl::producer<QString> value() const override;
|
||||
@@ -206,6 +212,7 @@ public:
|
||||
GenderRow(
|
||||
QWidget *parent,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value);
|
||||
|
||||
rpl::producer<QString> value() const override;
|
||||
@@ -247,9 +254,10 @@ template <typename Input>
|
||||
AbstractTextRow<Input>::AbstractTextRow(
|
||||
QWidget *parent,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value,
|
||||
int limit)
|
||||
: PanelDetailsRow(parent, label)
|
||||
: PanelDetailsRow(parent, label, maxLabelWidth)
|
||||
, _field(this, st::passportDetailsField, nullptr, value)
|
||||
, _value(value) {
|
||||
_field->setMaxLength(limit);
|
||||
@@ -299,8 +307,9 @@ CountryRow::CountryRow(
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value)
|
||||
: PanelDetailsRow(parent, label)
|
||||
: PanelDetailsRow(parent, label, maxLabelWidth)
|
||||
, _controller(controller)
|
||||
, _link(this, CountryString(value), st::boxLinkButton)
|
||||
, _value(value) {
|
||||
@@ -505,8 +514,9 @@ void DateInput::correctValue(
|
||||
DateRow::DateRow(
|
||||
QWidget *parent,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value)
|
||||
: PanelDetailsRow(parent, label)
|
||||
: PanelDetailsRow(parent, label, maxLabelWidth)
|
||||
, _day(
|
||||
this,
|
||||
st::passportDetailsDateField,
|
||||
@@ -726,25 +736,26 @@ int DateRow::resizeInner(int left, int top, int width) {
|
||||
const auto right = left + width;
|
||||
const auto &_st = st::passportDetailsDateField;
|
||||
const auto &font = _st.placeholderFont;
|
||||
const auto addToWidth = st::passportDetailsSeparatorPadding.left();
|
||||
const auto dayWidth = _st.textMargins.left()
|
||||
+ _st.placeholderMargins.left()
|
||||
+ font->width(lang(lng_date_input_day))
|
||||
+ _st.placeholderMargins.right()
|
||||
+ _st.textMargins.right()
|
||||
+ st::lineWidth;
|
||||
+ addToWidth;
|
||||
const auto monthWidth = _st.textMargins.left()
|
||||
+ _st.placeholderMargins.left()
|
||||
+ font->width(lang(lng_date_input_month))
|
||||
+ _st.placeholderMargins.right()
|
||||
+ _st.textMargins.right()
|
||||
+ st::lineWidth;
|
||||
+ addToWidth;
|
||||
_day->setGeometry(left, top, dayWidth, _day->height());
|
||||
left += dayWidth - st::lineWidth;
|
||||
left += dayWidth - addToWidth;
|
||||
_separator1->resizeToNaturalWidth(width);
|
||||
_separator1->move(left, top);
|
||||
left += _separator1->width();
|
||||
_month->setGeometry(left, top, monthWidth, _month->height());
|
||||
left += monthWidth - st::lineWidth;
|
||||
left += monthWidth - addToWidth;
|
||||
_separator2->resizeToNaturalWidth(width);
|
||||
_separator2->move(left, top);
|
||||
left += _separator2->width();
|
||||
@@ -829,8 +840,9 @@ void DateRow::startBorderAnimation() {
|
||||
GenderRow::GenderRow(
|
||||
QWidget *parent,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value)
|
||||
: PanelDetailsRow(parent, label)
|
||||
: PanelDetailsRow(parent, label, maxLabelWidth)
|
||||
, _group(StringToGender(value).has_value()
|
||||
? std::make_shared<Ui::RadioenumGroup<Gender>>(*StringToGender(value))
|
||||
: std::make_shared<Ui::RadioenumGroup<Gender>>())
|
||||
@@ -938,8 +950,10 @@ void GenderRow::errorAnimationCallback() {
|
||||
|
||||
PanelDetailsRow::PanelDetailsRow(
|
||||
QWidget *parent,
|
||||
const QString &label)
|
||||
: _label(label) {
|
||||
const QString &label,
|
||||
int maxLabelWidth)
|
||||
: _label(label)
|
||||
, _maxLabelWidth(maxLabelWidth) {
|
||||
}
|
||||
|
||||
object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
||||
@@ -947,6 +961,7 @@ object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
||||
Type type,
|
||||
not_null<PanelController*> controller,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value,
|
||||
const QString &error,
|
||||
int limit) {
|
||||
@@ -956,20 +971,35 @@ object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
||||
return object_ptr<AbstractTextRow<Ui::InputField>>(
|
||||
parent,
|
||||
label,
|
||||
maxLabelWidth,
|
||||
value,
|
||||
limit);
|
||||
case Type::Postcode:
|
||||
return object_ptr<AbstractTextRow<PostcodeInput>>(
|
||||
parent,
|
||||
label,
|
||||
maxLabelWidth,
|
||||
value,
|
||||
limit);
|
||||
case Type::Country:
|
||||
return object_ptr<CountryRow>(parent, controller, label, value);
|
||||
return object_ptr<CountryRow>(
|
||||
parent,
|
||||
controller,
|
||||
label,
|
||||
maxLabelWidth,
|
||||
value);
|
||||
case Type::Gender:
|
||||
return object_ptr<GenderRow>(parent, label, value);
|
||||
return object_ptr<GenderRow>(
|
||||
parent,
|
||||
label,
|
||||
maxLabelWidth,
|
||||
value);
|
||||
case Type::Date:
|
||||
return object_ptr<DateRow>(parent, label, value);
|
||||
return object_ptr<DateRow>(
|
||||
parent,
|
||||
label,
|
||||
maxLabelWidth,
|
||||
value);
|
||||
default:
|
||||
Unexpected("Type in PanelDetailsRow::Create.");
|
||||
}
|
||||
@@ -981,13 +1011,19 @@ object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
||||
return result;
|
||||
}
|
||||
|
||||
int PanelDetailsRow::LabelWidth(const QString &label) {
|
||||
return st::semiboldFont->width(label);
|
||||
}
|
||||
|
||||
bool PanelDetailsRow::setFocusFast() {
|
||||
return false;
|
||||
}
|
||||
|
||||
int PanelDetailsRow::resizeGetHeight(int newWidth) {
|
||||
const auto padding = st::passportDetailsPadding;
|
||||
const auto inputLeft = padding.left() + st::passportDetailsFieldLeft;
|
||||
const auto inputLeft = padding.left() + std::max(
|
||||
st::passportDetailsFieldLeft,
|
||||
_maxLabelWidth + st::passportDetailsFieldSkipMin);
|
||||
const auto inputTop = st::passportDetailsFieldTop;
|
||||
const auto inputRight = padding.right();
|
||||
const auto inputWidth = std::max(newWidth - inputLeft - inputRight, 0);
|
||||
@@ -997,6 +1033,7 @@ int PanelDetailsRow::resizeGetHeight(int newWidth) {
|
||||
+ (_error ? _error->height() : 0)
|
||||
+ padding.bottom();
|
||||
if (_error) {
|
||||
_error->resizeToWidth(inputWidth);
|
||||
_error->moveToLeft(inputLeft, result - _error->height());
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -37,16 +37,19 @@ public:
|
||||
|
||||
PanelDetailsRow(
|
||||
QWidget *parent,
|
||||
const QString &label);
|
||||
const QString &label,
|
||||
int maxLabelWidth);
|
||||
|
||||
static object_ptr<PanelDetailsRow> Create(
|
||||
QWidget *parent,
|
||||
Type type,
|
||||
not_null<PanelController*> controller,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value,
|
||||
const QString &error,
|
||||
int limit = 0);
|
||||
static int LabelWidth(const QString &label);
|
||||
|
||||
virtual bool setFocusFast();
|
||||
virtual rpl::producer<QString> value() const = 0;
|
||||
@@ -69,6 +72,7 @@ private:
|
||||
void startErrorAnimation(bool shown);
|
||||
|
||||
QString _label;
|
||||
int _maxLabelWidth = 0;
|
||||
object_ptr<Ui::SlideWrap<Ui::FlatLabel>> _error = { nullptr };
|
||||
bool _errorShown = false;
|
||||
bool _errorHideSubscription = false;
|
||||
|
||||
@@ -326,24 +326,42 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
||||
return ValueField();
|
||||
};
|
||||
|
||||
for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
|
||||
const auto &row = _scheme.rows[i];
|
||||
auto fields = (row.valueClass == Scheme::ValueClass::Fields)
|
||||
? &data
|
||||
: scanData;
|
||||
if (!fields) {
|
||||
continue;
|
||||
const auto enumerateRows = [&](auto &&callback) {
|
||||
for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
|
||||
const auto &row = _scheme.rows[i];
|
||||
auto fields = (row.valueClass == Scheme::ValueClass::Fields)
|
||||
? &data
|
||||
: scanData;
|
||||
if (!fields) {
|
||||
continue;
|
||||
}
|
||||
callback(i, row, *fields);
|
||||
}
|
||||
const auto current = valueOrEmpty(*fields, row.key);
|
||||
};
|
||||
auto maxLabelWidth = 0;
|
||||
enumerateRows([&](
|
||||
int i,
|
||||
const EditDocumentScheme::Row &row,
|
||||
const ValueMap &fields) {
|
||||
accumulate_max(
|
||||
maxLabelWidth,
|
||||
PanelDetailsRow::LabelWidth(row.label));
|
||||
});
|
||||
enumerateRows([&](
|
||||
int i,
|
||||
const EditDocumentScheme::Row &row,
|
||||
const ValueMap &fields) {
|
||||
const auto current = valueOrEmpty(fields, row.key);
|
||||
_details.emplace(i, inner->add(PanelDetailsRow::Create(
|
||||
inner,
|
||||
row.inputType,
|
||||
_controller,
|
||||
row.label,
|
||||
maxLabelWidth,
|
||||
current.text,
|
||||
current.error,
|
||||
row.lengthLimit)));
|
||||
}
|
||||
});
|
||||
|
||||
inner->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(inner, st::passportDetailsSkip));
|
||||
|
||||
@@ -326,7 +326,7 @@ void PanelForm::updateControlsGeometry() {
|
||||
_topShadow->moveToLeft(0, 0);
|
||||
_bottomShadow->resizeToWidth(width());
|
||||
_bottomShadow->moveToLeft(0, submitTop - st::lineWidth);
|
||||
_submit->resizeToWidth(width());
|
||||
_submit->setFullWidth(width());
|
||||
_submit->moveToLeft(0, submitTop);
|
||||
|
||||
_scroll->updateBars();
|
||||
|
||||
@@ -199,6 +199,9 @@ bool Get(
|
||||
const QString &filter,
|
||||
Type type,
|
||||
QString startFile) {
|
||||
if (parent) {
|
||||
parent = parent->window();
|
||||
}
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
if (NativeSupported()) {
|
||||
return GetNative(
|
||||
|
||||
@@ -281,7 +281,9 @@ QString RoundButton::computeFullText() const {
|
||||
|
||||
void RoundButton::resizeToText() {
|
||||
int innerWidth = contentWidth();
|
||||
if (_fullWidthOverride < 0) {
|
||||
if (_fullWidthOverride > 0) {
|
||||
resize(_fullWidthOverride, _st.height + _st.padding.top() + _st.padding.bottom());
|
||||
} else if (_fullWidthOverride < 0) {
|
||||
resize(innerWidth - _fullWidthOverride, _st.height + _st.padding.top() + _st.padding.bottom());
|
||||
} else if (_st.width <= 0) {
|
||||
resize(innerWidth - _st.width + _st.padding.left() + _st.padding.right(), _st.height + _st.padding.top() + _st.padding.bottom());
|
||||
|
||||
@@ -272,12 +272,22 @@ public:
|
||||
, _items(_expressions.size()) {
|
||||
}
|
||||
|
||||
void feed(const QString &text, const QString &textTag) {
|
||||
// Here we use the fact that text either contains only emoji
|
||||
// { adjustedTextLength = text.size() * (emojiLength - 1) }
|
||||
// or contains no emoji at all and can have tag edges in the middle
|
||||
// { adjustedTextLength = 0 }.
|
||||
//
|
||||
// Otherwise we would have to pass emoji positions inside text.
|
||||
void feed(
|
||||
const QString &text,
|
||||
int adjustedTextLength,
|
||||
const QString &textTag) {
|
||||
if (!_tags) {
|
||||
return;
|
||||
}
|
||||
const auto guard = gsl::finally([&] {
|
||||
_currentLength += text.size();
|
||||
_currentInternalLength += text.size();
|
||||
_currentAdjustedLength += adjustedTextLength;
|
||||
});
|
||||
if (!textTag.isEmpty()) {
|
||||
finishTags();
|
||||
@@ -290,7 +300,7 @@ public:
|
||||
while (true) {
|
||||
for (; tryFinishTag != _currentFreeTag; ++tryFinishTag) {
|
||||
auto &tag = (*_tags)[tryFinishTag];
|
||||
if (tag.length >= 0) {
|
||||
if (tag.internalLength >= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -298,8 +308,12 @@ public:
|
||||
Assert(i != end(_tagIndices));
|
||||
const auto tagIndex = i->second;
|
||||
|
||||
_items[tagIndex].applyOffset(
|
||||
tag.start + tag.tag.size() + 1 - _currentLength);
|
||||
const auto atLeastOffset =
|
||||
tag.internalStart
|
||||
+ tag.tag.size()
|
||||
+ 1
|
||||
- _currentInternalLength;
|
||||
_items[tagIndex].applyOffset(atLeastOffset);
|
||||
|
||||
fillItem(
|
||||
tagIndex,
|
||||
@@ -311,10 +325,7 @@ public:
|
||||
const auto position = matchPosition(tagIndex, Edge::Close);
|
||||
if (position < kInvalidPosition) {
|
||||
const auto till = position + tag.tag.size();
|
||||
finishTag(
|
||||
tryFinishTag,
|
||||
_currentLength + till,
|
||||
true);
|
||||
finishTag(tryFinishTag, till, true);
|
||||
_items[tagIndex].applyOffset(till);
|
||||
}
|
||||
}
|
||||
@@ -325,9 +336,7 @@ public:
|
||||
if (min < 0) {
|
||||
return;
|
||||
}
|
||||
startTag(
|
||||
_currentLength + matchPosition(min, Edge::Open),
|
||||
_expressions[min].tag);
|
||||
startTag(matchPosition(min, Edge::Open), _expressions[min].tag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,13 +351,18 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void finishTag(int index, int end, bool closed) {
|
||||
void finishTag(int index, int offsetFromAccumulated, bool closed) {
|
||||
Expects(_tags != nullptr);
|
||||
Expects(index >= 0 && index < _tags->size());
|
||||
|
||||
auto &tag = (*_tags)[index];
|
||||
if (tag.length < 0) {
|
||||
tag.length = end - tag.start;
|
||||
if (tag.internalLength < 0) {
|
||||
tag.internalLength = _currentInternalLength
|
||||
+ offsetFromAccumulated
|
||||
- tag.internalStart;
|
||||
tag.adjustedLength = _currentAdjustedLength
|
||||
+ offsetFromAccumulated
|
||||
- tag.adjustedStart;
|
||||
tag.closed = closed;
|
||||
}
|
||||
if (index == _currentTag) {
|
||||
@@ -369,25 +383,33 @@ private:
|
||||
}
|
||||
const auto endPosition = newlinePosition(
|
||||
text,
|
||||
std::max(0, tag.start + 1 - _currentLength));
|
||||
std::max(0, tag.internalStart + 1 - _currentInternalLength));
|
||||
if (matchPosition(tagIndex, Edge::Close) <= endPosition) {
|
||||
return false;
|
||||
}
|
||||
finishTag(index, _currentLength + endPosition, false);
|
||||
finishTag(index, endPosition, false);
|
||||
return true;
|
||||
}
|
||||
void finishTags() {
|
||||
while (_currentTag != _currentFreeTag) {
|
||||
finishTag(_currentTag, _currentLength, false);
|
||||
finishTag(_currentTag, 0, false);
|
||||
}
|
||||
}
|
||||
void startTag(int offset, const QString &tag) {
|
||||
void startTag(int offsetFromAccumulated, const QString &tag) {
|
||||
Expects(_tags != nullptr);
|
||||
|
||||
const auto newTag = InputField::MarkdownTag{
|
||||
_currentInternalLength + offsetFromAccumulated,
|
||||
-1,
|
||||
_currentAdjustedLength + offsetFromAccumulated,
|
||||
-1,
|
||||
false,
|
||||
tag
|
||||
};
|
||||
if (_currentFreeTag < _tags->size()) {
|
||||
(*_tags)[_currentFreeTag] = { offset, -1, false, tag };
|
||||
(*_tags)[_currentFreeTag] = newTag;
|
||||
} else {
|
||||
_tags->push_back({ offset, -1, false, tag });
|
||||
_tags->push_back(newTag);
|
||||
}
|
||||
++_currentFreeTag;
|
||||
}
|
||||
@@ -447,7 +469,8 @@ private:
|
||||
|
||||
int _currentTag = 0;
|
||||
int _currentFreeTag = 0;
|
||||
int _currentLength = 0;
|
||||
int _currentInternalLength = 0;
|
||||
int _currentAdjustedLength = 0;
|
||||
|
||||
};
|
||||
|
||||
@@ -1246,6 +1269,16 @@ void InputField::updatePalette() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cursor = textCursor();
|
||||
if (!cursor.hasSelection()) {
|
||||
auto format = cursor.charFormat();
|
||||
format.merge(PrepareTagFormat(
|
||||
_st,
|
||||
format.property(kTagProperty).toString()));
|
||||
cursor.setCharFormat(format);
|
||||
setTextCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void InputField::onTouchTimer() {
|
||||
@@ -1752,11 +1785,11 @@ QString InputField::getTextPart(
|
||||
if (full || !text.isEmpty()) {
|
||||
lastTag = format.property(kTagProperty).toString();
|
||||
tagAccumulator.feed(lastTag, result.size());
|
||||
markdownTagAccumulator.feed(text, lastTag);
|
||||
}
|
||||
|
||||
auto begin = text.data();
|
||||
auto ch = begin;
|
||||
auto adjustedLength = text.size();
|
||||
for (const auto end = begin + text.size(); ch != end; ++ch) {
|
||||
if (IsNewline(*ch) && ch->unicode() != '\r') {
|
||||
*ch = QLatin1Char('\n');
|
||||
@@ -1768,6 +1801,7 @@ QString InputField::getTextPart(
|
||||
if (ch > begin) {
|
||||
result.append(begin, ch - begin);
|
||||
}
|
||||
adjustedLength += (emojiText.size() - 1);
|
||||
if (!emojiText.isEmpty()) {
|
||||
result.append(emojiText);
|
||||
}
|
||||
@@ -1778,12 +1812,16 @@ QString InputField::getTextPart(
|
||||
if (ch > begin) {
|
||||
result.append(begin, ch - begin);
|
||||
}
|
||||
|
||||
if (full || !text.isEmpty()) {
|
||||
markdownTagAccumulator.feed(text, adjustedLength, lastTag);
|
||||
}
|
||||
}
|
||||
|
||||
block = block.next();
|
||||
if (block != till) {
|
||||
result.append('\n');
|
||||
markdownTagAccumulator.feed(newline, lastTag);
|
||||
markdownTagAccumulator.feed(newline, 1, lastTag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2141,14 +2179,17 @@ void InputField::highlightMarkdown() {
|
||||
from = b;
|
||||
};
|
||||
for (const auto &tag : _lastMarkdownTags) {
|
||||
if (tag.start > from) {
|
||||
applyColor(from, tag.start, QColor(0, 0, 0));
|
||||
} else if (tag.start < from) {
|
||||
if (tag.internalStart > from) {
|
||||
applyColor(from, tag.internalStart, QColor(0, 0, 0));
|
||||
} else if (tag.internalStart < from) {
|
||||
continue;
|
||||
}
|
||||
applyColor(tag.start, tag.start + tag.length, tag.closed
|
||||
? QColor(0, 128, 0)
|
||||
: QColor(128, 0, 0));
|
||||
applyColor(
|
||||
tag.internalStart,
|
||||
tag.internalStart + tag.internalLength,
|
||||
(tag.closed
|
||||
? QColor(0, 128, 0)
|
||||
: QColor(128, 0, 0)));
|
||||
}
|
||||
auto cursor = textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
@@ -2342,36 +2383,38 @@ TextWithTags InputField::getTextWithAppliedMarkdown() const {
|
||||
const auto linksEnd = links.end();
|
||||
for (const auto &tag : _lastMarkdownTags) {
|
||||
const auto tagLength = int(tag.tag.size());
|
||||
if (!tag.closed || tag.start < from) {
|
||||
if (!tag.closed || tag.adjustedStart < from) {
|
||||
continue;
|
||||
}
|
||||
const auto entityLength = tag.length - 2 * tagLength;
|
||||
const auto entityLength = tag.adjustedLength - 2 * tagLength;
|
||||
if (entityLength <= 0) {
|
||||
continue;
|
||||
}
|
||||
addOriginalTagsUpTill(tag.start);
|
||||
addOriginalTagsUpTill(tag.adjustedStart);
|
||||
const auto tagAdjustedEnd = tag.adjustedStart + tag.adjustedLength;
|
||||
if (originalTag != originalTagsEnd
|
||||
&& originalTag->offset < tag.start + tag.length) {
|
||||
&& originalTag->offset < tagAdjustedEnd) {
|
||||
continue;
|
||||
}
|
||||
while (link != linksEnd
|
||||
&& link->offset() + link->length() <= tag.start) {
|
||||
&& link->offset() + link->length() <= tag.adjustedStart) {
|
||||
++link;
|
||||
}
|
||||
if (link != linksEnd
|
||||
&& link->offset() < tag.start + tag.length
|
||||
&& (link->offset() + link->length() > tag.start + tag.length
|
||||
|| link->offset() < tag.start)) {
|
||||
&& link->offset() < tagAdjustedEnd
|
||||
&& (link->offset() + link->length() > tagAdjustedEnd
|
||||
|| link->offset() < tag.adjustedStart)) {
|
||||
continue;
|
||||
}
|
||||
addOriginalTextUpTill(tag.start);
|
||||
addOriginalTextUpTill(tag.adjustedStart);
|
||||
result.tags.push_back(TextWithTags::Tag{
|
||||
int(result.text.size()),
|
||||
entityLength,
|
||||
tag.tag });
|
||||
result.text.append(
|
||||
originalText.midRef(tag.start + tagLength, entityLength));
|
||||
from = tag.start + tag.length;
|
||||
result.text.append(originalText.midRef(
|
||||
tag.adjustedStart + tagLength,
|
||||
entityLength));
|
||||
from = tag.adjustedStart + tag.adjustedLength;
|
||||
removed += 2 * tagLength;
|
||||
}
|
||||
addOriginalTagsUpTill(originalText.size());
|
||||
@@ -2739,8 +2782,8 @@ void InputField::processInstantReplaces(const QString &appended) {
|
||||
}
|
||||
const auto position = textCursor().position();
|
||||
for (const auto &tag : _lastMarkdownTags) {
|
||||
if (tag.start < position
|
||||
&& tag.start + tag.length >= position
|
||||
if (tag.internalStart < position
|
||||
&& tag.internalStart + tag.internalLength >= position
|
||||
&& (tag.tag == kTagCode || tag.tag == kTagPre)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,8 +125,14 @@ public:
|
||||
using TagList = TextWithTags::Tags;
|
||||
|
||||
struct MarkdownTag {
|
||||
int start = 0;
|
||||
int length = 0;
|
||||
// With each emoji being QChar::ObjectReplacementCharacter.
|
||||
int internalStart = 0;
|
||||
int internalLength = 0;
|
||||
|
||||
// Adjusted by emoji to match _lastTextWithTags.
|
||||
int adjustedStart = 0;
|
||||
int adjustedLength = 0;
|
||||
|
||||
bool closed = false;
|
||||
QString tag;
|
||||
};
|
||||
|
||||
@@ -77,7 +77,10 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
||||
CGEventMaskBit(NX_SYSDEFINED),
|
||||
tapEventCallback,
|
||||
self);
|
||||
assert(_eventPort != NULL);
|
||||
if (!_eventPort) {
|
||||
[self stopWatchingMediaKeys];
|
||||
return;
|
||||
}
|
||||
|
||||
_eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0);
|
||||
assert(_eventPortSource != NULL);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
AppVersion 1003003
|
||||
AppVersion 1003007
|
||||
AppVersionStrMajor 1.3
|
||||
AppVersionStrSmall 1.3.3
|
||||
AppVersionStr 1.3.3
|
||||
AlphaChannel 1
|
||||
AppVersionStrSmall 1.3.7
|
||||
AppVersionStr 1.3.7
|
||||
AlphaChannel 0
|
||||
BetaVersion 0
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
1.3.7 (11.06.18)
|
||||
|
||||
- Push fixes to stable version.
|
||||
|
||||
1.3.6 alpha (11.06.18)
|
||||
|
||||
- Bug fixes and other minor improvements.
|
||||
|
||||
1.3.5 alpha (08.06.18)
|
||||
|
||||
- Bug fixes and other minor improvements.
|
||||
|
||||
1.3.4 alpha (07.06.18)
|
||||
|
||||
- Bug fixes and other minor improvements.
|
||||
|
||||
1.3.3 alpha (07.06.18)
|
||||
|
||||
- Bug fixes and other minor improvements.
|
||||
|
||||
Reference in New Issue
Block a user