mirror of https://github.com/procxx/kepka.git
Replace self-destruct media service messages text.
Also support runtime components with align up to std::max_align_t.
This commit is contained in:
parent
2e0513a30f
commit
9bd89121e8
|
@ -761,6 +761,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
"lng_action_took_screenshot" = "{from} took a screenshot!";
|
"lng_action_took_screenshot" = "{from} took a screenshot!";
|
||||||
"lng_action_you_took_screenshot" = "You took a screenshot!";
|
"lng_action_you_took_screenshot" = "You took a screenshot!";
|
||||||
|
|
||||||
|
"lng_ttl_photo_received" = "{from} sent you a self-destructing photo. Please view it on your mobile.";
|
||||||
|
"lng_ttl_photo_sent" = "You sent a self-destructing photo.";
|
||||||
|
"lng_ttl_photo_expired" = "Photo has expired";
|
||||||
|
"lng_ttl_video_received" = "{from} sent you a self-destructing video. Please view it on your mobile.";
|
||||||
|
"lng_ttl_video_sent" = "You sent a self-destructing video.";
|
||||||
|
"lng_ttl_video_expired" = "Video has expired";
|
||||||
|
|
||||||
"lng_profile_migrate_reached#one" = "{count} member limit reached";
|
"lng_profile_migrate_reached#one" = "{count} member limit reached";
|
||||||
"lng_profile_migrate_reached#other" = "{count} members limit reached";
|
"lng_profile_migrate_reached#other" = "{count} members limit reached";
|
||||||
"lng_profile_migrate_body" = "To get over this limit, you can upgrade your group to a supergroup.";
|
"lng_profile_migrate_body" = "To get over this limit, you can upgrade your group to a supergroup.";
|
||||||
|
|
|
@ -26,8 +26,8 @@ typedef void(*RuntimeComponentDestruct)(void *location);
|
||||||
typedef void(*RuntimeComponentMove)(void *location, void *waslocation);
|
typedef void(*RuntimeComponentMove)(void *location, void *waslocation);
|
||||||
|
|
||||||
struct RuntimeComponentWrapStruct {
|
struct RuntimeComponentWrapStruct {
|
||||||
// don't init any fields, because it is only created in
|
// Don't init any fields, because it is only created in
|
||||||
// global scope, so it will be filled by zeros from the start
|
// global scope, so it will be filled by zeros from the start.
|
||||||
RuntimeComponentWrapStruct() = default;
|
RuntimeComponentWrapStruct() = default;
|
||||||
RuntimeComponentWrapStruct(std::size_t size, std::size_t align, RuntimeComponentConstruct construct, RuntimeComponentDestruct destruct, RuntimeComponentMove move)
|
RuntimeComponentWrapStruct(std::size_t size, std::size_t align, RuntimeComponentConstruct construct, RuntimeComponentDestruct destruct, RuntimeComponentMove move)
|
||||||
: Size(size)
|
: Size(size)
|
||||||
|
@ -54,7 +54,8 @@ extern QAtomicInt RuntimeComponentIndexLast;
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
struct RuntimeComponent {
|
struct RuntimeComponent {
|
||||||
RuntimeComponent() {
|
RuntimeComponent() {
|
||||||
static_assert(alignof(Type) <= alignof(SmallestSizeType), "Components should align to a pointer!");
|
// While there is no std::aligned_alloc().
|
||||||
|
static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!");
|
||||||
}
|
}
|
||||||
RuntimeComponent(const RuntimeComponent &other) = delete;
|
RuntimeComponent(const RuntimeComponent &other) = delete;
|
||||||
RuntimeComponent &operator=(const RuntimeComponent &other) = delete;
|
RuntimeComponent &operator=(const RuntimeComponent &other) = delete;
|
||||||
|
@ -62,17 +63,17 @@ struct RuntimeComponent {
|
||||||
RuntimeComponent &operator=(RuntimeComponent &&other) = default;
|
RuntimeComponent &operator=(RuntimeComponent &&other) = default;
|
||||||
|
|
||||||
static int Index() {
|
static int Index() {
|
||||||
static QAtomicInt _index(0);
|
static QAtomicInt MyIndex(0);
|
||||||
if (int index = _index.loadAcquire()) {
|
if (auto index = MyIndex.loadAcquire()) {
|
||||||
return index - 1;
|
return index - 1;
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
int last = RuntimeComponentIndexLast.loadAcquire();
|
auto last = RuntimeComponentIndexLast.loadAcquire();
|
||||||
if (RuntimeComponentIndexLast.testAndSetOrdered(last, last + 1)) {
|
if (RuntimeComponentIndexLast.testAndSetOrdered(last, last + 1)) {
|
||||||
t_assert(last < 64);
|
t_assert(last < 64);
|
||||||
if (_index.testAndSetOrdered(0, last + 1)) {
|
if (MyIndex.testAndSetOrdered(0, last + 1)) {
|
||||||
RuntimeComponentWraps[last] = RuntimeComponentWrapStruct(
|
RuntimeComponentWraps[last] = RuntimeComponentWrapStruct(
|
||||||
CeilDivideMinimumOne<sizeof(Type), sizeof(SmallestSizeType)>::Result * sizeof(SmallestSizeType),
|
sizeof(Type),
|
||||||
alignof(Type),
|
alignof(Type),
|
||||||
Type::RuntimeComponentConstruct,
|
Type::RuntimeComponentConstruct,
|
||||||
Type::RuntimeComponentDestruct,
|
Type::RuntimeComponentDestruct,
|
||||||
|
@ -81,15 +82,13 @@ struct RuntimeComponent {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _index.loadAcquire() - 1;
|
return MyIndex.loadAcquire() - 1;
|
||||||
}
|
}
|
||||||
static uint64 Bit() {
|
static uint64 Bit() {
|
||||||
return (1ULL << Index());
|
return (1ULL << Index());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
using SmallestSizeType = void*;
|
|
||||||
|
|
||||||
static void RuntimeComponentConstruct(void *location, RuntimeComposer *composer) {
|
static void RuntimeComponentConstruct(void *location, RuntimeComposer *composer) {
|
||||||
new (location) Type();
|
new (location) Type();
|
||||||
}
|
}
|
||||||
|
@ -104,30 +103,32 @@ protected:
|
||||||
|
|
||||||
class RuntimeComposerMetadata {
|
class RuntimeComposerMetadata {
|
||||||
public:
|
public:
|
||||||
RuntimeComposerMetadata(uint64 mask) : size(0), last(64), _mask(mask) {
|
RuntimeComposerMetadata(uint64 mask) : _mask(mask) {
|
||||||
for (int i = 0; i < 64; ++i) {
|
for (int i = 0; i != 64; ++i) {
|
||||||
uint64 m = (1ULL << i);
|
auto componentBit = (1ULL << i);
|
||||||
if (_mask & m) {
|
if (_mask & componentBit) {
|
||||||
int s = RuntimeComponentWraps[i].Size;
|
auto componentSize = RuntimeComponentWraps[i].Size;
|
||||||
if (s) {
|
if (componentSize) {
|
||||||
|
auto componentAlign = RuntimeComponentWraps[i].Align;
|
||||||
|
if (auto badAlign = (size % componentAlign)) {
|
||||||
|
size += (componentAlign - badAlign);
|
||||||
|
}
|
||||||
offsets[i] = size;
|
offsets[i] = size;
|
||||||
size += s;
|
size += componentSize;
|
||||||
} else {
|
accumulate_max(align, componentAlign);
|
||||||
offsets[i] = -1;
|
|
||||||
}
|
}
|
||||||
} else if (_mask < m) {
|
} else if (_mask < componentBit) {
|
||||||
last = i;
|
last = i;
|
||||||
for (; i < 64; ++i) {
|
break;
|
||||||
offsets[i] = -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
offsets[i] = -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int size, last;
|
// Meta pointer in the start.
|
||||||
int offsets[64];
|
std::size_t size = sizeof(const RuntimeComposerMetadata*);
|
||||||
|
std::size_t align = alignof(const RuntimeComposerMetadata*);
|
||||||
|
std::size_t offsets[64] = { 0 };
|
||||||
|
int last = 64;
|
||||||
|
|
||||||
bool equals(uint64 mask) const {
|
bool equals(uint64 mask) const {
|
||||||
return _mask == mask;
|
return _mask == mask;
|
||||||
|
@ -150,28 +151,28 @@ class RuntimeComposer {
|
||||||
public:
|
public:
|
||||||
RuntimeComposer(uint64 mask = 0) : _data(zerodata()) {
|
RuntimeComposer(uint64 mask = 0) : _data(zerodata()) {
|
||||||
if (mask) {
|
if (mask) {
|
||||||
const RuntimeComposerMetadata *meta = GetRuntimeComposerMetadata(mask);
|
auto meta = GetRuntimeComposerMetadata(mask);
|
||||||
int size = sizeof(meta) + meta->size;
|
|
||||||
|
|
||||||
auto data = operator new(size);
|
auto data = operator new(meta->size);
|
||||||
t_assert(data != nullptr);
|
t_assert(data != nullptr);
|
||||||
|
|
||||||
_data = data;
|
_data = data;
|
||||||
_meta() = meta;
|
_meta() = meta;
|
||||||
for (int i = 0; i < meta->last; ++i) {
|
for (int i = 0; i < meta->last; ++i) {
|
||||||
int offset = meta->offsets[i];
|
auto offset = meta->offsets[i];
|
||||||
if (offset >= 0) {
|
if (offset >= sizeof(_meta())) {
|
||||||
try {
|
try {
|
||||||
auto constructAt = _dataptrunsafe(offset);
|
auto constructAt = _dataptrunsafe(offset);
|
||||||
auto space = RuntimeComponentWraps[i].Size;
|
auto space = RuntimeComponentWraps[i].Size;
|
||||||
auto alignedAt = std::align(RuntimeComponentWraps[i].Align, space, constructAt, space);
|
auto alignedAt = constructAt;
|
||||||
|
std::align(RuntimeComponentWraps[i].Align, space, alignedAt, space);
|
||||||
t_assert(alignedAt == constructAt);
|
t_assert(alignedAt == constructAt);
|
||||||
RuntimeComponentWraps[i].Construct(constructAt, this);
|
RuntimeComponentWraps[i].Construct(constructAt, this);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
--i;
|
--i;
|
||||||
offset = meta->offsets[--i];
|
offset = meta->offsets[--i];
|
||||||
if (offset >= 0) {
|
if (offset >= sizeof(_meta())) {
|
||||||
RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset));
|
RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,8 +188,8 @@ public:
|
||||||
if (_data != zerodata()) {
|
if (_data != zerodata()) {
|
||||||
auto meta = _meta();
|
auto meta = _meta();
|
||||||
for (int i = 0; i < meta->last; ++i) {
|
for (int i = 0; i < meta->last; ++i) {
|
||||||
int offset = meta->offsets[i];
|
auto offset = meta->offsets[i];
|
||||||
if (offset >= 0) {
|
if (offset >= sizeof(_meta())) {
|
||||||
RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset));
|
RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,7 +199,7 @@ public:
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
bool Has() const {
|
bool Has() const {
|
||||||
return (_meta()->offsets[Type::Index()] >= 0);
|
return (_meta()->offsets[Type::Index()] >= sizeof(_meta()));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
|
@ -218,8 +219,9 @@ protected:
|
||||||
if (_data != zerodata() && tmp._data != zerodata()) {
|
if (_data != zerodata() && tmp._data != zerodata()) {
|
||||||
auto meta = _meta(), wasmeta = tmp._meta();
|
auto meta = _meta(), wasmeta = tmp._meta();
|
||||||
for (int i = 0; i < meta->last; ++i) {
|
for (int i = 0; i < meta->last; ++i) {
|
||||||
int offset = meta->offsets[i], wasoffset = wasmeta->offsets[i];
|
auto offset = meta->offsets[i];
|
||||||
if (offset >= 0 && wasoffset >= 0) {
|
auto wasoffset = wasmeta->offsets[i];
|
||||||
|
if (offset >= sizeof(_meta()) && wasoffset >= sizeof(_meta())) {
|
||||||
RuntimeComponentWraps[i].Move(_dataptrunsafe(offset), tmp._dataptrunsafe(wasoffset));
|
RuntimeComponentWraps[i].Move(_dataptrunsafe(offset), tmp._dataptrunsafe(wasoffset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,15 +242,15 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void *_dataptrunsafe(int skip) const {
|
void *_dataptrunsafe(int skip) const {
|
||||||
return (char*)_data + sizeof(_meta()) + skip;
|
return (char*)_data + skip;
|
||||||
}
|
}
|
||||||
void *_dataptr(int skip) const {
|
void *_dataptr(int skip) const {
|
||||||
return (skip >= 0) ? _dataptrunsafe(skip) : 0;
|
return (skip >= sizeof(_meta())) ? _dataptrunsafe(skip) : nullptr;
|
||||||
}
|
}
|
||||||
const RuntimeComposerMetadata *&_meta() const {
|
const RuntimeComposerMetadata *&_meta() const {
|
||||||
return *static_cast<const RuntimeComposerMetadata**>(_data);
|
return *static_cast<const RuntimeComposerMetadata**>(_data);
|
||||||
}
|
}
|
||||||
void *_data;
|
void *_data = nullptr;
|
||||||
|
|
||||||
void swap(RuntimeComposer &other) {
|
void swap(RuntimeComposer &other) {
|
||||||
std::swap(_data, other._data);
|
std::swap(_data, other._data);
|
||||||
|
|
|
@ -764,6 +764,37 @@ void Histories::savePinnedToServer() const {
|
||||||
MTP::send(MTPmessages_ReorderPinnedDialogs(MTP_flags(flags), MTP_vector(peers)));
|
MTP::send(MTPmessages_ReorderPinnedDialogs(MTP_flags(flags), MTP_vector(peers)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Histories::selfDestructIn(gsl::not_null<HistoryItem*> item, TimeMs delay) {
|
||||||
|
_selfDestructItems.push_back(item->fullId());
|
||||||
|
if (!_selfDestructTimer.isActive() || _selfDestructTimer.remainingTime() > delay) {
|
||||||
|
_selfDestructTimer.callOnce(delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Histories::checkSelfDestructItems() {
|
||||||
|
auto now = getms(true);
|
||||||
|
auto nextDestructIn = TimeMs(0);
|
||||||
|
for (auto i = _selfDestructItems.begin(); i != _selfDestructItems.cend();) {
|
||||||
|
if (auto item = App::histItemById(*i)) {
|
||||||
|
if (auto destructIn = item->getSelfDestructIn(now)) {
|
||||||
|
if (nextDestructIn > 0) {
|
||||||
|
accumulate_min(nextDestructIn, destructIn);
|
||||||
|
} else {
|
||||||
|
nextDestructIn = destructIn;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
i = _selfDestructItems.erase(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i = _selfDestructItems.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextDestructIn > 0) {
|
||||||
|
_selfDestructTimer.callOnce(nextDestructIn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, bool detachExistingItem) {
|
HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, bool detachExistingItem) {
|
||||||
auto msgId = MsgId(0);
|
auto msgId = MsgId(0);
|
||||||
switch (msg.type()) {
|
switch (msg.type()) {
|
||||||
|
@ -799,7 +830,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
|
||||||
Good,
|
Good,
|
||||||
Unsupported,
|
Unsupported,
|
||||||
Empty,
|
Empty,
|
||||||
HasTTL,
|
HasTimeToLive,
|
||||||
};
|
};
|
||||||
auto badMedia = MediaCheckResult::Good;
|
auto badMedia = MediaCheckResult::Good;
|
||||||
if (m.has_media()) switch (m.vmedia.type()) {
|
if (m.has_media()) switch (m.vmedia.type()) {
|
||||||
|
@ -822,7 +853,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
|
||||||
case mtpc_messageMediaPhoto: {
|
case mtpc_messageMediaPhoto: {
|
||||||
auto &photo = m.vmedia.c_messageMediaPhoto();
|
auto &photo = m.vmedia.c_messageMediaPhoto();
|
||||||
if (photo.has_ttl_seconds()) {
|
if (photo.has_ttl_seconds()) {
|
||||||
badMedia = MediaCheckResult::HasTTL;
|
badMedia = MediaCheckResult::HasTimeToLive;
|
||||||
} else if (!photo.has_photo()) {
|
} else if (!photo.has_photo()) {
|
||||||
badMedia = MediaCheckResult::Empty;
|
badMedia = MediaCheckResult::Empty;
|
||||||
} else {
|
} else {
|
||||||
|
@ -836,7 +867,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
|
||||||
case mtpc_messageMediaDocument: {
|
case mtpc_messageMediaDocument: {
|
||||||
auto &document = m.vmedia.c_messageMediaDocument();
|
auto &document = m.vmedia.c_messageMediaDocument();
|
||||||
if (document.has_ttl_seconds()) {
|
if (document.has_ttl_seconds()) {
|
||||||
badMedia = MediaCheckResult::HasTTL;
|
badMedia = MediaCheckResult::HasTimeToLive;
|
||||||
} else if (!document.has_document()) {
|
} else if (!document.has_document()) {
|
||||||
badMedia = MediaCheckResult::Empty;
|
badMedia = MediaCheckResult::Empty;
|
||||||
} else {
|
} else {
|
||||||
|
@ -872,9 +903,8 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
|
||||||
} else if (badMedia == MediaCheckResult::Empty) {
|
} else if (badMedia == MediaCheckResult::Empty) {
|
||||||
auto message = HistoryService::PreparedText { lang(lng_message_empty) };
|
auto message = HistoryService::PreparedText { lang(lng_message_empty) };
|
||||||
result = HistoryService::create(this, m.vid.v, date(m.vdate), message, m.vflags.v, m.has_from_id() ? m.vfrom_id.v : 0);
|
result = HistoryService::create(this, m.vid.v, date(m.vdate), message, m.vflags.v, m.has_from_id() ? m.vfrom_id.v : 0);
|
||||||
} else if (badMedia == MediaCheckResult::HasTTL) {
|
} else if (badMedia == MediaCheckResult::HasTimeToLive) {
|
||||||
auto message = HistoryService::PreparedText { qsl("Self-destruct media, see mobile") };
|
result = HistoryService::create(this, m);
|
||||||
result = HistoryService::create(this, m.vid.v, date(m.vdate), message, m.vflags.v, m.has_from_id() ? m.vfrom_id.v : 0);
|
|
||||||
} else {
|
} else {
|
||||||
result = HistoryMessage::create(this, m);
|
result = HistoryMessage::create(this, m);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include "dialogs/dialogs_common.h"
|
#include "dialogs/dialogs_common.h"
|
||||||
#include "ui/effects/send_action_animations.h"
|
#include "ui/effects/send_action_animations.h"
|
||||||
#include "base/observer.h"
|
#include "base/observer.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
void HistoryInit();
|
void HistoryInit();
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ public:
|
||||||
Map map;
|
Map map;
|
||||||
|
|
||||||
Histories() : _a_typings(animation(this, &Histories::step_typings)) {
|
Histories() : _a_typings(animation(this, &Histories::step_typings)) {
|
||||||
|
_selfDestructTimer.setCallback([this] { checkSelfDestructItems(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void regSendAction(History *history, UserData *user, const MTPSendMessageAction &action, TimeId when);
|
void regSendAction(History *history, UserData *user, const MTPSendMessageAction &action, TimeId when);
|
||||||
|
@ -95,13 +97,19 @@ public:
|
||||||
base::Observable<SendActionAnimationUpdate> &sendActionAnimationUpdated() {
|
base::Observable<SendActionAnimationUpdate> &sendActionAnimationUpdated() {
|
||||||
return _sendActionAnimationUpdated;
|
return _sendActionAnimationUpdated;
|
||||||
}
|
}
|
||||||
|
void selfDestructIn(gsl::not_null<HistoryItem*> item, TimeMs delay);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void checkSelfDestructItems();
|
||||||
|
|
||||||
int _unreadFull = 0;
|
int _unreadFull = 0;
|
||||||
int _unreadMuted = 0;
|
int _unreadMuted = 0;
|
||||||
base::Observable<SendActionAnimationUpdate> _sendActionAnimationUpdated;
|
base::Observable<SendActionAnimationUpdate> _sendActionAnimationUpdated;
|
||||||
OrderedSet<History*> _pinnedDialogs;
|
OrderedSet<History*> _pinnedDialogs;
|
||||||
|
|
||||||
|
base::Timer _selfDestructTimer;
|
||||||
|
std::vector<FullMsgId> _selfDestructItems;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class HistoryBlock;
|
class HistoryBlock;
|
||||||
|
|
|
@ -579,7 +579,14 @@ public:
|
||||||
}
|
}
|
||||||
void markMediaRead() {
|
void markMediaRead() {
|
||||||
_flags &= ~MTPDmessage::Flag::f_media_unread;
|
_flags &= ~MTPDmessage::Flag::f_media_unread;
|
||||||
|
markMediaAsReadHook();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Zero result means this message is not self-destructing right now.
|
||||||
|
virtual TimeMs getSelfDestructIn(TimeMs now) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool definesReplyKeyboard() const {
|
bool definesReplyKeyboard() const {
|
||||||
if (auto markup = Get<HistoryMessageReplyMarkup>()) {
|
if (auto markup = Get<HistoryMessageReplyMarkup>()) {
|
||||||
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) {
|
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) {
|
||||||
|
@ -918,13 +925,16 @@ public:
|
||||||
protected:
|
protected:
|
||||||
HistoryItem(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime msgDate, int32 from);
|
HistoryItem(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime msgDate, int32 from);
|
||||||
|
|
||||||
// to completely create history item we need to call
|
// To completely create history item we need to call
|
||||||
// a virtual method, it can not be done from constructor
|
// a virtual method, it can not be done from constructor.
|
||||||
virtual void finishCreate();
|
virtual void finishCreate();
|
||||||
|
|
||||||
// called from resizeGetHeight() when MTPDmessage_ClientFlag::f_pending_init_dimensions is set
|
// Called from resizeGetHeight() when MTPDmessage_ClientFlag::f_pending_init_dimensions is set.
|
||||||
virtual void initDimensions() = 0;
|
virtual void initDimensions() = 0;
|
||||||
|
|
||||||
|
virtual void markMediaAsReadHook() {
|
||||||
|
}
|
||||||
|
|
||||||
virtual int resizeContentGetHeight() = 0;
|
virtual int resizeContentGetHeight() = 0;
|
||||||
|
|
||||||
void finishEdition(int oldKeyboardTop);
|
void finishEdition(int oldKeyboardTop);
|
||||||
|
|
|
@ -220,6 +220,13 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryService::setSelfDestruct(HistoryServiceSelfDestruct::Type type, int ttlSeconds) {
|
||||||
|
UpdateComponents(HistoryServiceSelfDestruct::Bit());
|
||||||
|
auto selfdestruct = Get<HistoryServiceSelfDestruct>();
|
||||||
|
selfdestruct->timeToLive = ttlSeconds * 1000LL;
|
||||||
|
selfdestruct->type = type;
|
||||||
|
}
|
||||||
|
|
||||||
bool HistoryService::updateDependent(bool force) {
|
bool HistoryService::updateDependent(bool force) {
|
||||||
auto dependent = GetDependentData();
|
auto dependent = GetDependentData();
|
||||||
t_assert(dependent != nullptr);
|
t_assert(dependent != nullptr);
|
||||||
|
@ -393,10 +400,14 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HistoryService::HistoryService(gsl::not_null<History*> history, const MTPDmessage &message) :
|
||||||
|
HistoryItem(history, message.vid.v, message.vflags.v, ::date(message.vdate), message.has_from_id() ? message.vfrom_id.v : 0) {
|
||||||
|
createFromMtp(message);
|
||||||
|
}
|
||||||
|
|
||||||
HistoryService::HistoryService(gsl::not_null<History*> history, const MTPDmessageService &message) :
|
HistoryService::HistoryService(gsl::not_null<History*> history, const MTPDmessageService &message) :
|
||||||
HistoryItem(history, message.vid.v, mtpCastFlags(message.vflags.v), ::date(message.vdate), message.has_from_id() ? message.vfrom_id.v : 0) {
|
HistoryItem(history, message.vid.v, mtpCastFlags(message.vflags.v), ::date(message.vdate), message.has_from_id() ? message.vfrom_id.v : 0) {
|
||||||
createFromMtp(message);
|
createFromMtp(message);
|
||||||
setMessageByAction(message.vaction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryService::HistoryService(gsl::not_null<History*> history, MsgId msgId, QDateTime date, const PreparedText &message, MTPDmessage::Flags flags, int32 from, PhotoData *photo) :
|
HistoryService::HistoryService(gsl::not_null<History*> history, MsgId msgId, QDateTime date, const PreparedText &message, MTPDmessage::Flags flags, int32 from, PhotoData *photo) :
|
||||||
|
@ -521,6 +532,35 @@ int HistoryService::resizeContentGetHeight() {
|
||||||
return _height;
|
return _height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryService::markMediaAsReadHook() {
|
||||||
|
if (auto selfdestruct = Get<HistoryServiceSelfDestruct>()) {
|
||||||
|
if (!selfdestruct->destructAt) {
|
||||||
|
selfdestruct->destructAt = getms(true) + selfdestruct->timeToLive;
|
||||||
|
App::histories().selfDestructIn(this, selfdestruct->timeToLive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeMs HistoryService::getSelfDestructIn(TimeMs now) {
|
||||||
|
if (auto selfdestruct = Get<HistoryServiceSelfDestruct>()) {
|
||||||
|
if (selfdestruct->destructAt > 0) {
|
||||||
|
if (selfdestruct->destructAt <= now) {
|
||||||
|
auto text = [selfdestruct] {
|
||||||
|
switch (selfdestruct->type) {
|
||||||
|
case HistoryServiceSelfDestruct::Type::Photo: return lang(lng_ttl_photo_expired);
|
||||||
|
case HistoryServiceSelfDestruct::Type::Video: return lang(lng_ttl_video_expired);
|
||||||
|
}
|
||||||
|
Unexpected("Type in HistoryServiceSelfDestruct::Type");
|
||||||
|
};
|
||||||
|
setServiceText({ text() });
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return selfdestruct->destructAt - now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool HistoryService::hasPoint(QPoint point) const {
|
bool HistoryService::hasPoint(QPoint point) const {
|
||||||
auto g = countGeometry();
|
auto g = countGeometry();
|
||||||
if (g.width() < 1) {
|
if (g.width() < 1) {
|
||||||
|
@ -580,6 +620,48 @@ HistoryTextState HistoryService::getState(QPoint point, HistoryStateRequest requ
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryService::createFromMtp(const MTPDmessage &message) {
|
||||||
|
auto mediaType = message.vmedia.type();
|
||||||
|
switch (mediaType) {
|
||||||
|
case mtpc_messageMediaPhoto: {
|
||||||
|
if (message.is_media_unread()) {
|
||||||
|
auto &photo = message.vmedia.c_messageMediaPhoto();
|
||||||
|
t_assert(photo.has_ttl_seconds());
|
||||||
|
setSelfDestruct(HistoryServiceSelfDestruct::Type::Photo, photo.vttl_seconds.v);
|
||||||
|
if (out()) {
|
||||||
|
setServiceText({ lang(lng_ttl_photo_sent) });
|
||||||
|
} else {
|
||||||
|
auto result = PreparedText();
|
||||||
|
result.links.push_back(fromLink());
|
||||||
|
result.text = lng_ttl_photo_received(lt_from, fromLinkText());
|
||||||
|
setServiceText(std::move(result));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setServiceText({ lang(lng_ttl_photo_expired) });
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case mtpc_messageMediaDocument: {
|
||||||
|
if (message.is_media_unread()) {
|
||||||
|
auto &document = message.vmedia.c_messageMediaDocument();
|
||||||
|
t_assert(document.has_ttl_seconds());
|
||||||
|
setSelfDestruct(HistoryServiceSelfDestruct::Type::Video, document.vttl_seconds.v);
|
||||||
|
if (out()) {
|
||||||
|
setServiceText({ lang(lng_ttl_video_sent) });
|
||||||
|
} else {
|
||||||
|
auto result = PreparedText();
|
||||||
|
result.links.push_back(fromLink());
|
||||||
|
result.text = lng_ttl_video_received(lt_from, fromLinkText());
|
||||||
|
setServiceText(std::move(result));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setServiceText({ lang(lng_ttl_video_expired) });
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: Unexpected("Media type in HistoryService::createFromMtp()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||||
if (message.vaction.type() == mtpc_messageActionGameScore) {
|
if (message.vaction.type() == mtpc_messageActionGameScore) {
|
||||||
UpdateComponents(HistoryServiceGameScore::Bit());
|
UpdateComponents(HistoryServiceGameScore::Bit());
|
||||||
|
|
|
@ -37,6 +37,16 @@ struct HistoryServicePayment : public RuntimeComponent<HistoryServicePayment>, p
|
||||||
QString amount;
|
QString amount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct HistoryServiceSelfDestruct : public RuntimeComponent<HistoryServiceSelfDestruct> {
|
||||||
|
enum class Type {
|
||||||
|
Photo,
|
||||||
|
Video,
|
||||||
|
};
|
||||||
|
Type type = Type::Photo;
|
||||||
|
TimeMs timeToLive = 0;
|
||||||
|
TimeMs destructAt = 0;
|
||||||
|
};
|
||||||
|
|
||||||
namespace HistoryLayout {
|
namespace HistoryLayout {
|
||||||
class ServiceMessagePainter;
|
class ServiceMessagePainter;
|
||||||
} // namespace HistoryLayout
|
} // namespace HistoryLayout
|
||||||
|
@ -48,6 +58,9 @@ public:
|
||||||
QList<ClickHandlerPtr> links;
|
QList<ClickHandlerPtr> links;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static gsl::not_null<HistoryService*> create(gsl::not_null<History*> history, const MTPDmessage &message) {
|
||||||
|
return _create(history, message);
|
||||||
|
}
|
||||||
static gsl::not_null<HistoryService*> create(gsl::not_null<History*> history, const MTPDmessageService &message) {
|
static gsl::not_null<HistoryService*> create(gsl::not_null<History*> history, const MTPDmessageService &message) {
|
||||||
return _create(history, message);
|
return _create(history, message);
|
||||||
}
|
}
|
||||||
|
@ -83,6 +96,7 @@ public:
|
||||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||||
|
|
||||||
void applyEdition(const MTPDmessageService &message) override;
|
void applyEdition(const MTPDmessageService &message) override;
|
||||||
|
TimeMs getSelfDestructIn(TimeMs now) override;
|
||||||
|
|
||||||
int32 addToOverview(AddToOverviewMethod method) override;
|
int32 addToOverview(AddToOverviewMethod method) override;
|
||||||
void eraseFromOverview() override;
|
void eraseFromOverview() override;
|
||||||
|
@ -102,6 +116,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
friend class HistoryLayout::ServiceMessagePainter;
|
friend class HistoryLayout::ServiceMessagePainter;
|
||||||
|
|
||||||
|
HistoryService(gsl::not_null<History*> history, const MTPDmessage &message);
|
||||||
HistoryService(gsl::not_null<History*> history, const MTPDmessageService &message);
|
HistoryService(gsl::not_null<History*> history, const MTPDmessageService &message);
|
||||||
HistoryService(gsl::not_null<History*> history, MsgId msgId, QDateTime date, const PreparedText &message, MTPDmessage::Flags flags = 0, UserId from = 0, PhotoData *photo = 0);
|
HistoryService(gsl::not_null<History*> history, MsgId msgId, QDateTime date, const PreparedText &message, MTPDmessage::Flags flags = 0, UserId from = 0, PhotoData *photo = 0);
|
||||||
friend class HistoryItemInstantiated<HistoryService>;
|
friend class HistoryItemInstantiated<HistoryService>;
|
||||||
|
@ -109,6 +124,8 @@ protected:
|
||||||
void initDimensions() override;
|
void initDimensions() override;
|
||||||
int resizeContentGetHeight() override;
|
int resizeContentGetHeight() override;
|
||||||
|
|
||||||
|
void markMediaAsReadHook() override;
|
||||||
|
|
||||||
void setServiceText(const PreparedText &prepared);
|
void setServiceText(const PreparedText &prepared);
|
||||||
|
|
||||||
QString fromLinkText() const {
|
QString fromLinkText() const {
|
||||||
|
@ -138,8 +155,10 @@ private:
|
||||||
void updateDependentText();
|
void updateDependentText();
|
||||||
void clearDependency();
|
void clearDependency();
|
||||||
|
|
||||||
|
void createFromMtp(const MTPDmessage &message);
|
||||||
void createFromMtp(const MTPDmessageService &message);
|
void createFromMtp(const MTPDmessageService &message);
|
||||||
void setMessageByAction(const MTPmessageAction &action);
|
void setMessageByAction(const MTPmessageAction &action);
|
||||||
|
void setSelfDestruct(HistoryServiceSelfDestruct::Type type, int ttlSeconds);
|
||||||
|
|
||||||
PreparedText preparePinnedText();
|
PreparedText preparePinnedText();
|
||||||
PreparedText prepareGameScoreText();
|
PreparedText prepareGameScoreText();
|
||||||
|
|
Loading…
Reference in New Issue