mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Better track paused story state.
This commit is contained in:
parent
b8cf00a0b2
commit
8b22f9dcac
14 changed files with 149 additions and 61 deletions
|
@ -588,7 +588,7 @@ void Stories::loadAround(FullStoryId id) {
|
|||
}
|
||||
}
|
||||
|
||||
void Stories::markAsRead(FullStoryId id) {
|
||||
void Stories::markAsRead(FullStoryId id, bool viewed) {
|
||||
const auto i = ranges::find(_all, id.peer, [](const StoriesList &list) {
|
||||
return list.user->id;
|
||||
});
|
||||
|
|
|
@ -115,7 +115,7 @@ public:
|
|||
void resolve(FullStoryId id, Fn<void()> done);
|
||||
|
||||
[[nodiscard]] bool isQuitPrevent();
|
||||
void markAsRead(FullStoryId id);
|
||||
void markAsRead(FullStoryId id, bool viewed);
|
||||
|
||||
private:
|
||||
[[nodiscard]] StoriesList parse(const MTPUserStories &stories);
|
||||
|
|
|
@ -1135,6 +1135,10 @@ rpl::producer<bool> ComposeControls::focusedValue() const {
|
|||
| rpl::then(_focusChanges.events());
|
||||
}
|
||||
|
||||
rpl::producer<bool> ComposeControls::tabbedPanelShownValue() const {
|
||||
return _tabbedPanel ? _tabbedPanel->shownValue() : rpl::single(false);
|
||||
}
|
||||
|
||||
rpl::producer<> ComposeControls::cancelRequests() const {
|
||||
return _cancelRequests.events();
|
||||
}
|
||||
|
@ -2013,9 +2017,12 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
|||
updateControlsGeometry(_wrap->size());
|
||||
});
|
||||
|
||||
const auto hadFocus = Ui::InFocusChain(_field);
|
||||
if (!draft) {
|
||||
clearFieldText(0, fieldHistoryAction);
|
||||
_field->setFocus();
|
||||
if (hadFocus) {
|
||||
_field->setFocus();
|
||||
}
|
||||
_header->editMessage({});
|
||||
_header->replyToMessage({});
|
||||
_canReplaceMedia = false;
|
||||
|
@ -2025,7 +2032,9 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
|
|||
|
||||
_textUpdateEvents = 0;
|
||||
setFieldText(draft->textWithTags, 0, fieldHistoryAction);
|
||||
_field->setFocus();
|
||||
if (hadFocus) {
|
||||
_field->setFocus();
|
||||
}
|
||||
draft->cursor.applyTo(_field);
|
||||
_textUpdateEvents = TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping;
|
||||
if (_preview) {
|
||||
|
@ -2253,11 +2262,13 @@ void ComposeControls::initVoiceRecordBar() {
|
|||
_voiceRecordBar->recordingStateChanges(
|
||||
) | rpl::start_with_next([=](bool active) {
|
||||
if (active) {
|
||||
_recording = true;
|
||||
changeFocusedControl();
|
||||
}
|
||||
_field->setVisible(!active);
|
||||
if (!active) {
|
||||
changeFocusedControl();
|
||||
_recording = false;
|
||||
}
|
||||
}, _wrap->lifetime());
|
||||
|
||||
|
@ -2938,6 +2949,10 @@ bool ComposeControls::isRecording() const {
|
|||
return _voiceRecordBar->isRecording();
|
||||
}
|
||||
|
||||
rpl::producer<bool> ComposeControls::recordingValue() const {
|
||||
return _recording.value();
|
||||
}
|
||||
|
||||
bool ComposeControls::preventsClose(Fn<void()> &&continueCallback) const {
|
||||
if (_voiceRecordBar->isActive()) {
|
||||
_voiceRecordBar->showDiscardBox(std::move(continueCallback));
|
||||
|
|
|
@ -146,6 +146,7 @@ public:
|
|||
|
||||
bool focus();
|
||||
[[nodiscard]] rpl::producer<bool> focusedValue() const;
|
||||
[[nodiscard]] rpl::producer<bool> tabbedPanelShownValue() const;
|
||||
[[nodiscard]] rpl::producer<> cancelRequests() const;
|
||||
[[nodiscard]] rpl::producer<Api::SendOptions> sendRequests() const;
|
||||
[[nodiscard]] rpl::producer<VoiceToSend> sendVoiceRequests() const;
|
||||
|
@ -213,6 +214,7 @@ public:
|
|||
[[nodiscard]] rpl::producer<bool> lockShowStarts() const;
|
||||
[[nodiscard]] bool isLockPresent() const;
|
||||
[[nodiscard]] bool isRecording() const;
|
||||
[[nodiscard]] rpl::producer<bool> recordingValue() const;
|
||||
|
||||
void applyCloudDraft();
|
||||
void applyDraft(
|
||||
|
@ -379,6 +381,7 @@ private:
|
|||
rpl::event_stream<std::optional<bool>> _attachRequests;
|
||||
rpl::event_stream<ReplyNextRequest> _replyNextRequests;
|
||||
rpl::event_stream<> _focusRequests;
|
||||
rpl::variable<bool> _recording;
|
||||
|
||||
TextUpdateEvents _textUpdateEvents = TextUpdateEvents()
|
||||
| TextUpdateEvent::SaveDraft
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "base/timer.h"
|
||||
#include "base/power_save_blocker.h"
|
||||
#include "base/qt_signal_producer.h"
|
||||
#include "chat_helpers/compose/compose_show.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_file_origin.h"
|
||||
|
@ -29,6 +30,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_boxes.h" // UserpicButton
|
||||
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
namespace Media::Stories {
|
||||
namespace {
|
||||
|
||||
|
@ -123,27 +126,52 @@ Controller::Controller(not_null<Delegate*> delegate)
|
|||
, _replyArea(std::make_unique<ReplyArea>(this)) {
|
||||
initLayout();
|
||||
|
||||
_replyArea->focusedValue(
|
||||
) | rpl::start_with_next([=](bool focused) {
|
||||
if (focused) {
|
||||
_replyArea->activeValue(
|
||||
) | rpl::start_with_next([=](bool active) {
|
||||
if (active) {
|
||||
_captionFullView = nullptr;
|
||||
}
|
||||
_contentFaded = focused;
|
||||
_contentFadeAnimation.start(
|
||||
[=] { _delegate->storiesRepaint(); },
|
||||
focused ? 0. : 1.,
|
||||
focused ? 1. : 0.,
|
||||
st::fadeWrapDuration);
|
||||
if (_started) {
|
||||
togglePaused(focused);
|
||||
}
|
||||
_replyActive = active;
|
||||
updateContentFaded();
|
||||
}, _lifetime);
|
||||
|
||||
_replyArea->focusedValue(
|
||||
) | rpl::start_with_next([=](bool focused) {
|
||||
_replyFocused = focused;
|
||||
}, _lifetime);
|
||||
|
||||
_delegate->storiesLayerShown(
|
||||
) | rpl::start_with_next([=](bool shown) {
|
||||
_layerShown = shown;
|
||||
updatePlayingAllowed();
|
||||
}, _lifetime);
|
||||
|
||||
const auto window = _wrap->window()->windowHandle();
|
||||
Assert(window != nullptr);
|
||||
base::qt_signal_producer(
|
||||
window,
|
||||
&QWindow::activeChanged
|
||||
) | rpl::start_with_next([=] {
|
||||
_windowActive = window->isActive();
|
||||
updatePlayingAllowed();
|
||||
}, _lifetime);
|
||||
_windowActive = window->isActive();
|
||||
|
||||
_contentFadeAnimation.stop();
|
||||
}
|
||||
|
||||
Controller::~Controller() = default;
|
||||
|
||||
void Controller::updateContentFaded() {
|
||||
_contentFaded = _replyActive;
|
||||
_contentFadeAnimation.start(
|
||||
[=] { _delegate->storiesRepaint(); },
|
||||
_contentFaded ? 0. : 1.,
|
||||
_contentFaded ? 1. : 0.,
|
||||
st::fadeWrapDuration);
|
||||
updatePlayingAllowed();
|
||||
}
|
||||
|
||||
void Controller::initLayout() {
|
||||
const auto headerHeight = st::storiesHeaderMargin.top()
|
||||
+ st::storiesHeaderPhoto.photoSize
|
||||
|
@ -373,6 +401,7 @@ void Controller::show(
|
|||
}
|
||||
const auto story = *maybeStory;
|
||||
const auto guard = gsl::finally([&] {
|
||||
_paused = false;
|
||||
_started = false;
|
||||
if (story->photo()) {
|
||||
_photoPlayback = std::make_unique<PhotoPlayback>(this);
|
||||
|
@ -421,10 +450,33 @@ void Controller::show(
|
|||
}
|
||||
stories.loadAround(storyId);
|
||||
|
||||
list.user->updateFull();
|
||||
if (_replyFocused) {
|
||||
unfocusReply();
|
||||
}
|
||||
updatePlayingAllowed();
|
||||
|
||||
if (_contentFaded) {
|
||||
togglePaused(true);
|
||||
list.user->updateFull();
|
||||
}
|
||||
|
||||
void Controller::updatePlayingAllowed() {
|
||||
if (!_shown) {
|
||||
return;
|
||||
}
|
||||
setPlayingAllowed(_started
|
||||
&& _windowActive
|
||||
&& !_paused
|
||||
&& !_replyActive
|
||||
&& !_layerShown);
|
||||
}
|
||||
|
||||
void Controller::setPlayingAllowed(bool allowed) {
|
||||
if (allowed) {
|
||||
_captionFullView = nullptr;
|
||||
}
|
||||
if (_photoPlayback) {
|
||||
_photoPlayback->togglePaused(!allowed);
|
||||
} else {
|
||||
_delegate->storiesTogglePaused(!allowed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,9 +504,7 @@ void Controller::ready() {
|
|||
return;
|
||||
}
|
||||
_started = true;
|
||||
if (!_contentFaded && _photoPlayback) {
|
||||
_photoPlayback->togglePaused(false);
|
||||
}
|
||||
updatePlayingAllowed();
|
||||
}
|
||||
|
||||
void Controller::updateVideoPlayback(const Player::TrackState &state) {
|
||||
|
@ -493,7 +543,7 @@ void Controller::maybeMarkAsRead(const Player::TrackState &state) {
|
|||
void Controller::markAsRead() {
|
||||
Expects(_list.has_value());
|
||||
|
||||
_list->user->owner().stories().markAsRead(_shown);
|
||||
_list->user->owner().stories().markAsRead(_shown, _started);
|
||||
}
|
||||
|
||||
bool Controller::subjumpAvailable(int delta) const {
|
||||
|
@ -551,21 +601,11 @@ void Controller::checkWaitingFor() {
|
|||
Expects(_list.has_value());
|
||||
|
||||
auto &stories = _list->user->owner().stories();
|
||||
const auto &all = stories.all();
|
||||
const auto i = ranges::find_if(all, [&](const Data::StoriesList &data) {
|
||||
return data.user->id == _waitingForId.peer;
|
||||
});
|
||||
if (i == end(all)) {
|
||||
_waitingForId = {};
|
||||
return;
|
||||
}
|
||||
const auto j = ranges::find(i->ids, _waitingForId.story);
|
||||
if (j == end(i->ids)) {
|
||||
_waitingForId = {};
|
||||
return;
|
||||
}
|
||||
const auto maybe = stories.lookup(_waitingForId);
|
||||
if (!maybe) {
|
||||
if (maybe.error() == Data::NoStory::Deleted) {
|
||||
_waitingForId = {};
|
||||
}
|
||||
return;
|
||||
}
|
||||
_delegate->storiesJumpTo(
|
||||
|
@ -596,19 +636,13 @@ bool Controller::jumpFor(int delta) {
|
|||
}
|
||||
|
||||
bool Controller::paused() const {
|
||||
return _photoPlayback
|
||||
? _photoPlayback->paused()
|
||||
: _delegate->storiesPaused();
|
||||
return _paused;
|
||||
}
|
||||
|
||||
void Controller::togglePaused(bool paused) {
|
||||
if (!paused) {
|
||||
_captionFullView = nullptr;
|
||||
}
|
||||
if (_photoPlayback) {
|
||||
_photoPlayback->togglePaused(paused);
|
||||
} else {
|
||||
_delegate->storiesTogglePaused(paused);
|
||||
if (_paused != paused) {
|
||||
_paused = paused;
|
||||
updatePlayingAllowed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -126,6 +126,10 @@ private:
|
|||
void maybeMarkAsRead(const Player::TrackState &state);
|
||||
void markAsRead();
|
||||
|
||||
void updateContentFaded();
|
||||
void updatePlayingAllowed();
|
||||
void setPlayingAllowed(bool allowed);
|
||||
|
||||
void showSiblings(
|
||||
const std::vector<Data::StoriesList> &lists,
|
||||
int index);
|
||||
|
@ -150,6 +154,12 @@ private:
|
|||
Ui::Animations::Simple _contentFadeAnimation;
|
||||
bool _contentFaded = false;
|
||||
|
||||
bool _windowActive = false;
|
||||
bool _replyFocused = false;
|
||||
bool _replyActive = false;
|
||||
bool _layerShown = false;
|
||||
bool _paused = false;
|
||||
|
||||
FullStoryId _shown;
|
||||
TextWithEntities _captionText;
|
||||
std::optional<Data::StoriesList> _list;
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
FullStoryId id) = 0;
|
||||
virtual void storiesClose() = 0;
|
||||
[[nodiscard]] virtual bool storiesPaused() = 0;
|
||||
[[nodiscard]] virtual rpl::producer<bool> storiesLayerShown() = 0;
|
||||
[[nodiscard]] virtual float64 storiesSiblingOver(SiblingType type) = 0;
|
||||
virtual void storiesTogglePaused(bool paused) = 0;
|
||||
virtual void storiesRepaint() = 0;
|
||||
|
|
|
@ -299,11 +299,11 @@ Api::SendAction ReplyArea::prepareSendAction(
|
|||
|
||||
void ReplyArea::chooseAttach(
|
||||
std::optional<bool> overrideSendImagesAsPhotos) {
|
||||
_chooseAttachRequest = false;
|
||||
if (!_data.user) {
|
||||
return;
|
||||
}
|
||||
const auto user = not_null(_data.user);
|
||||
_choosingAttach = false;
|
||||
if (const auto error = Data::AnyFileRestrictionError(user)) {
|
||||
_controller->uiShow()->showToast(*error);
|
||||
return;
|
||||
|
@ -312,15 +312,18 @@ void ReplyArea::chooseAttach(
|
|||
const auto filter = (overrideSendImagesAsPhotos == true)
|
||||
? FileDialog::ImagesOrAllFilter()
|
||||
: FileDialog::AllOrImagesFilter();
|
||||
const auto weak = make_weak(&_shownUserGuard);
|
||||
const auto callback = [=](FileDialog::OpenResult &&result) {
|
||||
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
_choosingAttach = false;
|
||||
});
|
||||
if (!weak
|
||||
|| (result.paths.isEmpty() && result.remoteContent.isEmpty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.remoteContent.isEmpty()) {
|
||||
} else if (!result.remoteContent.isEmpty()) {
|
||||
auto read = Images::Read({
|
||||
.content = result.remoteContent,
|
||||
});
|
||||
});
|
||||
if (!read.image.isNull() && !read.animated) {
|
||||
confirmSendingFiles(
|
||||
std::move(read.image),
|
||||
|
@ -339,12 +342,14 @@ void ReplyArea::chooseAttach(
|
|||
confirmSendingFiles(std::move(list));
|
||||
}
|
||||
};
|
||||
|
||||
_choosingAttach = true;
|
||||
FileDialog::GetOpenPaths(
|
||||
_controller->wrap().get(),
|
||||
tr::lng_choose_files(tr::now),
|
||||
filter,
|
||||
crl::guard(&_shownUserGuard, callback),
|
||||
nullptr);
|
||||
crl::guard(this, callback),
|
||||
crl::guard(this, [=] { _choosingAttach = false; }));
|
||||
}
|
||||
|
||||
bool ReplyArea::confirmSendingFiles(
|
||||
|
@ -488,9 +493,9 @@ void ReplyArea::initActions() {
|
|||
|
||||
_controls->attachRequests(
|
||||
) | rpl::filter([=] {
|
||||
return !_choosingAttach;
|
||||
return !_chooseAttachRequest;
|
||||
}) | rpl::start_with_next([=](std::optional<bool> overrideCompress) {
|
||||
_choosingAttach = true;
|
||||
_chooseAttachRequest = true;
|
||||
base::call_delayed(
|
||||
st::storiesAttach.ripple.hideDuration,
|
||||
this,
|
||||
|
@ -569,6 +574,17 @@ rpl::producer<bool> ReplyArea::focusedValue() const {
|
|||
return _controls->focusedValue();
|
||||
}
|
||||
|
||||
rpl::producer<bool> ReplyArea::activeValue() const {
|
||||
using namespace rpl::mappers;
|
||||
return rpl::combine(
|
||||
_controls->focusedValue(),
|
||||
_controls->recordingValue(),
|
||||
_controls->tabbedPanelShownValue(),
|
||||
_choosingAttach.value(),
|
||||
_1 || _2 || _3 || _4
|
||||
) | rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
void ReplyArea::showPremiumToast(not_null<DocumentData*> emoji) {
|
||||
// #TODO stories
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
void show(ReplyAreaData data);
|
||||
|
||||
[[nodiscard]] rpl::producer<bool> focusedValue() const;
|
||||
[[nodiscard]] rpl::producer<bool> activeValue() const;
|
||||
|
||||
private:
|
||||
using VoiceToSend = HistoryView::Controls::VoiceToSend;
|
||||
|
@ -130,7 +131,8 @@ private:
|
|||
|
||||
ReplyAreaData _data;
|
||||
base::has_weak_ptr _shownUserGuard;
|
||||
bool _choosingAttach = false;
|
||||
bool _chooseAttachRequest = false;
|
||||
rpl::variable<bool> _choosingAttach;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
|
|
|
@ -599,6 +599,7 @@ storiesComposeControls: ComposeControls(defaultComposeControls) {
|
|||
fadeLeft: icon {{ "fade_horizontal-flip_horizontal", storiesComposeBgOver }};
|
||||
fadeRight: icon {{ "fade_horizontal", storiesComposeBgOver }};
|
||||
field: InputField(defaultTabbedSearchField) {
|
||||
textFg: storiesComposeWhiteText;
|
||||
placeholderFg: storiesComposeGrayText;
|
||||
placeholderFgActive: storiesComposeGrayText;
|
||||
placeholderFgError: storiesComposeGrayText;
|
||||
|
|
|
@ -431,9 +431,10 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent(
|
|||
Ui::GL::kFormatRGBA,
|
||||
Ui::GL::kFormatRGBA,
|
||||
QSize(2, 2),
|
||||
_rgbaSize,
|
||||
_rgbaSize[index],
|
||||
stride,
|
||||
data);
|
||||
_rgbaSize[index] = QSize(2, 2);
|
||||
} else {
|
||||
const auto stride = image.bytesPerLine() / 4;
|
||||
const auto data = image.constBits();
|
||||
|
@ -441,10 +442,10 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent(
|
|||
Ui::GL::kFormatRGBA,
|
||||
Ui::GL::kFormatRGBA,
|
||||
image.size(),
|
||||
_rgbaSize,
|
||||
_rgbaSize[index],
|
||||
stride,
|
||||
data);
|
||||
_rgbaSize = image.size();
|
||||
_rgbaSize[index] = image.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ private:
|
|||
std::optional<QOpenGLShaderProgram> _controlsProgram;
|
||||
std::optional<QOpenGLShaderProgram> _roundedCornersProgram;
|
||||
Ui::GL::Textures<6> _textures; // image, sibling, right sibling, y, u, v
|
||||
QSize _rgbaSize;
|
||||
QSize _rgbaSize[3];
|
||||
QSize _lumaSize;
|
||||
QSize _chromaSize;
|
||||
qint64 _cacheKeys[3] = { 0 }; // image, sibling, right sibling
|
||||
|
|
|
@ -4053,6 +4053,10 @@ bool OverlayWidget::storiesPaused() {
|
|||
&& _streamed->instance.player().paused();
|
||||
}
|
||||
|
||||
rpl::producer<bool> OverlayWidget::storiesLayerShown() {
|
||||
return _layerBg->layerShownValue();
|
||||
}
|
||||
|
||||
void OverlayWidget::storiesTogglePaused(bool paused) {
|
||||
if (!_streamed
|
||||
|| _streamed->instance.player().failed()
|
||||
|
|
|
@ -247,6 +247,7 @@ private:
|
|||
FullStoryId id) override;
|
||||
void storiesClose() override;
|
||||
bool storiesPaused() override;
|
||||
rpl::producer<bool> storiesLayerShown() override;
|
||||
void storiesTogglePaused(bool paused) override;
|
||||
float64 storiesSiblingOver(Stories::SiblingType type) override;
|
||||
void storiesRepaint() override;
|
||||
|
|
Loading…
Add table
Reference in a new issue