Merge remote-tracking branch 'tdesktop/dev' into dev

This commit is contained in:
Eric Kotato 2020-01-28 22:06:59 +03:00
commit 5b9bc084f9
31 changed files with 668 additions and 1000 deletions

View file

@ -26,7 +26,7 @@ jobs:
let body = context.payload.issue.body;
console.log("Body of issue:\n" + body);
let index1 = body.indexOf(item1) + item1.length;
let index1 = body.indexOf(item1);
let index2 = body.indexOf(item2);
index2 = (index2 == -1) ? Number.MAX_SAFE_INTEGER : index2;
@ -46,7 +46,7 @@ jobs:
return version[0].split(".")[0];
}
let issueVer = parseVersion(body.substring(index1, index2));
let issueVer = parseVersion(body.substring(index1 + item1.length, index2));
if (issueVer == undefined) {
console.log(errorStr);

99
.github/workflows/snap.yml vendored Normal file
View file

@ -0,0 +1,99 @@
name: Snap.
on:
push:
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
jobs:
linux:
name: Ubuntu 18.04
runs-on: ubuntu-18.04
env:
UPLOAD_ARTIFACT: "false"
ONLY_CACHE: "false"
MANUAL_CACHING: "3"
steps:
- name: Clone.
uses: actions/checkout@v1
with:
submodules: recursive
- name: First set up.
run: |
sudo apt-get update
sudo apt-get install gcc-8 g++-8 -y
sudo snap install --classic snapcraft
# Workaround for snapcraft
# See https://forum.snapcraft.io/t/permissions-problem-using-snapcraft-in-azure-pipelines/13258
sudo chown root:root /
snapcraft --version > CACHE_KEY.txt
gcc-8 --version >> CACHE_KEY.txt
echo $MANUAL_CACHING >> CACHE_KEY.txt
md5cache=$(md5sum CACHE_KEY.txt | cut -c -32)
echo ::set-env name=CACHE_KEY::$md5cache
awk -v RS="" -v ORS="\n\n" '/^ cmake:/' snap/snapcraft.yaml > CMAKE_CACHE_KEY.txt
md5cache=$(md5sum CMAKE_CACHE_KEY.txt | cut -c -32)
echo ::set-env name=CMAKE_CACHE_KEY::$md5cache
awk -v RS="" -v ORS="\n\n" '/^ enchant:/' snap/snapcraft.yaml > ENCHANT_CACHE_KEY.txt
md5cache=$(md5sum ENCHANT_CACHE_KEY.txt | cut -c -32)
echo ::set-env name=ENCHANT_CACHE_KEY::$md5cache
- name: CMake cache.
id: cache-cmake
uses: actions/cache@v1
with:
path: parts/cmake
key: ${{ runner.OS }}-cmake-${{ env.CACHE_KEY }}-${{ env.CMAKE_CACHE_KEY }}
- name: CMake build.
if: steps.cache-cmake.outputs.cache-hit != 'true'
run: snapcraft build --destructive-mode cmake
- name: Enchant cache.
id: cache-enchant
uses: actions/cache@v1
with:
path: parts/enchant
key: ${{ runner.OS }}-enchant-${{ env.CACHE_KEY }}-${{ env.ENCHANT_CACHE_KEY }}
- name: Enchant build.
if: steps.cache-enchant.outputs.cache-hit != 'true'
run: snapcraft build --destructive-mode enchant
- name: Telegram Desktop snap build.
if: env.ONLY_CACHE == 'false'
run: snapcraft --destructive-mode
- name: Move artifact.
if: env.UPLOAD_ARTIFACT == 'true'
run: |
artifact_name=$(echo telegram-desktop_*.snap)
echo ::set-env name=ARTIFACT_NAME::$artifact_name
mkdir artifact
mv $artifact_name artifact
- uses: actions/upload-artifact@master
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:
name: ${{ env.ARTIFACT_NAME }}
path: artifact
- name: Remove unneeded directories for cache.
run: |
rm -rf parts/{cmake,enchant}/{build,src,ubuntu}
rm -rf parts/{cmake,enchant}/state/{stage,prime}

View file

@ -20,6 +20,7 @@ add_subdirectory(lib_lottie)
add_subdirectory(lib_qr)
add_subdirectory(codegen)
include(CheckCXXSourceCompiles)
include(lib_ui/cmake/generate_styles.cmake)
include(cmake/generate_lang.cmake)
include(cmake/generate_numbers.cmake)
@ -105,6 +106,16 @@ if (NOT DESKTOP_APP_USE_PACKAGED)
target_link_libraries(Telegram PRIVATE desktop-app::external_opus)
endif()
# Telegram uses long atomic types, so on some architectures libatomic is needed.
check_cxx_source_compiles("
#include <atomic>
std::atomic_int64_t foo;
int main() {return foo;}
" HAVE_LONG_ATOMIC_WITHOUT_LIB)
if (NOT HAVE_LONG_ATOMIC_WITHOUT_LIB)
target_link_libraries(Telegram PRIVATE atomic)
endif()
target_precompile_headers(Telegram PRIVATE ${src_loc}/stdafx.h)
nice_target_sources(Telegram ${src_loc}
PRIVATE

View file

@ -1268,6 +1268,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_cant_invite_make_admin" = "Make admin";
"lng_send_button" = "Send";
"lng_schedule_button" = "Schedule";
"lng_send_silent_message" = "Send without sound";
"lng_schedule_message" = "Schedule message";
"lng_reminder_message" = "Set a reminder";
@ -2199,11 +2200,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_polls_create_limit#other" = "You can add {count} more options.";
"lng_polls_create_maximum" = "You have added the maximum number of options.";
"lng_polls_create_settings" = "Settings";
"lng_polls_create_hint" = "Tap to select the right option";
"lng_polls_create_anonymous" = "Anonymous Votes";
"lng_polls_create_multiple_choice" = "Multiple Answers";
"lng_polls_create_quiz_mode" = "Quiz Mode";
"lng_polls_create_button" = "Create";
"lng_polls_create_one_answer" = "Quiz has only one right answer.";
"lng_polls_choose_question" = "Please enter a question.";
"lng_polls_choose_answers" = "Please enter at least two options.";
"lng_polls_choose_correct" = "Please choose the correct answer.";

View file

@ -60,5 +60,6 @@
</qresource>
<qresource prefix="/ktg_lang">
<file alias="ru.json">../../langs/rewrites/ru.json</file>
<file alias="kotatogramdesktop.desktop">../../../../lib/xdg/kotatogramdesktop.desktop</file>
</qresource>
</RCC>

View file

@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="1.9.8.0" />
Version="1.9.9.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>

View file

@ -579,6 +579,13 @@ void ApiWrap::sendMessageFail(
requestFullPeer(peer);
}
}
} else if (error.type() == qstr("SCHEDULE_STATUS_PRIVATE")) {
auto &scheduled = _session->data().scheduledMessages();
Assert(peer->isUser());
if (const auto item = scheduled.lookupItem(peer->id, itemId.msg)) {
scheduled.removeSending(item);
Ui::show(Box<InformBox>(tr::lng_cant_do_this(tr::now)));
}
}
if (const auto item = _session->data().message(itemId)) {
Assert(randomId != 0);
@ -3652,10 +3659,22 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) {
auto &d = update.c_updateNewMessage();
auto needToAdd = true;
if (d.vmessage().type() == mtpc_message) { // index forwarded messages to links _overview
if (_session->data().checkEntitiesAndViewsUpdate(d.vmessage().c_message())) { // already in blocks
const auto &data = d.vmessage().c_message();
if (_session->data().checkEntitiesAndViewsUpdate(data)) { // already in blocks
LOG(("Skipping message, because it is already in blocks!"));
needToAdd = false;
}
if (needToAdd && !data.is_from_scheduled()) {
// If we still need to add a new message,
// we should first check if this message is in
// the list of scheduled messages.
// This is necessary to correctly update the file reference.
// Note that when a message is scheduled until online
// while the recipient is already online, the server sends
// an ordinary new message with skipped "from_scheduled" flag.
_session->data().scheduledMessages().checkEntitiesAndUpdate(
data);
}
}
if (needToAdd) {
_session->data().addNewMessage(

View file

@ -873,7 +873,7 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
) | rpl::filter([=](not_null<QEvent*> e) {
return (e->type() == QEvent::MouseButtonPress) && quiz->checked();
}) | rpl::start_with_next([=] {
Ui::Toast::Show("Quiz has only one right answer.");
Ui::Toast::Show(tr::lng_polls_create_one_answer(tr::now));
}, multiple->lifetime());
}

View file

@ -1832,7 +1832,11 @@ void SendFilesBox::setupShadows(
}
void SendFilesBox::prepare() {
_send = addButton(tr::lng_send_button(), [=] { send({}); });
_send = addButton(
(_sendType == Api::SendType::Normal
? tr::lng_send_button()
: tr::lng_schedule_button()),
[=] { send({}); });
if (_sendType == Api::SendType::Normal) {
SetupSendMenuAndShortcuts(
_send,

View file

@ -15,8 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#define TDESKTOP_ALPHA_VERSION (0ULL)
#endif // TDESKTOP_ALLOW_CLOSED_ALPHA
constexpr auto AppVersion = 1009008;
constexpr auto AppVersionStr = "1.9.8";
constexpr auto AppVersion = 1009009;
constexpr auto AppVersionStr = "1.9.9";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
constexpr auto AppKotatoVersion = 1001005;

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "history/history.h"
#include "history/history_item_components.h"
#include "history/history_message.h"
#include "apiwrap.h"
namespace Data {
@ -111,11 +112,82 @@ MsgId ScheduledMessages::lookupId(not_null<HistoryItem*> item) const {
return j->second;
}
HistoryItem *ScheduledMessages::lookupItem(PeerId peer, MsgId msg) const {
const auto history = _session->data().historyLoaded(peer);
if (!history) {
return nullptr;
}
const auto i = _data.find(history);
if (i == end(_data)) {
return nullptr;
}
const auto &items = i->second.items;
const auto j = ranges::find_if(items, [&](auto &item) {
return item->id == msg;
});
if (j == end(items)) {
return nullptr;
}
return (*j).get();
}
int ScheduledMessages::count(not_null<History*> history) const {
const auto i = _data.find(history);
return (i != end(_data)) ? i->second.items.size() : 0;
}
void ScheduledMessages::sendNowSimpleMessage(
const MTPDupdateShortSentMessage &update,
not_null<HistoryItem*> local) {
Expects(local->isSending());
Expects(local->isScheduled());
Expects(local->date() == kScheduledUntilOnlineTimestamp);
// When the user sends a text message scheduled until online
// while the recipient is already online, the server sends
// updateShortSentMessage to the client and the client calls this method.
// Since such messages can only be sent to recipients,
// we know for sure that a message can't have fields such as the author,
// views count, etc.
const auto &history = local->history();
auto flags = NewMessageFlags(history->peer)
| MTPDmessage::Flag::f_entities
| MTPDmessage::Flag::f_from_id
| (local->replyToId()
? MTPDmessage::Flag::f_reply_to_msg_id
: MTPDmessage::Flag(0));
auto clientFlags = NewMessageClientFlags()
| MTPDmessage_ClientFlag::f_local_history_entry;
history->addNewMessage(
MTP_message(
MTP_flags(flags),
update.vid(),
MTP_int(_session->userId()),
peerToMTP(history->peer->id),
MTPMessageFwdHeader(),
MTPint(),
MTP_int(local->replyToId()),
update.vdate(),
MTP_string(local->originalText().text),
MTP_messageMediaEmpty(),
MTPReplyMarkup(),
Api::EntitiesToMTP(local->originalText().entities),
MTP_int(1),
MTPint(),
MTP_string(),
MTPlong(),
//MTPMessageReactions(),
MTPVector<MTPRestrictionReason>()),
clientFlags,
NewMessageType::Unread);
local->destroy();
}
void ScheduledMessages::apply(const MTPDupdateNewScheduledMessage &update) {
const auto &message = update.vmessage();
const auto peer = PeerFromMessage(message);
@ -132,6 +204,45 @@ void ScheduledMessages::apply(const MTPDupdateNewScheduledMessage &update) {
_updates.fire_copy(history);
}
void ScheduledMessages::checkEntitiesAndUpdate(const MTPDmessage &data) {
// When the user sends a message with a media scheduled until online
// while the recipient is already online, the server sends
// updateNewMessage to the client and the client calls this method.
const auto peer = peerFromMTP(data.vto_id());
if (!peerIsUser(peer)) {
return;
}
const auto history = _session->data().historyLoaded(peer);
if (!history) {
return;
}
const auto i = _data.find(history);
if (i == end(_data)) {
return;
}
const auto &itemMap = i->second.itemById;
const auto j = itemMap.find(data.vid().v);
if (j == end(itemMap)) {
return;
}
const auto existing = j->second;
Assert(existing->date() == kScheduledUntilOnlineTimestamp);
existing->updateSentContent({
qs(data.vmessage()),
Api::EntitiesFromMTP(data.ventities().value_or_empty())
}, data.vmedia());
existing->updateReplyMarkup(data.vreply_markup());
existing->updateForwardedInfo(data.vfwd_from());
_session->data().requestItemTextRefresh(existing);
existing->destroy();
}
void ScheduledMessages::apply(
const MTPDupdateDeleteScheduledMessages &update) {
const auto peer = peerFromMTP(update.vpeer());

View file

@ -29,8 +29,10 @@ public:
~ScheduledMessages();
[[nodiscard]] MsgId lookupId(not_null<HistoryItem*> item) const;
[[nodiscard]] HistoryItem *lookupItem(PeerId peer, MsgId msg) const;
[[nodiscard]] int count(not_null<History*> history) const;
void checkEntitiesAndUpdate(const MTPDmessage &data);
void apply(const MTPDupdateNewScheduledMessage &update);
void apply(const MTPDupdateDeleteScheduledMessages &update);
void apply(
@ -40,6 +42,10 @@ public:
void appendSending(not_null<HistoryItem*> item);
void removeSending(not_null<HistoryItem*> item);
void sendNowSimpleMessage(
const MTPDupdateShortSentMessage &update,
not_null<HistoryItem*> local);
[[nodiscard]] rpl::producer<> updates(not_null<History*> history);
[[nodiscard]] Data::MessagesSlice list(not_null<History*> history);

View file

@ -675,16 +675,17 @@ HistoryWidget::HistoryWidget(
) | rpl::filter([=](const Api::SendAction &action) {
return (action.history == _history);
}) | rpl::start_with_next([=](const Api::SendAction &action) {
const auto lastKeyboardUsed = lastForceReplyReplied(FullMsgId(
action.history->channelId(),
action.replyTo));
if (action.options.scheduled) {
cancelReply(lastKeyboardUsed);
crl::on_main(this, [=, history = action.history]{
controller->showSection(
HistoryView::ScheduledMemento(history));
});
} else {
fastShowAtEnd(action.history);
const auto lastKeyboardUsed = lastForceReplyReplied(FullMsgId(
action.history->channelId(),
action.replyTo));
if (cancelReply(lastKeyboardUsed) && !action.clearDraft) {
onCloudDraftSave();
}

View file

@ -688,7 +688,7 @@ void ScheduleBox(
}, timeInput->lifetime());
box->setFocusCallback([=] { timeInput->setFocusFast(); });
const auto submit = box->addButton(tr::lng_settings_save(), [=] {
const auto submit = box->addButton(tr::lng_schedule_button(), [=] {
save(false);
});
SetupSendMenuAndShortcuts(

View file

@ -3807,14 +3807,19 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
if (!IsServerMsgId(d.vid().v)) {
LOG(("API Error: Bad msgId got from server: %1").arg(d.vid().v));
} else if (randomId) {
const auto sent = session().data().messageSentData(randomId);
auto &owner = session().data();
const auto sent = owner.messageSentData(randomId);
const auto lookupMessage = [&] {
return sent.peerId
? session().data().message(
peerToChannel(sent.peerId),
d.vid().v)
? owner.message(peerToChannel(sent.peerId), d.vid().v)
: nullptr;
};
if (const auto id = owner.messageIdByRandomId(randomId)) {
if (const auto local = owner.message(id);
local->isScheduled()) {
owner.scheduledMessages().sendNowSimpleMessage(d, local);
}
}
const auto wasAlready = (lookupMessage() != nullptr);
feedUpdate(MTP_updateMessageID(d.vid(), MTP_long(randomId))); // ignore real date
if (const auto item = lookupMessage()) {

View file

@ -234,7 +234,9 @@ void Panel::refreshList() {
: nullptr;
const auto media = item ? item->media() : nullptr;
const auto document = media ? media->document() : nullptr;
if (!document || !document->isSharedMediaMusic()) {
if (!document
|| !document->isSharedMediaMusic()
|| !IsServerMsgId(item->id)) {
return nullptr;
}
const auto result = item->history()->peer;

View file

@ -29,6 +29,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Platform {
namespace {
constexpr auto kLauncherBasename = str_const(MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME) ".desktop");
bool noQtTrayIcon = false, tryAppIndicator = false;
bool useGtkBase = false, useAppIndicator = false, useStatusIcon = false, trayIconChecked = false, useUnityCount = false;
@ -547,7 +549,7 @@ void MainWindow::psFirstShow() {
auto snapName = QString::fromLatin1(qgetenv("SNAP_NAME"));
if(snapName.isEmpty()) {
std::vector<QString> possibleDesktopFiles = {
MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME) ".desktop",
str_const_toString(kLauncherBasename),
"Kotatogram.desktop"
};
@ -563,8 +565,12 @@ void MainWindow::psFirstShow() {
LOG(("Could not get Unity Launcher entry!"));
}
} else {
LOG(("SNAP Environment detected, setting Launcher entry to %1-kotatogramdesktop.desktop!").arg(snapName));
_desktopFile = snapName + "_kotatogramdesktop.desktop";
LOG(("SNAP Environment detected, setting Launcher entry to %1_%2.desktop!")
.arg(snapName)
.arg(str_const_toString(kLauncherBasename)));
_desktopFile = snapName
+ '_'
+ str_const_toString(kLauncherBasename);
useUnityCount=true;
}
_dbusPath = "/com/canonical/unity/launcherentry/" + QString::number(djbStringHash("application://" + _desktopFile));
@ -573,25 +579,24 @@ void MainWindow::psFirstShow() {
}
#endif
bool showShadows = true;
show();
//_private.enableShadow(winId());
if (cWindowPos().maximized) {
DEBUG_LOG(("Window Pos: First show, setting maximized."));
setWindowState(Qt::WindowMaximized);
}
if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) {
setWindowState(Qt::WindowMinimized);
if (Global::WorkMode().value() == dbiwmTrayOnly || Global::WorkMode().value() == dbiwmWindowAndTray) {
hide();
} else {
show();
}
showShadows = false;
} else {
show();
// If I call hide() synchronously here after show() then on Ubuntu 14.04
// it will show a window frame with transparent window body, without content.
// And to be able to "Show from tray" one more hide() will be required.
crl::on_main(this, [=] {
setWindowState(Qt::WindowMinimized);
if (Global::WorkMode().value() == dbiwmTrayOnly || Global::WorkMode().value() == dbiwmWindowAndTray) {
hide();
} else {
show();
}
});
}
setPositionInited();

View file

@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
#include <QtCore/QVersionNumber>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusReply>
#include <QtDBus/QDBusMetaType>
#endif
@ -31,7 +30,7 @@ constexpr auto kInterface = kService;
std::vector<QString> GetServerInformation(
const std::shared_ptr<QDBusInterface> &notificationInterface) {
std::vector<QString> serverInformation;
auto serverInformationReply = notificationInterface
const auto serverInformationReply = notificationInterface
->call(qsl("GetServerInformation"));
if (serverInformationReply.type() == QDBusMessage::ReplyMessage) {
@ -58,7 +57,7 @@ std::vector<QString> GetServerInformation(
QStringList GetCapabilities(
const std::shared_ptr<QDBusInterface> &notificationInterface) {
QDBusReply<QStringList> capabilitiesReply = notificationInterface
const QDBusReply<QStringList> capabilitiesReply = notificationInterface
->call(qsl("GetCapabilities"));
if (capabilitiesReply.isValid()) {
@ -95,7 +94,7 @@ NotificationData::NotificationData(
, _title(title)
, _peerId(peerId)
, _msgId(msgId) {
auto capabilities = GetCapabilities(_notificationInterface);
const auto capabilities = GetCapabilities(_notificationInterface);
if (capabilities.contains(qsl("body-markup"))) {
_body = subtitle.isEmpty()
@ -112,17 +111,25 @@ NotificationData::NotificationData(
if (capabilities.contains(qsl("actions"))) {
_actions << qsl("default") << QString();
connect(_notificationInterface.get(),
SIGNAL(ActionInvoked(uint, QString)),
this, SLOT(notificationClicked(uint)));
_notificationInterface->connection().connect(
str_const_toString(kService),
str_const_toString(kObjectPath),
str_const_toString(kInterface),
qsl("ActionInvoked"),
this,
SLOT(notificationClicked(uint)));
if (capabilities.contains(qsl("inline-reply"))) {
_actions << qsl("inline-reply")
<< tr::lng_notification_reply(tr::now);
connect(_notificationInterface.get(),
SIGNAL(NotificationReplied(uint,QString)),
this, SLOT(notificationReplied(uint,QString)));
_notificationInterface->connection().connect(
str_const_toString(kService),
str_const_toString(kObjectPath),
str_const_toString(kInterface),
qsl("NotificationReplied"),
this,
SLOT(notificationReplied(uint,QString)));
} else {
// icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
_actions << qsl("mail-reply-sender")
@ -153,15 +160,26 @@ NotificationData::NotificationData(
_hints["desktop-entry"] =
qsl(MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME));
connect(_notificationInterface.get(),
SIGNAL(NotificationClosed(uint, uint)),
this, SLOT(notificationClosed(uint)));
_notificationInterface->connection().connect(
str_const_toString(kService),
str_const_toString(kObjectPath),
str_const_toString(kInterface),
qsl("NotificationClosed"),
this,
SLOT(notificationClosed(uint)));
}
bool NotificationData::show() {
QDBusReply<uint> notifyReply = _notificationInterface->call(qsl("Notify"),
str_const_toString(AppName), uint(0), QString(), _title, _body,
_actions, _hints, -1);
const QDBusReply<uint> notifyReply = _notificationInterface->call(
qsl("Notify"),
str_const_toString(AppName),
uint(0),
QString(),
_title,
_body,
_actions,
_hints,
-1);
if (notifyReply.isValid()) {
_notificationId = notifyReply.value();
@ -174,7 +192,7 @@ bool NotificationData::show() {
}
bool NotificationData::close() {
QDBusReply<void> closeReply = _notificationInterface
const QDBusReply<void> closeReply = _notificationInterface
->call(qsl("CloseNotification"), _notificationId);
if (!closeReply.isValid()) {
@ -186,7 +204,7 @@ bool NotificationData::close() {
}
void NotificationData::setImage(const QString &imagePath) {
auto specificationVersion = ParseSpecificationVersion(
const auto specificationVersion = ParseSpecificationVersion(
GetServerInformation(_notificationInterface));
QString imageKey;
@ -211,9 +229,16 @@ void NotificationData::setImage(const QString &imagePath) {
return;
}
auto image = QImage(imagePath).convertToFormat(QImage::Format_RGBA8888);
QByteArray imageBytes((const char*)image.constBits(),
const auto image = QImage(imagePath)
.convertToFormat(QImage::Format_RGBA8888);
const QByteArray imageBytes(
(const char*)image.constBits(),
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
image.byteCount());
#else
image.sizeInBytes());
#endif
ImageData imageData;
imageData.width = image.width();
@ -285,7 +310,7 @@ const QDBusArgument &operator>>(const QDBusArgument &argument,
bool Supported() {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
static auto Available = QDBusInterface(
static const auto Available = QDBusInterface(
str_const_toString(kService),
str_const_toString(kObjectPath),
str_const_toString(kInterface)).isValid();
@ -316,10 +341,10 @@ Manager::Private::Private(Manager *manager, Type type)
str_const_toString(kInterface))) {
qDBusRegisterMetaType<NotificationData::ImageData>();
auto specificationVersion = ParseSpecificationVersion(
const auto specificationVersion = ParseSpecificationVersion(
GetServerInformation(_notificationInterface));
auto capabilities = GetCapabilities(_notificationInterface);
const auto capabilities = GetCapabilities(_notificationInterface);
if (!specificationVersion.isNull()) {
LOG(("Notification daemon specification version: %1")

View file

@ -24,6 +24,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QProcess>
#include <QtCore/QVersionNumber>
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
#include <QtDBus/QDBusInterface>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <cstdlib>
@ -38,6 +42,27 @@ using Platform::File::internal::EscapeShell;
namespace {
constexpr auto kDesktopFile = str_const(":/misc/kotatogramdesktop.desktop");
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
void SandboxAutostart(bool autostart) {
QVariantMap options;
options["reason"] = tr::lng_settings_auto_start(tr::now);
options["autostart"] = autostart;
options["commandline"] = QStringList({
cExeName(),
qsl("-autostart")
});
options["dbus-activatable"] = false;
QDBusInterface(
qsl("org.freedesktop.portal.Desktop"),
qsl("/org/freedesktop/portal/desktop"),
qsl("/org/freedesktop/portal/desktop")
).call(qsl("RequestBackground"), QString(), options);
}
#endif
bool RunShellCommand(const QByteArray &command) {
auto result = system(command.constData());
if (result) {
@ -87,6 +112,61 @@ void FallbackFontConfig() {
#endif // !DESKTOP_APP_USE_PACKAGED
}
bool GenerateDesktopFile(const QString &targetPath, const QString &args) {
DEBUG_LOG(("App Info: placing .desktop file to %1").arg(targetPath));
if (!QDir(targetPath).exists()) QDir().mkpath(targetPath);
const auto targetFile = targetPath
+ qsl(MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME) ".desktop");
QString fileText;
QFile source(str_const_toString(kDesktopFile));
if (source.open(QIODevice::ReadOnly)) {
QTextStream s(&source);
fileText = s.readAll();
source.close();
} else {
LOG(("App Error: Could not open '%1' for read")
.arg(str_const_toString(kDesktopFile)));
return false;
}
QFile target(targetFile);
if (target.open(QIODevice::WriteOnly)) {
#ifdef DESKTOP_APP_USE_PACKAGED
fileText = fileText.replace(
QRegularExpression(qsl("^Exec=(.*) -- %u$"),
QRegularExpression::MultilineOption),
qsl("Exec=\\1")
+ (args.isEmpty() ? QString() : ' ' + args));
#else
fileText = fileText.replace(
QRegularExpression(qsl("^TryExec=.*$"),
QRegularExpression::MultilineOption),
qsl("TryExec=")
+ EscapeShell(QFile::encodeName(cExeDir() + cExeName())));
fileText = fileText.replace(
QRegularExpression(qsl("^Exec=.*$"),
QRegularExpression::MultilineOption),
qsl("Exec=")
+ EscapeShell(QFile::encodeName(cExeDir() + cExeName()))
+ (args.isEmpty() ? QString() : ' ' + args));
#endif
target.write(fileText.toUtf8());
target.close();
DEBUG_LOG(("App Info: removing old .desktop file"));
QFile(qsl("%1kotatogram.desktop").arg(targetPath)).remove();
return true;
} else {
LOG(("App Error: Could not open '%1' for write").arg(targetFile));
return false;
}
}
} // namespace
namespace Platform {
@ -98,7 +178,7 @@ void SetApplicationIcon(const QIcon &icon) {
bool InSandbox() {
static const auto Sandbox = QFileInfo::exists(
QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation)
+ qsl("/flatpak-info"));
+ qsl("/flatpak-info"));
return Sandbox;
}
@ -120,6 +200,8 @@ QString CurrentExecutablePath(int argc, char *argv[]) {
}
QString SingleInstanceLocalServerName(const QString &hash) {
const auto isSnap = !qgetenv("SNAP").isEmpty();
const auto runtimeDir = QStandardPaths::writableLocation(
QStandardPaths::RuntimeLocation);
@ -128,6 +210,8 @@ QString SingleInstanceLocalServerName(const QString &hash) {
+ qsl("/app/")
+ QString::fromUtf8(qgetenv("FLATPAK_ID"))
+ '/' + hash;
} else if (QFileInfo::exists(runtimeDir) && isSnap) {
return runtimeDir + '/' + hash;
} else if (QFileInfo::exists(runtimeDir)) {
return runtimeDir + '/' + hash + '-' + cGUIDStr();
} else { // non-systemd distros
@ -258,104 +342,45 @@ void finish() {
void RegisterCustomScheme() {
#ifndef TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME
auto home = getHomeDir();
if (home.isEmpty() || cAlphaVersion() || cExeName().isEmpty()) return; // don't update desktop file for alpha version
if (home.isEmpty() || cAlphaVersion() || cExeName().isEmpty())
return; // don't update desktop file for alpha version
if (Core::UpdaterDisabled())
return;
const auto applicationsPath = QStandardPaths::writableLocation(
QStandardPaths::ApplicationsLocation) + '/';
#ifndef TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION
DEBUG_LOG(("App Info: placing .desktop file"));
if (QDir(home + qsl(".local/")).exists()) {
QString apps = home + qsl(".local/share/applications/");
QString icons = home + qsl(".local/share/icons/");
if (!QDir(apps).exists()) QDir().mkpath(apps);
if (!QDir(icons).exists()) QDir().mkpath(icons);
GenerateDesktopFile(applicationsPath, qsl("-- %u"));
QString path = cWorkingDir() + qsl("tdata/"), file = path + qsl("kotatogramdesktop.desktop");
QDir().mkpath(path);
QFile f(file);
if (f.open(QIODevice::WriteOnly)) {
QString icon = icons + qsl("kotatogram.png");
auto iconExists = QFile(icon).exists();
if (Local::oldSettingsVersion() < 10021 && iconExists) {
// Icon was changed.
if (QFile(icon).remove()) {
iconExists = false;
}
}
if (!iconExists) {
if (QFile(qsl(":/gui/art/logo_256.png")).copy(icon)) {
DEBUG_LOG(("App Info: Icon copied to 'tdata'"));
}
}
const auto icons =
QStandardPaths::writableLocation(
QStandardPaths::GenericDataLocation)
+ qsl("/icons/");
QTextStream s(&f);
s.setCodec("UTF-8");
s << "[Desktop Entry]\n";
s << "Version=1.0\n";
s << "Name=Kotatogram Desktop\n";
s << "Comment=Experimental Telegram Desktop fork\n";
s << "TryExec=" << EscapeShell(QFile::encodeName(cExeDir() + cExeName())) << "\n";
s << "Exec=" << EscapeShell(QFile::encodeName(cExeDir() + cExeName())) << " -- %u\n";
s << "Icon=kotatogram\n";
s << "Terminal=false\n";
s << "StartupWMClass=KotatogramDesktop\n";
s << "Type=Application\n";
s << "Categories=Network;InstantMessaging;Qt;\n";
s << "MimeType=x-scheme-handler/tg;\n";
s << "Keywords=tg;chat;im;messaging;messenger;sms;tdesktop;\n";
s << "X-GNOME-UsesNotifications=true\n";
f.close();
if (!QDir(icons).exists()) QDir().mkpath(icons);
if (RunShellCommand("desktop-file-install --dir=" + EscapeShell(QFile::encodeName(home + qsl(".local/share/applications"))) + " --delete-original " + EscapeShell(QFile::encodeName(file)))) {
DEBUG_LOG(("App Info: removing old .desktop file"));
QFile(qsl("%1.local/share/applications/kotatogram.desktop").arg(home)).remove();
RunShellCommand("update-desktop-database " + EscapeShell(QFile::encodeName(home + qsl(".local/share/applications"))));
RunShellCommand("xdg-mime default kotatogramdesktop.desktop x-scheme-handler/tg");
}
} else {
LOG(("App Error: Could not open '%1' for write").arg(file));
const auto icon = icons + qsl("kotatogram.png");
auto iconExists = QFile(icon).exists();
if (Local::oldSettingsVersion() < 10021 && iconExists) {
// Icon was changed.
if (QFile(icon).remove()) {
iconExists = false;
}
}
if (!iconExists) {
if (QFile(qsl(":/gui/art/logo_256.png")).copy(icon)) {
DEBUG_LOG(("App Info: Icon copied to 'tdata'"));
}
}
#endif // !TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION
DEBUG_LOG(("App Info: registerting for Gnome"));
if (RunShellCommand("gconftool-2 -t string -s /desktop/gnome/url-handlers/tg/command " + EscapeShell(EscapeShell(QFile::encodeName(cExeDir() + cExeName())) + " -- %s"))) {
RunShellCommand("gconftool-2 -t bool -s /desktop/gnome/url-handlers/tg/needs_terminal false");
RunShellCommand("gconftool-2 -t bool -s /desktop/gnome/url-handlers/tg/enabled true");
}
RunShellCommand("update-desktop-database "
+ EscapeShell(QFile::encodeName(applicationsPath)));
DEBUG_LOG(("App Info: placing .protocol file"));
QString services;
if (QDir(home + qsl(".kde4/")).exists()) {
services = home + qsl(".kde4/share/kde4/services/");
} else if (QDir(home + qsl(".kde/")).exists()) {
services = home + qsl(".kde/share/kde4/services/");
}
if (!services.isEmpty()) {
if (!QDir(services).exists()) QDir().mkpath(services);
QString path = services, file = path + qsl("tg.protocol");
QFile f(file);
if (f.open(QIODevice::WriteOnly)) {
QTextStream s(&f);
s.setCodec("UTF-8");
s << "[Protocol]\n";
s << "exec=" << QFile::decodeName(EscapeShell(QFile::encodeName(cExeDir() + cExeName()))) << " -- %u\n";
s << "protocol=tg\n";
s << "input=none\n";
s << "output=none\n";
s << "helper=true\n";
s << "listing=false\n";
s << "reading=false\n";
s << "writing=false\n";
s << "makedir=false\n";
s << "deleting=false\n";
f.close();
} else {
LOG(("App Error: Could not open '%1' for write").arg(file));
}
}
RunShellCommand("xdg-mime default "
MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME)
".desktop x-scheme-handler/tg");
#endif // !TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME
}
@ -417,6 +442,28 @@ bool psShowOpenWithMenu(int x, int y, const QString &file) {
}
void psAutoStart(bool start, bool silent) {
auto home = getHomeDir();
if (home.isEmpty() || cAlphaVersion() || cExeName().isEmpty())
return;
if (InSandbox()) {
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
SandboxAutostart(start);
#endif
} else {
const auto autostart =
QStandardPaths::writableLocation(
QStandardPaths::GenericConfigLocation)
+ qsl("/autostart/");
if (start) {
GenerateDesktopFile(autostart, qsl("-autostart"));
} else {
QFile::remove(autostart
+ qsl(MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME)
".desktop"));
}
}
}
void psSendToMenu(bool send, bool silent) {

View file

@ -358,7 +358,7 @@ void SetupTrayContent(not_null<Ui::VerticalLayout*> container) {
}
#ifndef OS_WIN_STORE
if (Platform::IsWindows()) {
if (Platform::IsWindows() || Platform::IsLinux()) {
const auto minimizedToggled = [] {
return cStartMinimized() && !Global::LocalPasscode();
};
@ -369,9 +369,6 @@ void SetupTrayContent(not_null<Ui::VerticalLayout*> container) {
const auto minimized = addSlidingCheckbox(
tr::lng_settings_start_min(tr::now),
minimizedToggled());
const auto sendto = addCheckbox(
tr::ktg_settings_add_sendto(tr::now),
cSendToMenu());
autostart->checkedChanges(
) | rpl::filter([](bool checked) {
@ -408,6 +405,12 @@ void SetupTrayContent(not_null<Ui::VerticalLayout*> container) {
) | rpl::start_with_next([=] {
minimized->entity()->setChecked(minimizedToggled());
}, minimized->lifetime());
}
if (Platform::IsWindows()) {
const auto sendto = addCheckbox(
tr::ktg_settings_add_sendto(tr::now),
cSendToMenu());
sendto->checkedChanges(
) | rpl::filter([](bool checked) {

View file

@ -401,10 +401,10 @@ void PreparedList::mergeToEnd(PreparedList &&other, bool cutToAlbumSize) {
size_t(cutToAlbumSize ? kMaxAlbumCount : INT_MAX),
files.size() + other.files.size()));
for (auto &file : other.files) {
files.push_back(std::move(file));
if (cutToAlbumSize && files.size() == kMaxAlbumCount) {
break;
}
files.push_back(std::move(file));
}
if (files.size() > 1 && files.size() <= kMaxAlbumCount) {
const auto badIt = ranges::find(

View file

@ -1,7 +1,7 @@
AppVersion 1009008
AppVersion 1009009
AppVersionStrMajor 1.9
AppVersionStrSmall 1.9.8
AppVersionStr 1.9.8
AppVersionStrSmall 1.9.9
AppVersionStr 1.9.9
BetaChannel 0
AlphaVersion 0
AppVersionOriginal 1.9.8
AppVersionOriginal 1.9.9

@ -1 +1 @@
Subproject commit b1d635f9271040ae57c999fe9436c44470484372
Subproject commit d37efd134a8040d85be4a52fc88fb87bd48130fe

@ -1 +1 @@
Subproject commit f69cde56f1d3c0a0a7d83625f6f01a4e145eed14
Subproject commit ffca05522fbe58d1ad098713d5327cb93c1bf2a2

View file

@ -1,3 +1,7 @@
1.9.9 (28.01.20)
- Bug fixes and other minor improvements.
1.9.8 (24.01.20)
- Bug fixes and other minor improvements.

View file

@ -1,66 +0,0 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Author: Marco Trevisan <marco@ubuntu.com>
# Copyright (C) 2017-2018 Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import snapcraft
from snapcraft.internal import sources
from snapcraft.plugins import autotools
class Dict2Object(object):
def __init__(self, d):
for k, v in d.items():
setattr(self, k.replace('-', '_'), v)
class AutotoolsSubsourcePlugin(autotools.AutotoolsPlugin):
@classmethod
def schema(cls):
schema = super().schema()
schema['properties']['sub-sources'] = {
'type': 'array',
'minitems': 0,
'uniqueItems': True,
'items': {
'type': 'object',
'additionalProperties': True,
},
'default': [],
}
return schema
@classmethod
def get_pull_properties(cls):
return [
'sub-sources',
]
def pull(self):
super().pull()
for src in self.options.sub_sources:
[name] = src.keys()
[values] = src.values()
if 'source' in values:
dest = values['dest'] if 'dest' in values else ''
sources.get(os.path.join(self.sourcedir, dest),
os.path.join(self.build_basedir, dest),
Dict2Object(values))

View file

@ -1,100 +0,0 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Author: Marco Trevisan <marco@ubuntu.com>
# Copyright (C) 2017-2018 Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import snapcraft
from snapcraft.plugins import cmake
class GypCMakePlugin(cmake.CMakePlugin):
"""A basic plugin for snapcraft that generates CMake files from gyp"""
@classmethod
def schema(cls):
schema = super().schema()
schema['properties']['gyp-file'] = {
'type': 'string'
}
schema['properties']['build-type'] = {
'type': 'string',
'default': 'Release',
'enum': ['Debug', 'Release'],
}
schema['properties']['environment'] = {
'type': 'array',
'minitems': 0,
'uniqueItems': True,
'items': {
'type': 'object',
'minitems': 0,
'uniqueItems': True,
'items': {
'type': 'string',
},
},
'default': [],
}
schema['required'].append('gyp-file')
schema['build-properties'].extend([
'build-type',
'gyp-file',
])
return schema
def __init__(self, name, options, project):
super().__init__(name, options, project)
self.build_packages.extend([
'binutils',
'python',
])
self.builddir = os.path.join(
self.build_basedir, 'out', self.options.build_type)
def build(self):
env = self._build_environment()
gyp_path = os.path.join(self.sourcedir, os.path.dirname(self.options.gyp_file))
for environ in self.options.environment:
[env_name] = list(environ)
env[env_name] = str(environ[env_name])
if not os.path.exists(os.path.join(self.builddir, 'CMakeLists.txt')):
gyp_command = ['gyp'] + self.options.configflags + ['--format=cmake']
gyp_command.append('--generator-output={}'.format(self.build_basedir))
gyp_command.append(os.path.basename(self.options.gyp_file))
self.run(gyp_command, cwd=gyp_path)
if not os.path.exists(os.path.join(self.builddir, 'Makefile')):
self.run(['cmake', '.'], env=env)
self.make(env=env)
if self.options.artifacts and self.options.build_type == 'Release':
for artifact in self.options.artifacts:
dest = os.path.join(self.installdir, artifact)
if os.path.isfile(dest):
mime_type = self.run_output(
'file --mime-type -b {}'.format(dest).split())
if 'application/x-executable' in mime_type:
self.run(['strip', dest])

View file

@ -1,65 +0,0 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Author: Marco Trevisan <marco@ubuntu.com>
# Copyright (C) 2017-2018 Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import snapcraft
import requests
from snapcraft.plugins import python
class PatchedPythonPlugin(python.PythonPlugin):
@classmethod
def schema(cls):
schema = super().schema()
schema['properties']['patches'] = {
'type': 'array',
'minitems': 0,
'uniqueItems': True,
'items': {
'type': 'string',
},
'default': [],
}
schema['pull-properties'].extend([
'patches',
])
return schema
def pull(self):
super().pull()
for patch in self.options.patches:
patch_name = os.path.basename(patch)
patch_stamp = os.path.join(
self.sourcedir, '.snapcraft-patched-{}'.format(patch_name))
if not os.path.exists(patch_stamp):
if os.path.exists(patch):
patch_file = os.path.join(os.getcwd(), patch)
else:
patch_file = os.path.join(
self.sourcedir, 'snapcraft-patch-{}'.format(patch_name))
with open(patch_file, 'wb') as file:
file.write(requests.get(patch).content)
patch_cmd = 'git apply -v3 {}'.format(patch_file).split()
self.run(patch_cmd, cwd=self.sourcedir)
open(patch_stamp, 'a').close()

View file

@ -1,204 +0,0 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Author: Marco Trevisan <marco@ubuntu.com>
# Copyright (C) 2017-2018 Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import shutil
import snapcraft
from snapcraft.plugins import make
class QtBuilderPlugin(make.MakePlugin):
@classmethod
def schema(cls):
schema = super().schema()
schema['properties']['configflags'] = {
'type': 'array',
'minitems': 1,
'uniqueItems': False,
'items': {
'type': 'string',
},
'default': [],
}
schema['properties']['qt-source-git'] = {
'type': 'string'
}
schema['properties']['qt-source-depth'] = {
'type': 'integer',
'default': 1
}
schema['properties']['qt-version'] = {
'type': 'string'
}
schema['properties']['qt-patches-base-url'] = {
'type': 'string'
}
schema['properties']['qt-patches-path'] = {
'type': 'string'
}
schema['properties']['qt-submodules'] = {
'type': 'array',
'minitems': 0,
'uniqueItems': True,
'items': {
'type': 'string',
},
'default': [],
}
schema['properties']['qt-extra-plugins'] = {
'type': 'array',
'minitems': 0,
'uniqueItems': True,
'items': {
'type': 'object',
'minitems': 0,
'uniqueItems': True,
'items': {
'type': 'string',
},
},
'default': [],
}
schema['properties']['environment'] = {
'type': 'array',
'minitems': 0,
'uniqueItems': True,
'items': {
'type': 'object',
'minitems': 0,
'uniqueItems': True,
'items': {
'type': 'string',
},
},
'default': [],
}
schema['required'].append('qt-source-git')
schema['build-properties'].append('configflags')
return schema
@classmethod
def get_pull_properties(cls):
return [
'qt-version',
'qt-patches-base-url',
'qt-patches-path',
'qt-submodules',
'qt-extra-plugins',
]
def __init__(self, name, options, project):
super().__init__(name, options, project)
self.build_packages.extend(['g++', 'patch', 'perl', 'wget'])
self.options.source_depth = self.options.qt_source_depth
if self.options.qt_version:
if self.options.qt_version[0] == 'v':
self.options.source_branch = self.options.qt_version
self.options.qt_version = self.options.qt_version[1:]
else:
self.options.source_branch = '.'.join(
self.options.qt_version.split('.')[:-1])
def pull(self):
if not os.path.exists(os.path.join(self.sourcedir, '.git')) or \
not os.path.exists(os.path.join(self.sourcedir, 'init-repository')):
shutil.rmtree(self.sourcedir, ignore_errors=True)
command = 'git clone {} {}'.format(
self.options.qt_source_git, self.sourcedir).split()
if self.options.source_branch:
command.extend(['--branch', str(self.options.source_branch)])
if self.options.source_depth:
command.extend(['--depth', str(self.options.source_depth)])
self.run(command)
command = 'perl init-repository --branch -f'.split()
if len(self.options.qt_submodules):
command.extend('--module-subset={}'.format(
','.join(self.options.qt_submodules)).split())
self.run(command, cwd=self.sourcedir)
if self.options.qt_version:
self.run("git submodule foreach git checkout v{}".format(
self.options.qt_version).split(), self.sourcedir)
patch_file_template = '${{name}}{}.diff'.format(
'_' + self.options.qt_version.replace('.', '_') \
if self.options.qt_version else '')
if self.options.qt_patches_base_url:
patch_uri_template = '{}/{}'.format(
self.options.qt_patches_base_url, patch_file_template)
patch_cmd = 'git submodule foreach -q'.split() + \
['[ -e {touch_file} ] || ' \
'wget -q -O - {patch_uri_template} | patch -p1 && ' \
'touch {touch_file}'.format(
patch_uri_template=patch_uri_template,
touch_file='.snapcraft-qt-patched')]
self.run(patch_cmd, cwd=self.sourcedir)
if self.options.qt_patches_path:
patch_path_template = os.path.join(
os.getcwd(), self.options.qt_patches_path, patch_file_template)
patch_cmd = 'git submodule foreach -q'.split() + \
['[ -e {patch} ] && git apply -v3 {patch} || true'.format(
patch=patch_path_template)]
self.run(patch_cmd, cwd=self.sourcedir)
for extra_plugin in self.options.qt_extra_plugins:
[framework] = list(extra_plugin)
final_path = os.path.join(self.sourcedir, 'qtbase', 'src',
'plugins', framework)
for repo in extra_plugin[framework]:
repo_path = os.path.basename(repo)
if repo_path.endswith('.git'):
repo_path = repo_path[:-4]
if not os.path.exists(os.path.join(final_path, repo_path)):
command = 'git clone {}'.format(repo).split()
self.run(command, cwd=final_path)
def build(self):
env = {}
for environ in self.options.environment:
[env_name] = list(environ)
env[env_name] = str(environ[env_name])
self.run(['./configure'] + self.options.configflags, env=env)
super().build()

View file

@ -1,24 +0,0 @@
#!/bin/bash
default_downloaddir="$SNAP_USER_DATA/Telegram Desktop"
if [ -d "$default_downloaddir" ] && [ ! -L "$default_downloaddir" ]; then
dest_downloaddir="$SNAP_USER_COMMON/$(basename "$default_downloaddir")"
if [ -d "$dest_downloaddir" ]; then
mv -v "$default_downloaddir/*" "$dest_downloaddir/"
rmdir "$default_downloaddir"
else
mv -v "$default_downloaddir" "$SNAP_USER_COMMON"
fi
ln -sv "$dest_downloaddir" "$default_downloaddir"
fi
default_im_module="xim"
if [ -n "$TELEGRAM_QT_IM_MODULE" ]; then
export QT_IM_MODULE="$TELEGRAM_QT_IM_MODULE"
elif [ -z "$QT_IM_MODULE" ] || [ "$QT_IM_MODULE" == "ibus" ]; then
export QT_IM_MODULE="$default_im_module"
fi
exec desktop-launch Telegram $*

View file

@ -1,438 +1,221 @@
name: telegram-desktop
version: git
adopt-info: telegram
description: |
Telegram is a popular messaging protocol with encryption and security as
its key focus.
Fast and secure desktop app, perfectly synced with your mobile phone.
icon: Telegram/Resources/art/icon512@2x.png
base: core18
grade: stable
confinement: strict
version-script: |
set -x
version_file=Telegram/build/version
version=$(sed -n "s/AppVersionStr[ ]\+\(.*\)\+/\1/p" $version_file)
beta=$(sed -n "s/BetaChannel[ ]\+\(.*\)\+/\1/p" $version_file)
if [ "$beta" != "0" ]; then
version="$version-beta"
fi
version="${version}$(git describe --tags | sed 's,^v[^-]\+,,')"
echo $version
apps:
telegram-desktop:
command: telegram-launch
command: bin/desktop-launch telegram-desktop
common-id: org.telegram.desktop
desktop: share/applications/telegramdesktop.desktop
environment:
DISABLE_WAYLAND: 1
WAYLAND_DISPLAY: no-display
QTCOMPOSE: $SNAP/usr/share/X11/locale
HOME: "$SNAP_USER_COMMON"
# Use GTK3 cursor theme, icon theme and open/save file dialogs.
QT_QPA_PLATFORMTHEME: gtk3
plugs:
- desktop
- desktop-legacy
- gsettings
- home
- network
- network-bind
- network-manager
- pulseaudio
- removable-media
- unity7
plugs:
# Support for common GTK themes
# https://forum.snapcraft.io/t/how-to-use-the-system-gtk-theme-via-the-gtk-common-themes-snap/6235
gsettings:
gtk-3-themes:
interface: content
target: $SNAP/usr/share/themes
target: $SNAP/data-dir/themes
default-provider: gtk-common-themes
icon-themes:
interface: content
target: $SNAP/usr/share/icons
target: $SNAP/data-dir/icons
default-provider: gtk-common-themes
sound-themes:
interface: content
target: $SNAP/usr/share/sounds
target: $SNAP/data-dir/sounds
default-provider: gtk-common-themes
parts:
telegram:
plugin: gyp-cmake
plugin: cmake
source: .
source-type: git
parse-info: [lib/xdg/telegramdesktop.appdata.xml]
build-packages:
- libappindicator-dev
- libappindicator3-dev
- libenchant-dev
- libexif-dev
- libicu-dev
- liblzma-dev
- libssl-dev
- libdee-dev
- zlib1g-dev
gyp-file: Telegram/gyp/Telegram.gyp
build-type: 'Release'
artifacts: ['Telegram']
environment:
- CC: gcc-8
- CXX: g++-8
organize:
Telegram: bin/Telegram
configflags:
- -Dapi_id=611335
- -Dapi_hash=d524b414d21f4d37f08684c1df41ac9c
- -Dlinux_path_breakpad=$SNAPCRAFT_STAGE
- -Dlinux_path_range=$SNAPCRAFT_STAGE/range-v3
- -Dlinux_path_ffmpeg=$SNAPCRAFT_STAGE
- -Dlinux_path_libexif_lib=$SNAPCRAFT_STAGE
- -Dlinux_path_openal=$SNAPCRAFT_STAGE
- -Dlinux_path_opus_include=$SNAPCRAFT_STAGE/include/opus
- -Dlinux_path_qt=$SNAPCRAFT_STAGE
- -Dlinux_path_va=$SNAPCRAFT_STAGE
- -Dlinux_path_vdpau=$SNAPCRAFT_STAGE
- -Dlinux_path_xkbcommon=$SNAPCRAFT_STAGE
- -Dlinux_lib_ssl=-lssl
- -Dlinux_lib_crypto=-lcrypto
- -Dlinux_lib_icu=-licuuc -licutu -licui18n
- -Dbuild_defines=TDESKTOP_DISABLE_AUTOUPDATE,
TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME,
TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION
- --depth=.
override-build: |
set -xe
snapcraftctl build
part_src=$SNAPCRAFT_PART_INSTALL/../src
snap_gui=$SNAPCRAFT_STAGE/../snap/gui
mkdir -vp $snap_gui
cp -v $part_src/lib/xdg/telegramdesktop.desktop $snap_gui
cp -v $part_src/Telegram/Resources/art/icon512@2x.png $snap_gui/icon.png
sed -i "s|^Icon=.*|Icon=\${SNAP}/meta/gui/icon.png|g" $snap_gui/telegramdesktop.desktop
after:
- breakpad
- ffmpeg
- gyp
- libva
- openal
- qt
- range-v3
- gcc8
telegram-launcher:
plugin: dump
source: snap/scripts
organize:
telegram-launch: bin/telegram-launch
desktop-integration:
plugin: nil
stage-packages:
- libappindicator3-1
- libnotify4
- libpulse0
after: [desktop-gtk3]
stage:
- -./usr/share/fonts/**
- -./usr/share/themes/**
- -./usr/share/icons/**
- -./usr/share/sounds/**
desktop-gtk3:
stage:
- -./usr/share/fonts/**
- -./usr/share/themes/**
- -./usr/share/icons/**
- -./usr/share/sounds/**
override-build: |
set -xe
snapcraftctl build
export XDG_DATA_DIRS=$SNAPCRAFT_PART_INSTALL/usr/share
update-mime-database $SNAPCRAFT_PART_INSTALL/usr/share/mime
for dir in $SNAPCRAFT_PART_INSTALL/usr/share/icons/*/; do
if [ -f $dir/index.theme ]; then
gtk-update-icon-cache-3.0 -q $dir
fi
done
libva:
source: https://github.com/01org/libva.git
source-depth: 1
plugin: autotools
build-packages:
- libdrm-dev
- libegl1-mesa-dev
- libgl1-mesa-dev
- libx11-dev
- libxext-dev
- libxfixes-dev
configflags:
- --enable-static
prime: [-./*]
libvdpau:
source: https://gitlab.freedesktop.org/vdpau/libvdpau.git
source-depth: 1
source-branch: libvdpau-1.2
plugin: autotools
build-packages:
- libx11-dev
- x11proto-dri2-dev
- libxext-dev
configflags:
- --enable-static
prime: [-./*]
opus:
source: https://github.com/xiph/opus.git
source-depth: 1
source-branch: v1.2.1
plugin: autotools
prime: [-./*]
ffmpeg:
source: https://github.com/FFmpeg/FFmpeg.git
source-depth: 1
source-branch: release/3.4
plugin: autotools
build-packages:
- libass-dev
- libfreetype6-dev
- libgpac-dev
- liblzma-dev
- libsdl1.2-dev
- libtheora-dev
- libtool
- libvorbis-dev
- libxcb1-dev
- libxcb-shm0-dev
- libxcb-xfixes0-dev
- pkg-config
- texi2html
- yasm
- zlib1g-dev
configflags:
- --prefix=/
- --disable-debug
- --disable-programs
- --disable-doc
- --disable-everything
- --enable-gpl
- --enable-version3
- --enable-libopus
- --enable-decoder=aac
- --enable-decoder=aac_latm
- --enable-decoder=aasc
- --enable-decoder=flac
- --enable-decoder=gif
- --enable-decoder=h264
- --enable-decoder=h264_vdpau
- --enable-decoder=mp1
- --enable-decoder=mp1float
- --enable-decoder=mp2
- --enable-decoder=mp2float
- --enable-decoder=mp3
- --enable-decoder=mp3adu
- --enable-decoder=mp3adufloat
- --enable-decoder=mp3float
- --enable-decoder=mp3on4
- --enable-decoder=mp3on4float
- --enable-decoder=mpeg4
- --enable-decoder=mpeg4_vdpau
- --enable-decoder=msmpeg4v2
- --enable-decoder=msmpeg4v3
- --enable-decoder=opus
- --enable-decoder=vorbis
- --enable-decoder=wavpack
- --enable-decoder=wmalossless
- --enable-decoder=wmapro
- --enable-decoder=wmav1
- --enable-decoder=wmav2
- --enable-decoder=wmavoice
- --enable-encoder=libopus
- --enable-hwaccel=h264_vaapi
- --enable-hwaccel=h264_vdpau
- --enable-hwaccel=mpeg4_vaapi
- --enable-hwaccel=mpeg4_vdpau
- --enable-parser=aac
- --enable-parser=aac_latm
- --enable-parser=flac
- --enable-parser=h264
- --enable-parser=mpeg4video
- --enable-parser=mpegaudio
- --enable-parser=opus
- --enable-parser=vorbis
- --enable-demuxer=aac
- --enable-demuxer=flac
- --enable-demuxer=gif
- --enable-demuxer=h264
- --enable-demuxer=mov
- --enable-demuxer=mp3
- --enable-demuxer=ogg
- --enable-demuxer=wav
- --enable-muxer=ogg
- --enable-muxer=opus
after:
- libva
- libvdpau
- opus
prime: [-./*]
openal:
source: https://github.com/kcat/openal-soft.git
source-depth: 1
source-tag: openal-soft-1.19.1
plugin: cmake
build-packages:
- oss4-dev
- portaudio19-dev
configflags:
- -DCMAKE_BUILD_TYPE=Release
- -DALSOFT_EXAMPLES=OFF
- -DALSOFT_TESTS=OFF
- -DALSOFT_UTILS=OFF
- -DLIBTYPE=STATIC
after:
- ffmpeg
prime: [-./*]
libxkbcommon:
source: https://github.com/xkbcommon/libxkbcommon.git
source-depth: 1
source-tag: xkbcommon-0.8.4
plugin: autotools
build-packages:
- xutils-dev
- bison
- python-xcbgen
prime: [-./*]
qt:
plugin: qtbuilder
qt-version: 5.12.5
qt-source-git: https://code.qt.io/qt/qt5.git
qt-submodules: ['qtbase', 'qtimageformats']
qt-patches-base-url: https://raw.githubusercontent.com/desktop-app/patches/master
qt-extra-plugins:
- platforminputcontexts:
- https://github.com/telegramdesktop/fcitx.git
- https://github.com/telegramdesktop/hime.git
- https://github.com/telegramdesktop/nimf.git
environment:
- CC: gcc-8
- CXX: g++-8
- QMAKE_CC: gcc-8
- QMAKE_CXX: g++-8
parse-info: [share/metainfo/telegramdesktop.appdata.xml]
build-packages:
- gcc-8
- g++-8
- qtbase5-private-dev
- libayatana-appindicator3-dev
- libgtk-3-dev
- libasound2-dev
- libdbusmenu-glib-dev
- libffi-dev
- libavcodec-dev
- libavformat-dev
- libavutil-dev
- libswscale-dev
- libswresample-dev
- liblz4-dev
- liblzma-dev
- libminizip-dev
- libopenal-dev
- libopus-dev
- libpulse-dev
- libssl-dev
- libx11-xcb-dev
- libxcb-xkb-dev
- libxcb-icccm4-dev
- libxcb-image0-dev
- libxcb-keysyms1-dev
- libxcb-randr0-dev
- libxcb-render-util0-dev
- libxcb-sync-dev
- libxcb-util0-dev
- libxcb-xfixes0-dev
- libxcb1-dev
- libxrender-dev
- zlib1g-dev
stage-packages:
- qt5-image-formats-plugins
- libayatana-appindicator3-1
- libasound2
- libavcodec57
- libavformat57
- libavutil55
- libswscale4
- libswresample2
- liblz4-1
- liblzma5
- libminizip1
- libopenal1
- libopus0
- libpulse0
- libssl1.1
- zlib1g
configflags:
- -prefix
- $SNAPCRAFT_STAGE
- -release
- -force-debug-info
- -opensource
- -confirm-license
- -qt-zlib
- -qt-libpng
- -qt-libjpeg
- -qt-harfbuzz
- -qt-pcre
- -qt-xcb
- -no-gtk
- -no-mirclient
- -system-freetype
- -fontconfig
- -no-opengl
- -static
- -dbus-runtime
- -openssl-linked
- -nomake
- examples
- -nomake
- tests
- -DCMAKE_C_COMPILER=gcc-8
- -DCMAKE_CXX_COMPILER=g++-8
- -DCMAKE_BUILD_TYPE=Release
- -DTDESKTOP_API_ID=611335
- -DTDESKTOP_API_HASH=d524b414d21f4d37f08684c1df41ac9c
- -DDESKTOP_APP_USE_PACKAGED_FONTS=OFF
- -DDESKTOP_APP_USE_PACKAGED_RLOTTIE=OFF
- -DTDESKTOP_USE_PACKAGED_TGVOIP=OFF
override-pull: |
snapcraftctl pull
version_file=Telegram/build/version
version=$(sed -n "s/AppVersionStr[ ]\+\(.*\)\+/\1/p" $version_file)
beta=$(sed -n "s/BetaChannel[ ]\+\(.*\)\+/\1/p" $version_file)
if [ "$beta" != "0" ]; then
version="$version-beta"
fi
version="${version}$(git describe --tags | sed 's,^v[^-]\+,,')"
snapcraftctl set-version "$version"
sed -i 's|^Icon=.*|Icon=/share/icons/hicolor/512x512/apps/telegram.png|g' lib/xdg/telegramdesktop.desktop
after:
- libxkbcommon
- gcc8
- cmake
- desktop-qt5
- enchant
- range-v3
- xxhash
spellchecking:
plugin: nil
stage-packages:
- hunspell-de-de
- hunspell-en-au
- hunspell-en-ca
- hunspell-en-gb
- hunspell-en-us
- hunspell-en-za
- hunspell-fr-classical
- hunspell-it
- hunspell-pl
- hunspell-es
- hunspell-pt-br
- hunspell-pt-pt
desktop-qt5:
source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
source-subdir: qt
plugin: make
make-parameters: ["FLAVOR=qt5"]
build-packages:
- build-essential
- qtbase5-dev
- dpkg-dev
stage-packages:
- libxkbcommon0
- ttf-ubuntu-font-family
- dmz-cursor-theme
- light-themes
- adwaita-icon-theme
- gnome-themes-standard
- shared-mime-info
- libqt5gui5
- libgdk-pixbuf2.0-0
- libqt5svg5 # for loading icon themes which are svg
- try: [appmenu-qt5] # not available on core18
- locales-all
- xdg-user-dirs
- fcitx-frontend-qt5
qt5-gtk-platform:
plugin: nil
stage-packages:
- qt5-gtk-platformtheme
cmake:
source: "https://gitlab.kitware.com/cmake/cmake.git"
source-depth: 1
source-branch: master
source-type: git
plugin: make
override-build: |
${SNAPCRAFT_PART_SRC}/bootstrap \
--parallel=${SNAPCRAFT_PARALLEL_BUILD_COUNT} \
-- \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/ \
-DBUILD_TESTING=OFF
snapcraftctl build
build-packages:
- build-essential
- libssl-dev
- libncurses5-dev
stage-packages:
- libssl1.1
- libncurses5
- libtinfo5
prime: [-./*]
breakpad:
plugin: autotools-subsource
source: https://chromium.googlesource.com/breakpad/breakpad
source-type: git
source-commit: bc8fb886
sub-sources:
- linux-syscall-support:
dest: src/third_party/lss
source: https://chromium.googlesource.com/linux-syscall-support
source-type: git
source-commit: a91633d1
prime: [-./*]
enchant:
source: https://github.com/AbiWord/enchant.git
source-depth: 1
source-tag: v2.2.7
plugin: autotools
build-packages:
- libglib2.0-dev
- libhunspell-dev
stage-packages:
- libglib2.0-0
- libhunspell-1.6-0
configflags:
- --enable-relocatable
prime: [-./bin/*]
range-v3:
source: https://github.com/ericniebler/range-v3.git
source-depth: 1
source-tag: 0.10.0
plugin: nil
override-build: |
set -x
snapcraftctl build
mkdir $SNAPCRAFT_PART_INSTALL/range-v3
cp -rv * $SNAPCRAFT_PART_INSTALL/range-v3
plugin: cmake
configflags:
- -DRANGE_V3_TESTS=OFF
- -DRANGE_V3_EXAMPLES=OFF
- -DRANGE_V3_DOCS=OFF
prime: [-./*]
gyp:
plugin: patched-python
source: https://chromium.googlesource.com/external/gyp
source-type: git
source-commit: 702ac58e47
python-version: python2
patches:
- Telegram/Patches/gyp.diff
prime: [-./*]
# Since this is supposed to be built against ubuntu 16.04, we need to manually
# install gcc8, and this is a workaround to achieve this.
# This part can be safely removed when build.snapcraft.io will allow
# to build against 18.04.
gcc8:
plugin: nil
build-packages:
- libmpc-dev
- libcloog-ppl-dev
override-pull: |
set -x
echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu xenial main" | \
sudo tee /etc/apt/sources.list.d/ubuntu-toolchain-r.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 60C317803A41BA51845E371A1E9377A2BA9EF27F
sudo apt-get update \
-o Dir::Etc::sourcelist="sources.list.d/ubuntu-toolchain-r.list" \
-o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"
snapcraftctl pull
override-build: |
set -x
snapcraftctl build
sudo apt install gcc-8 g++-8 -o Debug::pkgProblemResolver=yes --no-install-recommends -y
sudo apt-mark auto gcc-8 g++-8
sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r.list
prime: [-./*]
xxhash:
source: https://github.com/Cyan4973/xxHash.git
source-depth: 1
source-tag: v0.7.2
plugin: make
make-parameters: [PREFIX=]
prime: [-./bin/*]