mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Add author to the top of Reply in Another Chat.
This commit is contained in:
parent
511cfc524f
commit
f74dd3ca1e
8 changed files with 201 additions and 13 deletions
|
@ -3684,6 +3684,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
"lng_reply_in_another_title" = "Reply in...";
|
||||
"lng_reply_in_another_chat" = "Reply in Another Chat";
|
||||
"lng_reply_in_author" = "Message author";
|
||||
"lng_reply_in_chats_list" = "Your chats";
|
||||
"lng_reply_show_in_chat" = "Show in Chat";
|
||||
"lng_reply_remove" = "Do Not Reply";
|
||||
"lng_reply_about_quote" = "You can select a specific part to quote.";
|
||||
|
|
|
@ -878,6 +878,13 @@ peerListWithInviteViaLink: PeerList(peerListBox) {
|
|||
peerListSingleRow: PeerList(peerListBox) {
|
||||
padding: margins(0px, 0px, 0px, 0px);
|
||||
}
|
||||
peerListSmallSkips: PeerList(peerListBox) {
|
||||
padding: margins(
|
||||
0px,
|
||||
defaultVerticalListSkip,
|
||||
0px,
|
||||
defaultVerticalListSkip);
|
||||
}
|
||||
|
||||
scheduleHeight: 95px;
|
||||
scheduleDateTop: 38px;
|
||||
|
|
|
@ -1944,6 +1944,13 @@ PeerListContent::SkipResult PeerListContent::selectSkip(int direction) {
|
|||
}
|
||||
}
|
||||
|
||||
if (_controller->overrideKeyboardNavigation(
|
||||
direction,
|
||||
_selected.index.value,
|
||||
newSelectedIndex)) {
|
||||
return { _selected.index.value, _selected.index.value };
|
||||
}
|
||||
|
||||
_selected.index.value = newSelectedIndex;
|
||||
_selected.element = 0;
|
||||
if (newSelectedIndex >= 0) {
|
||||
|
|
|
@ -357,6 +357,8 @@ public:
|
|||
virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0;
|
||||
virtual std::shared_ptr<Main::SessionShow> peerListUiShow() = 0;
|
||||
|
||||
virtual void peerListSelectSkip(int direction) = 0;
|
||||
|
||||
virtual void peerListPressLeftToContextMenu(bool shown) = 0;
|
||||
virtual bool peerListTrackRowPressFromGlobal(QPoint globalPosition) = 0;
|
||||
|
||||
|
@ -573,6 +575,13 @@ public:
|
|||
Unexpected("PeerListController::customRowRippleMaskGenerator.");
|
||||
}
|
||||
|
||||
virtual bool overrideKeyboardNavigation(
|
||||
int direction,
|
||||
int fromIndex,
|
||||
int toIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
@ -1016,6 +1025,10 @@ public:
|
|||
bool highlightRow,
|
||||
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override;
|
||||
|
||||
void peerListSelectSkip(int direction) override {
|
||||
_content->selectSkip(direction);
|
||||
}
|
||||
|
||||
void peerListPressLeftToContextMenu(bool shown) override {
|
||||
_content->pressLeftToContextMenu(shown);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "base/timer_rpl.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/filters/edit_filter_chats_list.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "chat_helpers/compose/compose_show.h"
|
||||
|
@ -41,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "window/themes/window_theme.h"
|
||||
#include "window/section_widget.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
@ -911,6 +913,117 @@ void DraftOptionsBox(
|
|||
}, box->lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
struct AuthorSelector {
|
||||
object_ptr<Ui::RpWidget> content = { nullptr };
|
||||
Fn<bool(int, int, int)> overrideKey;
|
||||
};
|
||||
[[nodiscard]] AuthorSelector AuthorRowSelector(
|
||||
not_null<Main::Session*> session,
|
||||
FullReplyTo reply,
|
||||
Fn<void(not_null<Data::Thread*>)> chosen) {
|
||||
const auto item = session->data().message(reply.messageId);
|
||||
if (!item) {
|
||||
return {};
|
||||
}
|
||||
const auto displayFrom = item->displayFrom();
|
||||
const auto from = displayFrom ? displayFrom : item->from().get();
|
||||
if (!from->isUser() || from == item->history()->peer || from->isSelf()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
class AuthorController final : public PeerListController {
|
||||
public:
|
||||
AuthorController(not_null<PeerData*> peer, Fn<void()> click)
|
||||
: _peer(peer)
|
||||
, _click(std::move(click)) {
|
||||
}
|
||||
|
||||
void prepare() override {
|
||||
delegate()->peerListAppendRow(
|
||||
std::make_unique<ChatsListBoxController::Row>(
|
||||
_peer->owner().history(_peer),
|
||||
&computeListSt().item));
|
||||
delegate()->peerListRefreshRows();
|
||||
TrackPremiumRequiredChanges(this, _lifetime);
|
||||
}
|
||||
void loadMoreRows() override {
|
||||
}
|
||||
void rowClicked(not_null<PeerListRow*> row) override {
|
||||
if (RecipientRow::ShowLockedError(this, row, WritePremiumRequiredError)) {
|
||||
return;
|
||||
} else if (const auto onstack = _click) {
|
||||
onstack();
|
||||
}
|
||||
}
|
||||
Main::Session &session() const override {
|
||||
return _peer->session();
|
||||
}
|
||||
|
||||
private:
|
||||
const not_null<PeerData*> _peer;
|
||||
Fn<void()> _click;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
|
||||
const auto container = result.data();
|
||||
|
||||
container->add(CreatePeerListSectionSubtitle(
|
||||
container,
|
||||
tr::lng_reply_in_author()));
|
||||
Ui::AddSkip(container);
|
||||
|
||||
const auto delegate = container->lifetime().make_state<
|
||||
PeerListContentDelegateSimple
|
||||
>();
|
||||
const auto controller = container->lifetime().make_state<
|
||||
AuthorController
|
||||
>(from, [=] { chosen(from->owner().history(from)); });
|
||||
controller->setStyleOverrides(&st::peerListSingleRow);
|
||||
const auto content = container->add(object_ptr<PeerListContent>(
|
||||
container,
|
||||
controller));
|
||||
delegate->setContent(content);
|
||||
controller->setDelegate(delegate);
|
||||
|
||||
Ui::AddSkip(container);
|
||||
container->add(CreatePeerListSectionSubtitle(
|
||||
container,
|
||||
tr::lng_reply_in_chats_list()));
|
||||
|
||||
const auto overrideKey = [=](int direction, int from, int to) {
|
||||
if (!content->isVisible()) {
|
||||
return false;
|
||||
} else if (direction > 0 && from < 0 && to >= 0) {
|
||||
if (content->hasSelection()) {
|
||||
const auto was = content->selectedIndex();
|
||||
const auto now = content->selectSkip(1).reallyMovedTo;
|
||||
if (was != now) {
|
||||
return true;
|
||||
}
|
||||
content->clearSelection();
|
||||
} else {
|
||||
content->selectSkip(1);
|
||||
return true;
|
||||
}
|
||||
} else if (direction < 0 && to < 0) {
|
||||
if (!content->hasSelection()) {
|
||||
content->selectLast();
|
||||
} else if (from >= 0 || content->hasSelection()) {
|
||||
content->selectSkip(-1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return {
|
||||
.content = std::move(result),
|
||||
.overrideKey = overrideKey,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ShowReplyToChatBox(
|
||||
|
@ -921,7 +1034,7 @@ void ShowReplyToChatBox(
|
|||
public:
|
||||
using Chosen = not_null<Data::Thread*>;
|
||||
|
||||
Controller(not_null<Main::Session*> session)
|
||||
Controller(not_null<Main::Session*> session, FullReplyTo reply)
|
||||
: ChooseRecipientBoxController({
|
||||
.session = session,
|
||||
.callback = [=](Chosen thread) {
|
||||
|
@ -929,6 +1042,13 @@ void ShowReplyToChatBox(
|
|||
},
|
||||
.premiumRequiredError = WritePremiumRequiredError,
|
||||
}) {
|
||||
_authorRow = AuthorRowSelector(
|
||||
session,
|
||||
reply,
|
||||
[=](Chosen thread) { _singleChosen.fire_copy(thread); });
|
||||
if (_authorRow.content) {
|
||||
setStyleOverrides(&st::peerListSmallSkips);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<Chosen> singleChosen() const {
|
||||
|
@ -939,13 +1059,26 @@ void ShowReplyToChatBox(
|
|||
return tr::lng_saved_quote_here(tr::now);
|
||||
}
|
||||
|
||||
bool overrideKeyboardNavigation(
|
||||
int direction,
|
||||
int fromIndex,
|
||||
int toIndex) override {
|
||||
return _authorRow.overrideKey
|
||||
&& _authorRow.overrideKey(direction, fromIndex, toIndex);
|
||||
}
|
||||
|
||||
private:
|
||||
void prepareViewHook() override {
|
||||
if (_authorRow.content) {
|
||||
delegate()->peerListSetAboveWidget(
|
||||
std::move(_authorRow.content));
|
||||
}
|
||||
ChooseRecipientBoxController::prepareViewHook();
|
||||
delegate()->peerListSetTitle(tr::lng_reply_in_another_title());
|
||||
}
|
||||
|
||||
rpl::event_stream<Chosen> _singleChosen;
|
||||
AuthorSelector _authorRow;
|
||||
|
||||
};
|
||||
|
||||
|
@ -956,7 +1089,7 @@ void ShowReplyToChatBox(
|
|||
};
|
||||
const auto session = &show->session();
|
||||
const auto state = [&] {
|
||||
auto controller = std::make_unique<Controller>(session);
|
||||
auto controller = std::make_unique<Controller>(session, reply);
|
||||
const auto controllerRaw = controller.get();
|
||||
auto box = Box<PeerListBox>(std::move(controller), [=](
|
||||
not_null<PeerListBox*> box) {
|
||||
|
|
|
@ -1704,6 +1704,9 @@ void VoiceRecordBar::startRecording() {
|
|||
) | rpl::start_with_next_error([=](const Update &update) {
|
||||
_recordingTipRequired = (update.samples < kMinSamples);
|
||||
recordUpdated(update.level, update.samples);
|
||||
if (update.finished) {
|
||||
stop(true);
|
||||
}
|
||||
}, [=] {
|
||||
stop(false);
|
||||
}, _recordingLifetime);
|
||||
|
|
|
@ -16,6 +16,8 @@ namespace Capture {
|
|||
struct Update {
|
||||
int samples = 0;
|
||||
ushort level = 0;
|
||||
|
||||
bool finished = false;
|
||||
};
|
||||
|
||||
struct Chunk {
|
||||
|
|
|
@ -24,6 +24,7 @@ constexpr auto kUpdateEach = crl::time(100);
|
|||
constexpr auto kAudioFrequency = 48'000;
|
||||
constexpr auto kAudioBitRate = 32'000;
|
||||
constexpr auto kVideoBitRate = 3 * 1024 * 1024;
|
||||
constexpr auto kMaxDuration = 10 * crl::time(1000); AssertIsDebug();
|
||||
|
||||
using namespace FFmpeg;
|
||||
|
||||
|
@ -52,6 +53,7 @@ private:
|
|||
void initEncoding();
|
||||
bool initVideo();
|
||||
bool initAudio();
|
||||
void notifyFinished();
|
||||
void deinitEncoding();
|
||||
void finishEncoding();
|
||||
void fail();
|
||||
|
@ -66,6 +68,9 @@ private:
|
|||
void updateMaxLevel(const Media::Capture::Chunk &chunk);
|
||||
void updateResultDuration(int64 pts, AVRational timeBase);
|
||||
|
||||
void cutCircleFromYUV420P(not_null<AVFrame*> frame);
|
||||
void initCircleMask();
|
||||
|
||||
const crl::weak_on_queue<Private> _weak;
|
||||
|
||||
FormatPointer _format;
|
||||
|
@ -95,14 +100,12 @@ private:
|
|||
QByteArray _result;
|
||||
int64_t _resultOffset = 0;
|
||||
crl::time _resultDuration = 0;
|
||||
bool _finished = false;
|
||||
|
||||
ushort _maxLevelSinceLastUpdate = 0;
|
||||
crl::time _lastUpdateDuration = 0;
|
||||
rpl::event_stream<Update, rpl::empty_error> _updates;
|
||||
|
||||
void cutCircleFromYUV420P(not_null<AVFrame*> frame);
|
||||
void initCircleMask();
|
||||
|
||||
std::vector<bool> _circleMask; // Always nice to use vector<bool>! :D
|
||||
|
||||
};
|
||||
|
@ -400,7 +403,7 @@ void RoundVideoRecorder::Private::deinitEncoding() {
|
|||
void RoundVideoRecorder::Private::push(
|
||||
int64 mcstimestamp,
|
||||
const QImage &frame) {
|
||||
if (!_format) {
|
||||
if (!_format || _finished) {
|
||||
return;
|
||||
} else if (!_firstAudioChunkFinished) {
|
||||
// Skip frames while we didn't start receiving audio.
|
||||
|
@ -412,7 +415,7 @@ void RoundVideoRecorder::Private::push(
|
|||
}
|
||||
|
||||
void RoundVideoRecorder::Private::push(const Media::Capture::Chunk &chunk) {
|
||||
if (!_format) {
|
||||
if (!_format || _finished) {
|
||||
return;
|
||||
} else if (!_firstAudioChunkFinished || !_firstVideoFrameTime) {
|
||||
_firstAudioChunkFinished = chunk.finished;
|
||||
|
@ -425,6 +428,11 @@ void RoundVideoRecorder::Private::push(const Media::Capture::Chunk &chunk) {
|
|||
void RoundVideoRecorder::Private::encodeVideoFrame(
|
||||
int64 mcstimestamp,
|
||||
const QImage &frame) {
|
||||
Expects(!_finished);
|
||||
|
||||
if (_videoFirstTimestamp == -1) {
|
||||
_videoFirstTimestamp = mcstimestamp;
|
||||
}
|
||||
const auto fwidth = frame.width();
|
||||
const auto fheight = frame.height();
|
||||
const auto fmin = std::min(fwidth, fheight);
|
||||
|
@ -443,10 +451,6 @@ void RoundVideoRecorder::Private::encodeVideoFrame(
|
|||
return;
|
||||
}
|
||||
|
||||
if (_videoFirstTimestamp == -1) {
|
||||
_videoFirstTimestamp = mcstimestamp;
|
||||
}
|
||||
|
||||
const auto cdata = frame.constBits()
|
||||
+ (frame.bytesPerLine() * fy)
|
||||
+ (fx * frame.depth() / 8);
|
||||
|
@ -466,7 +470,10 @@ void RoundVideoRecorder::Private::encodeVideoFrame(
|
|||
cutCircleFromYUV420P(_videoFrame.get());
|
||||
|
||||
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
||||
if (!writeFrame(_videoFrame, _videoCodec, _videoStream)) {
|
||||
if (_videoFrame->pts >= kMaxDuration * int64(1000)) {
|
||||
notifyFinished();
|
||||
return;
|
||||
} else if (!writeFrame(_videoFrame, _videoCodec, _videoStream)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -534,6 +541,8 @@ void RoundVideoRecorder::Private::cutCircleFromYUV420P(
|
|||
|
||||
void RoundVideoRecorder::Private::encodeAudioFrame(
|
||||
const Media::Capture::Chunk &chunk) {
|
||||
Expects(!_finished);
|
||||
|
||||
updateMaxLevel(chunk);
|
||||
|
||||
if (_audioTail.isEmpty()) {
|
||||
|
@ -578,7 +587,10 @@ void RoundVideoRecorder::Private::encodeAudioFrame(
|
|||
|
||||
_audioFrame->pts = _audioPts;
|
||||
_audioPts += _audioFrame->nb_samples;
|
||||
if (!writeFrame(_audioFrame, _audioCodec, _audioStream)) {
|
||||
if (_audioPts >= kMaxDuration * int64(kAudioFrequency) / 1000) {
|
||||
notifyFinished();
|
||||
return;
|
||||
} else if (!writeFrame(_audioFrame, _audioCodec, _audioStream)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -593,6 +605,15 @@ void RoundVideoRecorder::Private::encodeAudioFrame(
|
|||
}
|
||||
}
|
||||
|
||||
void RoundVideoRecorder::Private::notifyFinished() {
|
||||
_finished = true;
|
||||
_updates.fire({
|
||||
.samples = int(_resultDuration * 48),
|
||||
.level = base::take(_maxLevelSinceLastUpdate),
|
||||
.finished = true,
|
||||
});
|
||||
}
|
||||
|
||||
bool RoundVideoRecorder::Private::writeFrame(
|
||||
const FramePointer &frame,
|
||||
const CodecPointer &codec,
|
||||
|
|
Loading…
Add table
Reference in a new issue