diff --git a/Telegram/SourceFiles/data/data_stories.cpp b/Telegram/SourceFiles/data/data_stories.cpp index 82b6c91b3..dd6d80fbb 100644 --- a/Telegram/SourceFiles/data/data_stories.cpp +++ b/Telegram/SourceFiles/data/data_stories.cpp @@ -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; }); diff --git a/Telegram/SourceFiles/data/data_stories.h b/Telegram/SourceFiles/data/data_stories.h index 44da5e7e3..7a4546a0b 100644 --- a/Telegram/SourceFiles/data/data_stories.h +++ b/Telegram/SourceFiles/data/data_stories.h @@ -115,7 +115,7 @@ public: void resolve(FullStoryId id, Fn done); [[nodiscard]] bool isQuitPrevent(); - void markAsRead(FullStoryId id); + void markAsRead(FullStoryId id, bool viewed); private: [[nodiscard]] StoriesList parse(const MTPUserStories &stories); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 23a57dc06..45c07c9ee 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1135,6 +1135,10 @@ rpl::producer ComposeControls::focusedValue() const { | rpl::then(_focusChanges.events()); } +rpl::producer 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 ComposeControls::recordingValue() const { + return _recording.value(); +} + bool ComposeControls::preventsClose(Fn &&continueCallback) const { if (_voiceRecordBar->isActive()) { _voiceRecordBar->showDiscardBox(std::move(continueCallback)); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index d0696bef6..11b2efe22 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -146,6 +146,7 @@ public: bool focus(); [[nodiscard]] rpl::producer focusedValue() const; + [[nodiscard]] rpl::producer tabbedPanelShownValue() const; [[nodiscard]] rpl::producer<> cancelRequests() const; [[nodiscard]] rpl::producer sendRequests() const; [[nodiscard]] rpl::producer sendVoiceRequests() const; @@ -213,6 +214,7 @@ public: [[nodiscard]] rpl::producer lockShowStarts() const; [[nodiscard]] bool isLockPresent() const; [[nodiscard]] bool isRecording() const; + [[nodiscard]] rpl::producer recordingValue() const; void applyCloudDraft(); void applyDraft( @@ -379,6 +381,7 @@ private: rpl::event_stream> _attachRequests; rpl::event_stream _replyNextRequests; rpl::event_stream<> _focusRequests; + rpl::variable _recording; TextUpdateEvents _textUpdateEvents = TextUpdateEvents() | TextUpdateEvent::SaveDraft diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp index 35d2f3709..6921f7b80 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp @@ -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 + namespace Media::Stories { namespace { @@ -123,27 +126,52 @@ Controller::Controller(not_null delegate) , _replyArea(std::make_unique(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(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(); } } diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.h b/Telegram/SourceFiles/media/stories/media_stories_controller.h index f381e51c1..47adde27e 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.h +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.h @@ -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 &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 _list; diff --git a/Telegram/SourceFiles/media/stories/media_stories_delegate.h b/Telegram/SourceFiles/media/stories/media_stories_delegate.h index a9c3b0a35..549788c84 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_delegate.h +++ b/Telegram/SourceFiles/media/stories/media_stories_delegate.h @@ -44,6 +44,7 @@ public: FullStoryId id) = 0; virtual void storiesClose() = 0; [[nodiscard]] virtual bool storiesPaused() = 0; + [[nodiscard]] virtual rpl::producer storiesLayerShown() = 0; [[nodiscard]] virtual float64 storiesSiblingOver(SiblingType type) = 0; virtual void storiesTogglePaused(bool paused) = 0; virtual void storiesRepaint() = 0; diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp index 105f61def..92abe1133 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp @@ -299,11 +299,11 @@ Api::SendAction ReplyArea::prepareSendAction( void ReplyArea::chooseAttach( std::optional 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 overrideCompress) { - _choosingAttach = true; + _chooseAttachRequest = true; base::call_delayed( st::storiesAttach.ripple.hideDuration, this, @@ -569,6 +574,17 @@ rpl::producer ReplyArea::focusedValue() const { return _controls->focusedValue(); } +rpl::producer 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 emoji) { // #TODO stories } diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.h b/Telegram/SourceFiles/media/stories/media_stories_reply.h index d9683521e..d0cef32c3 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.h +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.h @@ -58,6 +58,7 @@ public: void show(ReplyAreaData data); [[nodiscard]] rpl::producer focusedValue() const; + [[nodiscard]] rpl::producer 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 _choosingAttach; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index c822bcf0d..bded9e12e 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -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; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp index e2f2bff04..a1bdce930 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp @@ -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(); } } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h index 95d57ec87..df6f2aa71 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h @@ -124,7 +124,7 @@ private: std::optional _controlsProgram; std::optional _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 diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index f2efd4578..df290b574 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -4053,6 +4053,10 @@ bool OverlayWidget::storiesPaused() { && _streamed->instance.player().paused(); } +rpl::producer OverlayWidget::storiesLayerShown() { + return _layerBg->layerShownValue(); +} + void OverlayWidget::storiesTogglePaused(bool paused) { if (!_streamed || _streamed->instance.player().failed() diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 2bc572a91..a32c0ef59 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -247,6 +247,7 @@ private: FullStoryId id) override; void storiesClose() override; bool storiesPaused() override; + rpl::producer storiesLayerShown() override; void storiesTogglePaused(bool paused) override; float64 storiesSiblingOver(Stories::SiblingType type) override; void storiesRepaint() override;