mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-06 15:13:57 +02:00
Support topic icon display in topic profile.
This commit is contained in:
parent
fcc4503791
commit
df5602d203
26 changed files with 317 additions and 119 deletions
|
@ -2003,9 +2003,8 @@ void ApiWrap::applyAffectedMessages(
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::saveCurrentDraftToCloud() {
|
void ApiWrap::saveCurrentDraftToCloud() {
|
||||||
Core::App().saveCurrentDraftsToHistories();
|
|
||||||
|
|
||||||
for (const auto &controller : _session->windows()) {
|
for (const auto &controller : _session->windows()) {
|
||||||
|
controller->materializeLocalDrafts();
|
||||||
if (const auto thread = controller->activeChatCurrent().thread()) {
|
if (const auto thread = controller->activeChatCurrent().thread()) {
|
||||||
const auto history = thread->owningHistory();
|
const auto history = thread->owningHistory();
|
||||||
_session->local().writeDrafts(history);
|
_session->local().writeDrafts(history);
|
||||||
|
@ -2028,10 +2027,8 @@ void ApiWrap::saveDraftsToCloud() {
|
||||||
if (!thread) {
|
if (!thread) {
|
||||||
i = _draftsSaveRequestIds.erase(i);
|
i = _draftsSaveRequestIds.erase(i);
|
||||||
continue;
|
continue;
|
||||||
}
|
} else if (i->second) {
|
||||||
auto &requestId = i->second;
|
++i;
|
||||||
++i;
|
|
||||||
if (requestId) {
|
|
||||||
continue; // sent already
|
continue; // sent already
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2055,9 +2052,9 @@ void ApiWrap::saveDraftsToCloud() {
|
||||||
}
|
}
|
||||||
if (cloudDraft->msgId) {
|
if (cloudDraft->msgId) {
|
||||||
flags |= MTPmessages_SaveDraft::Flag::f_reply_to_msg_id;
|
flags |= MTPmessages_SaveDraft::Flag::f_reply_to_msg_id;
|
||||||
if (cloudDraft->topicRootId) {
|
}
|
||||||
flags |= MTPmessages_SaveDraft::Flag::f_top_msg_id;
|
if (cloudDraft->topicRootId) {
|
||||||
}
|
flags |= MTPmessages_SaveDraft::Flag::f_top_msg_id;
|
||||||
}
|
}
|
||||||
if (!textWithTags.tags.isEmpty()) {
|
if (!textWithTags.tags.isEmpty()) {
|
||||||
flags |= MTPmessages_SaveDraft::Flag::f_entities;
|
flags |= MTPmessages_SaveDraft::Flag::f_entities;
|
||||||
|
@ -2111,6 +2108,7 @@ void ApiWrap::saveDraftsToCloud() {
|
||||||
}).send();
|
}).send();
|
||||||
|
|
||||||
i->second = cloudDraft->saveRequestId;
|
i->second = cloudDraft->saveRequestId;
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace ChatHelpers {
|
||||||
|
|
||||||
enum class StickerLottieSize : uint8 {
|
enum class StickerLottieSize : uint8 {
|
||||||
MessageHistory,
|
MessageHistory,
|
||||||
StickerSet,
|
StickerSet, // In Emoji used for forum topic profile cover icons.
|
||||||
StickersPanel,
|
StickersPanel,
|
||||||
StickersFooter,
|
StickersFooter,
|
||||||
SetsListThumbnail,
|
SetsListThumbnail,
|
||||||
|
|
|
@ -1144,14 +1144,6 @@ bool Application::hasActiveWindow(not_null<Main::Session*> session) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::saveCurrentDraftsToHistories() {
|
|
||||||
if (!_primaryWindow) {
|
|
||||||
return;
|
|
||||||
} else if (const auto controller = _primaryWindow->sessionController()) {
|
|
||||||
controller->content()->saveFieldToHistoryLocalDraft();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Window::Controller *Application::primaryWindow() const {
|
Window::Controller *Application::primaryWindow() const {
|
||||||
return _primaryWindow.get();
|
return _primaryWindow.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,7 +152,6 @@ public:
|
||||||
|
|
||||||
// Windows interface.
|
// Windows interface.
|
||||||
bool hasActiveWindow(not_null<Main::Session*> session) const;
|
bool hasActiveWindow(not_null<Main::Session*> session) const;
|
||||||
void saveCurrentDraftsToHistories();
|
|
||||||
[[nodiscard]] Window::Controller *primaryWindow() const;
|
[[nodiscard]] Window::Controller *primaryWindow() const;
|
||||||
[[nodiscard]] Window::Controller *activeWindow() const;
|
[[nodiscard]] Window::Controller *activeWindow() const;
|
||||||
[[nodiscard]] Window::Controller *separateWindowForPeer(
|
[[nodiscard]] Window::Controller *separateWindowForPeer(
|
||||||
|
|
|
@ -151,10 +151,11 @@ struct TopicUpdate {
|
||||||
UnreadReactions = (1U << 3),
|
UnreadReactions = (1U << 3),
|
||||||
Notifications = (1U << 4),
|
Notifications = (1U << 4),
|
||||||
Title = (1U << 5),
|
Title = (1U << 5),
|
||||||
Icon = (1U << 6),
|
IconId = (1U << 6),
|
||||||
CloudDraft = (1U << 7),
|
ColorId = (1U << 7),
|
||||||
|
CloudDraft = (1U << 8),
|
||||||
|
|
||||||
LastUsedBit = (1U << 7),
|
LastUsedBit = (1U << 8),
|
||||||
};
|
};
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||||
|
|
|
@ -492,7 +492,7 @@ void ForumTopic::applyIconId(DocumentId iconId) {
|
||||||
_defaultIcon = QImage();
|
_defaultIcon = QImage();
|
||||||
}
|
}
|
||||||
updateChatListEntry();
|
updateChatListEntry();
|
||||||
session().changes().topicUpdated(this, UpdateFlag::Icon);
|
session().changes().topicUpdated(this, UpdateFlag::IconId);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 ForumTopic::colorId() const {
|
int32 ForumTopic::colorId() const {
|
||||||
|
@ -500,7 +500,10 @@ int32 ForumTopic::colorId() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForumTopic::applyColorId(int32 colorId) {
|
void ForumTopic::applyColorId(int32 colorId) {
|
||||||
_colorId = colorId;
|
if (_colorId != colorId) {
|
||||||
|
_colorId = colorId;
|
||||||
|
session().changes().topicUpdated(this, UpdateFlag::ColorId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForumTopic::applyItemAdded(not_null<HistoryItem*> item) {
|
void ForumTopic::applyItemAdded(not_null<HistoryItem*> item) {
|
||||||
|
|
|
@ -39,6 +39,22 @@ constexpr auto kUnsubscribeUpdatesDelay = 3 * crl::time(1000);
|
||||||
|
|
||||||
using SizeTag = CustomEmojiManager::SizeTag;
|
using SizeTag = CustomEmojiManager::SizeTag;
|
||||||
|
|
||||||
|
class CallbackListener final : public CustomEmojiManager::Listener {
|
||||||
|
public:
|
||||||
|
explicit CallbackListener(Fn<void(not_null<DocumentData*>)> callback)
|
||||||
|
: _callback(std::move(callback)) {
|
||||||
|
Expects(_callback != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void customEmojiResolveDone(not_null<DocumentData*> document) {
|
||||||
|
_callback(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
Fn<void(not_null<DocumentData*>)> _callback;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] ChatHelpers::StickerLottieSize LottieSizeFromTag(SizeTag tag) {
|
[[nodiscard]] ChatHelpers::StickerLottieSize LottieSizeFromTag(SizeTag tag) {
|
||||||
// NB! onlyCustomEmoji dimensions caching uses last ::EmojiInteraction-s.
|
// NB! onlyCustomEmoji dimensions caching uses last ::EmojiInteraction-s.
|
||||||
using LottieSize = ChatHelpers::StickerLottieSize;
|
using LottieSize = ChatHelpers::StickerLottieSize;
|
||||||
|
@ -546,6 +562,28 @@ void CustomEmojiManager::unregisterListener(not_null<Listener*> listener) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<not_null<DocumentData*>> CustomEmojiManager::resolve(
|
||||||
|
DocumentId documentId) {
|
||||||
|
return [=](auto consumer) {
|
||||||
|
auto result = rpl::lifetime();
|
||||||
|
const auto put = [=](not_null<DocumentData*> document) {
|
||||||
|
if (!document->sticker()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
consumer.put_next_copy(document);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if (!put(owner().document(documentId))) {
|
||||||
|
const auto listener = new CallbackListener(put);
|
||||||
|
result.add([=] {
|
||||||
|
unregisterListener(listener);
|
||||||
|
delete listener;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader(
|
std::unique_ptr<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader(
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
SizeTag tag,
|
SizeTag tag,
|
||||||
|
|
|
@ -65,6 +65,9 @@ public:
|
||||||
void resolve(DocumentId documentId, not_null<Listener*> listener);
|
void resolve(DocumentId documentId, not_null<Listener*> listener);
|
||||||
void unregisterListener(not_null<Listener*> listener);
|
void unregisterListener(not_null<Listener*> listener);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<not_null<DocumentData*>> resolve(
|
||||||
|
DocumentId documentId);
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<Ui::CustomEmoji::Loader> createLoader(
|
[[nodiscard]] std::unique_ptr<Ui::CustomEmoji::Loader> createLoader(
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
SizeTag tag,
|
SizeTag tag,
|
||||||
|
|
|
@ -35,6 +35,11 @@ largeForumTopicIcon: ForumTopicIcon {
|
||||||
font: font(bold 13px);
|
font: font(bold 13px);
|
||||||
textTop: 3px;
|
textTop: 3px;
|
||||||
}
|
}
|
||||||
|
infoForumTopicIcon: ForumTopicIcon {
|
||||||
|
size: 32px;
|
||||||
|
font: font(bold 15px);
|
||||||
|
textTop: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
dialogsUnreadFont: font(12px bold);
|
dialogsUnreadFont: font(12px bold);
|
||||||
dialogsUnreadHeight: 19px;
|
dialogsUnreadHeight: 19px;
|
||||||
|
|
|
@ -182,9 +182,9 @@ constexpr auto kSkipRepaintWhileScrollMs = 100;
|
||||||
constexpr auto kShowMembersDropdownTimeoutMs = 300;
|
constexpr auto kShowMembersDropdownTimeoutMs = 300;
|
||||||
constexpr auto kDisplayEditTimeWarningMs = 300 * 1000;
|
constexpr auto kDisplayEditTimeWarningMs = 300 * 1000;
|
||||||
constexpr auto kFullDayInMs = 86400 * 1000;
|
constexpr auto kFullDayInMs = 86400 * 1000;
|
||||||
constexpr auto kSaveDraftTimeout = 1000;
|
constexpr auto kSaveDraftTimeout = crl::time(1000);
|
||||||
constexpr auto kSaveDraftAnywayTimeout = 5000;
|
constexpr auto kSaveDraftAnywayTimeout = 5 * crl::time(1000);
|
||||||
constexpr auto kSaveCloudDraftIdleTimeout = 14000;
|
constexpr auto kSaveCloudDraftIdleTimeout = 14 * crl::time(1000);
|
||||||
constexpr auto kRefreshSlowmodeLabelTimeout = crl::time(200);
|
constexpr auto kRefreshSlowmodeLabelTimeout = crl::time(200);
|
||||||
constexpr auto kCommonModifiers = 0
|
constexpr auto kCommonModifiers = 0
|
||||||
| Qt::ShiftModifier
|
| Qt::ShiftModifier
|
||||||
|
@ -865,6 +865,11 @@ HistoryWidget::HistoryWidget(
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
controller->materializeLocalDraftsRequests(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
saveFieldToHistoryLocalDraft();
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
setupScheduledToggle();
|
setupScheduledToggle();
|
||||||
setupSendAsToggle();
|
setupSendAsToggle();
|
||||||
orderWidgets();
|
orderWidgets();
|
||||||
|
|
|
@ -227,7 +227,6 @@ public:
|
||||||
void clearSupportPreloadRequest();
|
void clearSupportPreloadRequest();
|
||||||
void clearDelayedShowAtRequest();
|
void clearDelayedShowAtRequest();
|
||||||
void clearDelayedShowAt();
|
void clearDelayedShowAt();
|
||||||
void saveFieldToHistoryLocalDraft();
|
|
||||||
|
|
||||||
void toggleChooseChatTheme(not_null<PeerData*> peer);
|
void toggleChooseChatTheme(not_null<PeerData*> peer);
|
||||||
[[nodiscard]] Ui::ChatTheme *customChatTheme() const;
|
[[nodiscard]] Ui::ChatTheme *customChatTheme() const;
|
||||||
|
@ -355,6 +354,7 @@ private:
|
||||||
void checkFieldAutocomplete();
|
void checkFieldAutocomplete();
|
||||||
void showMembersDropdown();
|
void showMembersDropdown();
|
||||||
void windowIsVisibleChanged();
|
void windowIsVisibleChanged();
|
||||||
|
void saveFieldToHistoryLocalDraft();
|
||||||
|
|
||||||
// Checks if we are too close to the top or to the bottom
|
// Checks if we are too close to the top or to the bottom
|
||||||
// in the scroll area and preloads history if needed.
|
// in the scroll area and preloads history if needed.
|
||||||
|
|
|
@ -68,6 +68,7 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kSaveDraftTimeout = crl::time(1000);
|
constexpr auto kSaveDraftTimeout = crl::time(1000);
|
||||||
constexpr auto kSaveDraftAnywayTimeout = 5 * crl::time(1000);
|
constexpr auto kSaveDraftAnywayTimeout = 5 * crl::time(1000);
|
||||||
|
constexpr auto kSaveCloudDraftIdleTimeout = 14 * crl::time(1000);
|
||||||
constexpr auto kMouseEvents = {
|
constexpr auto kMouseEvents = {
|
||||||
QEvent::MouseMove,
|
QEvent::MouseMove,
|
||||||
QEvent::MouseButtonPress,
|
QEvent::MouseButtonPress,
|
||||||
|
@ -870,7 +871,8 @@ ComposeControls::ComposeControls(
|
||||||
st::historySendSize.height()))
|
st::historySendSize.height()))
|
||||||
, _sendMenuType(sendMenuType)
|
, _sendMenuType(sendMenuType)
|
||||||
, _unavailableEmojiPasted(unavailableEmojiPasted)
|
, _unavailableEmojiPasted(unavailableEmojiPasted)
|
||||||
, _saveDraftTimer([=] { saveDraft(); }) {
|
, _saveDraftTimer([=] { saveDraft(); })
|
||||||
|
, _saveCloudDraftTimer([=] { saveCloudDraft(); }) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1341,6 +1343,11 @@ void ComposeControls::init() {
|
||||||
}, _wrap->lifetime());
|
}, _wrap->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_window->materializeLocalDraftsRequests(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
saveFieldToHistoryLocalDraft();
|
||||||
|
}, _wrap->lifetime());
|
||||||
|
|
||||||
orderControls();
|
orderControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1660,6 +1667,7 @@ void ComposeControls::fieldChanged() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_saveCloudDraftTimer.cancel();
|
||||||
if (!(_textUpdateEvents & TextUpdateEvent::SaveDraft)) {
|
if (!(_textUpdateEvents & TextUpdateEvent::SaveDraft)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1709,6 +1717,10 @@ void ComposeControls::saveDraft(bool delayed) {
|
||||||
writeDrafts();
|
writeDrafts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ComposeControls::saveCloudDraft() {
|
||||||
|
session().api().saveCurrentDraftToCloud();
|
||||||
|
}
|
||||||
|
|
||||||
void ComposeControls::writeDraftTexts() {
|
void ComposeControls::writeDraftTexts() {
|
||||||
Expects(_history != nullptr);
|
Expects(_history != nullptr);
|
||||||
|
|
||||||
|
@ -1773,9 +1785,15 @@ void ComposeControls::writeDrafts() {
|
||||||
}
|
}
|
||||||
_saveDraftText = false;
|
_saveDraftText = false;
|
||||||
|
|
||||||
//if (!isEditingMessage() && !_inlineBot) {
|
if (!isEditingMessage() && !_inlineBot) {
|
||||||
// _saveCloudDraftTimer.callOnce(kSaveCloudDraftIdleTimeout);
|
_saveCloudDraftTimer.callOnce(kSaveCloudDraftIdleTimeout);
|
||||||
//}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComposeControls::applyCloudDraft() {
|
||||||
|
if (!isEditingMessage()) {
|
||||||
|
applyDraft(Ui::InputField::HistoryAction::NewEntry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
||||||
|
|
|
@ -179,6 +179,7 @@ public:
|
||||||
[[nodiscard]] bool isLockPresent() const;
|
[[nodiscard]] bool isLockPresent() const;
|
||||||
[[nodiscard]] bool isRecording() const;
|
[[nodiscard]] bool isRecording() const;
|
||||||
|
|
||||||
|
void applyCloudDraft();
|
||||||
void applyDraft(
|
void applyDraft(
|
||||||
FieldHistoryAction fieldHistoryAction = FieldHistoryAction::Clear);
|
FieldHistoryAction fieldHistoryAction = FieldHistoryAction::Clear);
|
||||||
|
|
||||||
|
@ -265,6 +266,7 @@ private:
|
||||||
[[nodiscard]] Data::DraftKey draftKeyCurrent() const;
|
[[nodiscard]] Data::DraftKey draftKeyCurrent() const;
|
||||||
void saveDraft(bool delayed = false);
|
void saveDraft(bool delayed = false);
|
||||||
void saveDraftDelayed();
|
void saveDraftDelayed();
|
||||||
|
void saveCloudDraft();
|
||||||
|
|
||||||
void writeDrafts();
|
void writeDrafts();
|
||||||
void writeDraftTexts();
|
void writeDraftTexts();
|
||||||
|
@ -334,6 +336,7 @@ private:
|
||||||
crl::time _saveDraftStart = 0;
|
crl::time _saveDraftStart = 0;
|
||||||
bool _saveDraftText = false;
|
bool _saveDraftText = false;
|
||||||
base::Timer _saveDraftTimer;
|
base::Timer _saveDraftTimer;
|
||||||
|
base::Timer _saveCloudDraftTimer;
|
||||||
|
|
||||||
UserData *_inlineBot = nullptr;
|
UserData *_inlineBot = nullptr;
|
||||||
QString _inlineBotUsername;
|
QString _inlineBotUsername;
|
||||||
|
|
|
@ -44,22 +44,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class Listener final : public Data::CustomEmojiManager::Listener {
|
|
||||||
public:
|
|
||||||
explicit Listener(Fn<void(not_null<DocumentData*>)> check)
|
|
||||||
: _check(std::move(check)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void customEmojiResolveDone(
|
|
||||||
not_null<DocumentData*> document) override {
|
|
||||||
_check(document);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Fn<void(not_null<DocumentData*>)> _check;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] bool BarCurrentlyHidden(not_null<PeerData*> peer) {
|
[[nodiscard]] bool BarCurrentlyHidden(not_null<PeerData*> peer) {
|
||||||
const auto settings = peer->settings();
|
const auto settings = peer->settings();
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
|
@ -83,43 +67,29 @@ private:
|
||||||
[[nodiscard]] rpl::producer<TextWithEntities> ResolveIsCustom(
|
[[nodiscard]] rpl::producer<TextWithEntities> ResolveIsCustom(
|
||||||
not_null<Data::Session*> owner,
|
not_null<Data::Session*> owner,
|
||||||
DocumentId id) {
|
DocumentId id) {
|
||||||
return [=](auto consumer) {
|
return owner->customEmojiManager().resolve(
|
||||||
const auto document = owner->document(id);
|
id
|
||||||
const auto manager = &owner->customEmojiManager();
|
) | rpl::map([=](not_null<DocumentData*> document) {
|
||||||
const auto check = [=](not_null<DocumentData*> document) {
|
const auto sticker = document->sticker();
|
||||||
if (const auto sticker = document->sticker()) {
|
Assert(sticker != nullptr);
|
||||||
const auto setId = manager->coloredSetId();
|
|
||||||
const auto text = (setId == sticker->set.id)
|
const auto &manager = document->owner().customEmojiManager();
|
||||||
? QString()
|
const auto setId = manager.coloredSetId();
|
||||||
: sticker->alt;
|
const auto text = (setId == sticker->set.id)
|
||||||
if (text.isEmpty()) {
|
? QString()
|
||||||
consumer.put_next({});
|
: sticker->alt;
|
||||||
} else {
|
if (text.isEmpty()) {
|
||||||
consumer.put_next({
|
return TextWithEntities();
|
||||||
.text = text,
|
|
||||||
.entities = { EntityInText(
|
|
||||||
EntityType::CustomEmoji,
|
|
||||||
0,
|
|
||||||
text.size(),
|
|
||||||
Data::SerializeCustomEmojiId(document)) },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
auto lifetime = rpl::lifetime();
|
|
||||||
if (!check(document)) {
|
|
||||||
const auto manager = &owner->customEmojiManager();
|
|
||||||
const auto listener = new Listener(check);
|
|
||||||
lifetime.add([=] {
|
|
||||||
manager->unregisterListener(listener);
|
|
||||||
delete listener;
|
|
||||||
});
|
|
||||||
manager->resolve(id, listener);
|
|
||||||
}
|
}
|
||||||
return lifetime;
|
return TextWithEntities{
|
||||||
};
|
.text = text,
|
||||||
|
.entities = { EntityInText(
|
||||||
|
EntityType::CustomEmoji,
|
||||||
|
0,
|
||||||
|
text.size(),
|
||||||
|
Data::SerializeCustomEmojiId(document)) },
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<TextWithEntities> PeerCustomStatus(
|
[[nodiscard]] rpl::producer<TextWithEntities> PeerCustomStatus(
|
||||||
|
|
|
@ -359,6 +359,7 @@ RepliesWidget::~RepliesWidget() {
|
||||||
_history->owner().sendActionManager().repliesPainterRemoved(
|
_history->owner().sendActionManager().repliesPainterRemoved(
|
||||||
_history,
|
_history,
|
||||||
_rootId);
|
_rootId);
|
||||||
|
session().api().saveCurrentDraftToCloud();
|
||||||
controller()->sendingAnimation().clear();
|
controller()->sendingAnimation().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,13 +462,19 @@ void RepliesWidget::setupTopicViewer() {
|
||||||
void RepliesWidget::subscribeToTopic() {
|
void RepliesWidget::subscribeToTopic() {
|
||||||
Expects(_topic != nullptr);
|
Expects(_topic != nullptr);
|
||||||
|
|
||||||
using TopicUpdateFlag = Data::TopicUpdate::Flag;
|
using Flag = Data::TopicUpdate::Flag;
|
||||||
session().changes().topicUpdates(
|
session().changes().topicUpdates(
|
||||||
_topic,
|
_topic,
|
||||||
(TopicUpdateFlag::UnreadMentions
|
(Flag::UnreadMentions
|
||||||
| TopicUpdateFlag::UnreadReactions)
|
| Flag::UnreadReactions
|
||||||
|
| Flag::CloudDraft)
|
||||||
) | rpl::start_with_next([=](const Data::TopicUpdate &update) {
|
) | rpl::start_with_next([=](const Data::TopicUpdate &update) {
|
||||||
_cornerButtons.updateUnreadThingsVisibility();
|
if (update.flags & (Flag::UnreadMentions | Flag::UnreadReactions)) {
|
||||||
|
_cornerButtons.updateUnreadThingsVisibility();
|
||||||
|
}
|
||||||
|
if (update.flags & Flag::CloudDraft) {
|
||||||
|
_composeControls->applyCloudDraft();
|
||||||
|
}
|
||||||
}, _topicLifetime);
|
}, _topicLifetime);
|
||||||
|
|
||||||
_topic->destroyed(
|
_topic->destroyed(
|
||||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "history/view/media/history_view_sticker_player.h"
|
#include "history/view/media/history_view_sticker_player.h"
|
||||||
|
|
||||||
|
#include "core/file_location.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -114,4 +116,46 @@ bool WebmPlayer::markFrameShown() {
|
||||||
return _reader->moveToNextFrame();
|
return _reader->moveToNextFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StaticStickerPlayer::StaticStickerPlayer(
|
||||||
|
const Core::FileLocation &location,
|
||||||
|
const QByteArray &data,
|
||||||
|
QSize size)
|
||||||
|
: _frame(Images::Read({
|
||||||
|
.path = location.name(),
|
||||||
|
.content = data,
|
||||||
|
.forceOpaque = true,
|
||||||
|
}).image) {
|
||||||
|
if (!_frame.isNull()) {
|
||||||
|
size = _frame.size().scaled(size, Qt::KeepAspectRatio);
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
_frame = Images::Prepare(std::move(_frame), size * ratio, {});
|
||||||
|
_frame.setDevicePixelRatio(ratio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StaticStickerPlayer::setRepaintCallback(Fn<void()> callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StaticStickerPlayer::ready() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StaticStickerPlayer::framesCount() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticStickerPlayer::FrameInfo StaticStickerPlayer::frame(
|
||||||
|
QSize size,
|
||||||
|
QColor colored,
|
||||||
|
bool mirrorHorizontal,
|
||||||
|
crl::time now,
|
||||||
|
bool paused) {
|
||||||
|
return { _frame };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StaticStickerPlayer::markFrameShown() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -66,4 +66,27 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class StaticStickerPlayer final : public StickerPlayer {
|
||||||
|
public:
|
||||||
|
StaticStickerPlayer(
|
||||||
|
const Core::FileLocation &location,
|
||||||
|
const QByteArray &data,
|
||||||
|
QSize size);
|
||||||
|
|
||||||
|
void setRepaintCallback(Fn<void()> callback) override;
|
||||||
|
bool ready() override;
|
||||||
|
int framesCount() override;
|
||||||
|
FrameInfo frame(
|
||||||
|
QSize size,
|
||||||
|
QColor colored,
|
||||||
|
bool mirrorHorizontal,
|
||||||
|
crl::time now,
|
||||||
|
bool paused) override;
|
||||||
|
bool markFrameShown() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage _frame;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -315,6 +315,11 @@ infoProfileMegagroupCover: InfoProfileCover(infoProfileCover) {
|
||||||
}
|
}
|
||||||
infoTopicCover: InfoProfileCover(infoProfileMegagroupCover) {
|
infoTopicCover: InfoProfileCover(infoProfileMegagroupCover) {
|
||||||
height: 77px;
|
height: 77px;
|
||||||
|
photo: UserpicButton(defaultUserpicButton) {
|
||||||
|
size: size(36px, 36px);
|
||||||
|
}
|
||||||
|
photoLeft: 22px;
|
||||||
|
photoTop: 18px;
|
||||||
nameLeft: 79px;
|
nameLeft: 79px;
|
||||||
nameTop: 14px;
|
nameTop: 14px;
|
||||||
statusLeft: 79px;
|
statusLeft: 79px;
|
||||||
|
|
|
@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_document_media.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
|
@ -19,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "info/profile/info_profile_badge.h"
|
#include "info/profile/info_profile_badge.h"
|
||||||
#include "info/profile/info_profile_emoji_status_panel.h"
|
#include "info/profile/info_profile_emoji_status_panel.h"
|
||||||
#include "info/info_controller.h"
|
#include "info/info_controller.h"
|
||||||
|
#include "history/view/media/history_view_sticker_player.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
|
@ -27,10 +30,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "settings/settings_premium.h"
|
#include "settings/settings_premium.h"
|
||||||
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "api/api_peer_photo.h"
|
#include "api/api_peer_photo.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
#include "styles/style_dialogs.h"
|
||||||
|
|
||||||
namespace Info::Profile {
|
namespace Info::Profile {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -185,32 +190,90 @@ Cover::Cover(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cover::setupIcon(not_null<Data::ForumTopic*> topic) {
|
void Cover::setupIconPlayer(not_null<Data::ForumTopic*> topic) {
|
||||||
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
|
||||||
IconIdValue(
|
IconIdValue(
|
||||||
topic
|
topic
|
||||||
) | rpl::start_with_next([=](DocumentId id) {
|
) | rpl::map([=](DocumentId id) {
|
||||||
_icon = id
|
return topic->owner().customEmojiManager().resolve(id);
|
||||||
? topic->owner().customEmojiManager().create(
|
}) | rpl::flatten_latest(
|
||||||
id,
|
) | rpl::map([=](not_null<DocumentData*> document) {
|
||||||
[=] { _iconView->update(); },
|
const auto media = document->createMediaView();
|
||||||
tag)
|
media->checkStickerLarge();
|
||||||
: nullptr;
|
media->goodThumbnailWanted();
|
||||||
|
|
||||||
|
return rpl::single() | rpl::then(
|
||||||
|
document->owner().session().downloaderTaskFinished()
|
||||||
|
) | rpl::filter([=] {
|
||||||
|
return media->loaded();
|
||||||
|
}) | rpl::take(1) | rpl::map([=] {
|
||||||
|
auto result = std::shared_ptr<StickerPlayer>();
|
||||||
|
const auto sticker = document->sticker();
|
||||||
|
if (sticker->isLottie()) {
|
||||||
|
result = std::make_shared<HistoryView::LottiePlayer>(
|
||||||
|
ChatHelpers::LottiePlayerFromDocument(
|
||||||
|
media.get(),
|
||||||
|
ChatHelpers::StickerLottieSize::StickerSet,
|
||||||
|
_st.photo.size,
|
||||||
|
Lottie::Quality::High));
|
||||||
|
} else if (sticker->isWebm()) {
|
||||||
|
result = std::make_shared<HistoryView::WebmPlayer>(
|
||||||
|
media->owner()->location(),
|
||||||
|
media->bytes(),
|
||||||
|
_st.photo.size);
|
||||||
|
} else {
|
||||||
|
result = std::make_shared<HistoryView::StaticStickerPlayer>(
|
||||||
|
media->owner()->location(),
|
||||||
|
media->bytes(),
|
||||||
|
_st.photo.size);
|
||||||
|
}
|
||||||
|
result->setRepaintCallback([=] { _iconView->update(); });
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}) | rpl::flatten_latest(
|
||||||
|
) | rpl::start_with_next([=](std::shared_ptr<StickerPlayer> player) {
|
||||||
|
_iconPlayer = std::move(player);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
const auto size = Data::FrameSizeFromTag(tag);
|
}
|
||||||
_iconView->resize(size, size);
|
|
||||||
|
void Cover::setupIconImage(not_null<Data::ForumTopic*> topic) {
|
||||||
|
rpl::combine(
|
||||||
|
TitleValue(topic),
|
||||||
|
ColorIdValue(topic)
|
||||||
|
) | rpl::map([=](const QString &title, int32 colorId) {
|
||||||
|
using namespace Data;
|
||||||
|
return ForumTopicIconFrame(colorId, title, st::infoForumTopicIcon);
|
||||||
|
}) | rpl::start_with_next([=](QImage &&image) {
|
||||||
|
_iconImage = std::move(image);
|
||||||
|
_iconView->update();
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cover::setupIcon(not_null<Data::ForumTopic*> topic) {
|
||||||
|
setupIconPlayer(topic);
|
||||||
|
setupIconImage(topic);
|
||||||
|
|
||||||
|
_iconView->resize(_st.photo.size);
|
||||||
_iconView->paintRequest(
|
_iconView->paintRequest(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
auto p = QPainter(_iconView.data());
|
auto p = QPainter(_iconView.data());
|
||||||
if (_icon) {
|
const auto paint = [&](const QImage &image) {
|
||||||
_icon->paint(p, {
|
const auto size = image.size() / image.devicePixelRatio();
|
||||||
.preview = st::windowBgOver->c,
|
p.drawImage(
|
||||||
.now = crl::now(),
|
(_st.photo.size.width() - size.width()) / 2,
|
||||||
.paused = _controller->isGifPausedAtLeastFor(
|
(_st.photo.size.height() - size.height()) / 2,
|
||||||
Window::GifPauseReason::Layer),
|
image);
|
||||||
});
|
};
|
||||||
} else {
|
if (_iconPlayer && _iconPlayer->ready()) {
|
||||||
|
paint(_iconPlayer->frame(
|
||||||
|
_st.photo.size,
|
||||||
|
QColor(0, 0, 0, 0),
|
||||||
|
false,
|
||||||
|
crl::now(),
|
||||||
|
_controller->isGifPausedAtLeastFor(
|
||||||
|
Window::GifPauseReason::Layer)).image);
|
||||||
|
_iconPlayer->markFrameShown();
|
||||||
|
} else if (!topic->iconId() && !_iconImage.isNull()) {
|
||||||
|
paint(_iconImage);
|
||||||
}
|
}
|
||||||
}, _iconView->lifetime());
|
}, _iconView->lifetime());
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,9 @@ template <typename Widget>
|
||||||
class SlideWrap;
|
class SlideWrap;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Ui::Text {
|
namespace HistoryView {
|
||||||
struct CustomEmojiColored;
|
class StickerPlayer;
|
||||||
} // namespace Ui::Text
|
} // namespace HistoryView
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class ForumTopic;
|
class ForumTopic;
|
||||||
|
@ -67,6 +67,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using StickerPlayer = HistoryView::StickerPlayer;
|
||||||
|
|
||||||
Cover(
|
Cover(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
@ -75,6 +77,8 @@ private:
|
||||||
rpl::producer<QString> title);
|
rpl::producer<QString> title);
|
||||||
|
|
||||||
void setupIcon(not_null<Data::ForumTopic*> topic);
|
void setupIcon(not_null<Data::ForumTopic*> topic);
|
||||||
|
void setupIconPlayer(not_null<Data::ForumTopic*> topic);
|
||||||
|
void setupIconImage(not_null<Data::ForumTopic*> topic);
|
||||||
void setupChildGeometry();
|
void setupChildGeometry();
|
||||||
void initViewers(rpl::producer<QString> title);
|
void initViewers(rpl::producer<QString> title);
|
||||||
void refreshStatusText();
|
void refreshStatusText();
|
||||||
|
@ -92,7 +96,8 @@ private:
|
||||||
|
|
||||||
object_ptr<Ui::UserpicButton> _userpic;
|
object_ptr<Ui::UserpicButton> _userpic;
|
||||||
object_ptr<Ui::RpWidget> _iconView;
|
object_ptr<Ui::RpWidget> _iconView;
|
||||||
std::unique_ptr<Ui::Text::CustomEmoji> _icon;
|
std::shared_ptr<StickerPlayer> _iconPlayer;
|
||||||
|
QImage _iconImage;
|
||||||
object_ptr<Ui::FlatLabel> _name = { nullptr };
|
object_ptr<Ui::FlatLabel> _name = { nullptr };
|
||||||
object_ptr<Ui::FlatLabel> _status = { nullptr };
|
object_ptr<Ui::FlatLabel> _status = { nullptr };
|
||||||
//object_ptr<CoverDropArea> _dropArea = { nullptr };
|
//object_ptr<CoverDropArea> _dropArea = { nullptr };
|
||||||
|
|
|
@ -104,10 +104,17 @@ rpl::producer<QString> TitleValue(not_null<Data::ForumTopic*> topic) {
|
||||||
rpl::producer<DocumentId> IconIdValue(not_null<Data::ForumTopic*> topic) {
|
rpl::producer<DocumentId> IconIdValue(not_null<Data::ForumTopic*> topic) {
|
||||||
return topic->session().changes().topicFlagsValue(
|
return topic->session().changes().topicFlagsValue(
|
||||||
topic,
|
topic,
|
||||||
Data::TopicUpdate::Flag::Icon
|
Data::TopicUpdate::Flag::IconId
|
||||||
) | rpl::map([=] { return topic->iconId(); });
|
) | rpl::map([=] { return topic->iconId(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<int32> ColorIdValue(not_null<Data::ForumTopic*> topic) {
|
||||||
|
return topic->session().changes().topicFlagsValue(
|
||||||
|
topic,
|
||||||
|
Data::TopicUpdate::Flag::ColorId
|
||||||
|
) | rpl::map([=] { return topic->colorId(); });
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<TextWithEntities> PhoneValue(not_null<UserData*> user) {
|
rpl::producer<TextWithEntities> PhoneValue(not_null<UserData*> user) {
|
||||||
return rpl::merge(
|
return rpl::merge(
|
||||||
Countries::Instance().updated(),
|
Countries::Instance().updated(),
|
||||||
|
|
|
@ -49,6 +49,8 @@ rpl::producer<not_null<PeerData*>> MigratedOrMeValue(
|
||||||
not_null<Data::ForumTopic*> topic);
|
not_null<Data::ForumTopic*> topic);
|
||||||
[[nodiscard]] rpl::producer<DocumentId> IconIdValue(
|
[[nodiscard]] rpl::producer<DocumentId> IconIdValue(
|
||||||
not_null<Data::ForumTopic*> topic);
|
not_null<Data::ForumTopic*> topic);
|
||||||
|
[[nodiscard]] rpl::producer<int32> ColorIdValue(
|
||||||
|
not_null<Data::ForumTopic*> topic);
|
||||||
[[nodiscard]] rpl::producer<TextWithEntities> PhoneValue(
|
[[nodiscard]] rpl::producer<TextWithEntities> PhoneValue(
|
||||||
not_null<UserData*> user);
|
not_null<UserData*> user);
|
||||||
[[nodiscard]] rpl::producer<TextWithEntities> PhoneOrHiddenValue(
|
[[nodiscard]] rpl::producer<TextWithEntities> PhoneOrHiddenValue(
|
||||||
|
|
|
@ -2727,10 +2727,6 @@ bool MainWidget::doWeMarkAsRead() const {
|
||||||
return isActive() && !_mainSection;
|
return isActive() && !_mainSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::saveFieldToHistoryLocalDraft() {
|
|
||||||
_history->saveFieldToHistoryLocalDraft();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainWidget::isOneColumn() const {
|
bool MainWidget::isOneColumn() const {
|
||||||
return _controller->adaptive().isOneColumn();
|
return _controller->adaptive().isOneColumn();
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,8 +169,6 @@ public:
|
||||||
bool isActive() const;
|
bool isActive() const;
|
||||||
[[nodiscard]] bool doWeMarkAsRead() const;
|
[[nodiscard]] bool doWeMarkAsRead() const;
|
||||||
|
|
||||||
void saveFieldToHistoryLocalDraft();
|
|
||||||
|
|
||||||
void showForwardLayer(Data::ForwardDraft &&draft);
|
void showForwardLayer(Data::ForwardDraft &&draft);
|
||||||
void showSendPathsLayer();
|
void showSendPathsLayer();
|
||||||
void shareUrlLayer(const QString &url, const QString &text);
|
void shareUrlLayer(const QString &url, const QString &text);
|
||||||
|
|
|
@ -1119,6 +1119,14 @@ void SessionController::floatPlayerAreaUpdated() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SessionController::materializeLocalDrafts() {
|
||||||
|
_materializeLocalDraftsRequests.fire({});
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> SessionController::materializeLocalDraftsRequests() const {
|
||||||
|
return _materializeLocalDraftsRequests.events();
|
||||||
|
}
|
||||||
|
|
||||||
int SessionController::dialogsSmallColumnWidth() const {
|
int SessionController::dialogsSmallColumnWidth() const {
|
||||||
return st::defaultDialogRow.padding.left()
|
return st::defaultDialogRow.padding.left()
|
||||||
+ st::defaultDialogRow.photoSize
|
+ st::defaultDialogRow.photoSize
|
||||||
|
|
|
@ -378,6 +378,9 @@ public:
|
||||||
bool isGifPausedAtLeastFor(GifPauseReason reason) const;
|
bool isGifPausedAtLeastFor(GifPauseReason reason) const;
|
||||||
void floatPlayerAreaUpdated();
|
void floatPlayerAreaUpdated();
|
||||||
|
|
||||||
|
void materializeLocalDrafts();
|
||||||
|
[[nodiscard]] rpl::producer<> materializeLocalDraftsRequests() const;
|
||||||
|
|
||||||
struct ColumnLayout {
|
struct ColumnLayout {
|
||||||
int bodyWidth = 0;
|
int bodyWidth = 0;
|
||||||
int dialogsWidth = 0;
|
int dialogsWidth = 0;
|
||||||
|
@ -629,6 +632,8 @@ private:
|
||||||
|
|
||||||
QString _premiumRef;
|
QString _premiumRef;
|
||||||
|
|
||||||
|
rpl::event_stream<> _materializeLocalDraftsRequests;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue