Better track paused story state.

This commit is contained in:
John Preston 2023-05-30 17:57:11 +04:00
parent b8cf00a0b2
commit 8b22f9dcac
14 changed files with 149 additions and 61 deletions

View File

@ -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;
});

View File

@ -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);

View File

@ -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));

View File

@ -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

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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
}

View File

@ -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;

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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

View File

@ -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()

View File

@ -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;