Support story link previews.

This commit is contained in:
John Preston 2023-06-23 16:04:53 +04:00
parent 22b6f27f7b
commit 1b581a1597
11 changed files with 85 additions and 8 deletions

View File

@ -3702,6 +3702,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_view_button_background" = "View background"; "lng_view_button_background" = "View background";
"lng_view_button_theme" = "View theme"; "lng_view_button_theme" = "View theme";
"lng_view_button_message" = "View message"; "lng_view_button_message" = "View message";
"lng_view_button_story" = "View story";
"lng_view_button_voice_chat" = "Voice chat"; "lng_view_button_voice_chat" = "Voice chat";
"lng_view_button_voice_chat_channel" = "Live stream"; "lng_view_button_voice_chat_channel" = "Live stream";
"lng_view_button_request_join" = "Request to Join"; "lng_view_button_request_join" = "Request to Join";

View File

@ -779,7 +779,7 @@ QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) {
const auto fallback = [&] { const auto fallback = [&] {
const auto base = user->username(); const auto base = user->username();
const auto story = QString::number(storyId.story); const auto story = QString::number(storyId.story);
const auto query = base + "/s" + story; const auto query = base + "/s/" + story;
return session().createInternalLinkFull(query); return session().createInternalLinkFull(query);
}; };
const auto i = _unlikelyStoryLinks.find(storyId); const auto i = _unlikelyStoryLinks.find(storyId);

View File

@ -1982,11 +1982,16 @@ MediaStory::MediaStory(not_null<HistoryItem*> parent, FullStoryId storyId)
: Media(parent) : Media(parent)
, _storyId(storyId) { , _storyId(storyId) {
const auto stories = &parent->history()->owner().stories(); const auto stories = &parent->history()->owner().stories();
const auto maybeStory = stories->lookup(storyId); if (const auto maybeStory = stories->lookup(storyId)) {
if (!maybeStory) { parent->setText((*maybeStory)->caption());
} else {
if (maybeStory.error() == NoStory::Unknown) { if (maybeStory.error() == NoStory::Unknown) {
stories->resolve(storyId, crl::guard(this, [=] { stories->resolve(storyId, crl::guard(this, [=] {
_expired = !stories->lookup(storyId); if (const auto maybeStory = stories->lookup(storyId)) {
parent->setText((*maybeStory)->caption());
} else {
_expired = true;
}
parent->history()->owner().requestItemViewRefresh(parent); parent->history()->owner().requestItemViewRefresh(parent);
})); }));
} else { } else {
@ -2052,6 +2057,7 @@ std::unique_ptr<HistoryView::Media> MediaStory::createView(
const auto stories = &parent()->history()->owner().stories(); const auto stories = &parent()->history()->owner().stories();
const auto maybeStory = stories->lookup(_storyId); const auto maybeStory = stories->lookup(_storyId);
if (!maybeStory) { if (!maybeStory) {
realParent->setText(TextWithEntities());
if (maybeStory.error() == Data::NoStory::Deleted) { if (maybeStory.error() == Data::NoStory::Deleted) {
_expired = true; _expired = true;
return nullptr; return nullptr;
@ -2065,6 +2071,7 @@ std::unique_ptr<HistoryView::Media> MediaStory::createView(
} }
_expired = false; _expired = false;
const auto story = *maybeStory; const auto story = *maybeStory;
realParent->setText(story->caption());
if (const auto photo = story->photo()) { if (const auto photo = story->photo()) {
return std::make_unique<HistoryView::Photo>( return std::make_unique<HistoryView::Photo>(
message, message,

View File

@ -3215,6 +3215,7 @@ not_null<WebPageData*> Session::processWebpage(const MTPDwebPagePending &data) {
QString(), QString(),
QString(), QString(),
TextWithEntities(), TextWithEntities(),
FullStoryId(),
nullptr, nullptr,
nullptr, nullptr,
WebPageCollage(), WebPageCollage(),
@ -3269,6 +3270,7 @@ not_null<WebPageData*> Session::webpage(
siteName, siteName,
title, title,
description, description,
FullStoryId(),
photo, photo,
document, document,
std::move(collage), std::move(collage),
@ -3321,16 +3323,55 @@ void Session::webpageApplyFields(
} }
return nullptr; return nullptr;
}; };
auto story = (Data::Story*)nullptr;
auto storyId = FullStoryId();
if (const auto attributes = data.vattributes()) {
for (const auto &attribute : attributes->v) {
attribute.match([&](const MTPDwebPageAttributeStory &data) {
storyId = FullStoryId{
peerFromUser(data.vuser_id()),
data.vid().v,
};
if (const auto embed = data.vstory()) {
story = stories().applyFromWebpage(
peerFromUser(data.vuser_id()),
*embed);
} else if (const auto maybe = stories().lookup(storyId)) {
story = *maybe;
} else if (maybe.error() == Data::NoStory::Unknown) {
stories().resolve(storyId, [=] {
if (const auto maybe = stories().lookup(storyId)) {
const auto story = *maybe;
page->document = story->document();
page->photo = story->photo();
page->description = story->caption();
page->type = WebPageType::Story;
notifyWebPageUpdateDelayed(page);
}
});
}
}, [](const auto &) {});
}
}
webpageApplyFields( webpageApplyFields(
page, page,
ParseWebPageType(data), (story ? WebPageType::Story : ParseWebPageType(data)),
qs(data.vurl()), qs(data.vurl()),
qs(data.vdisplay_url()), qs(data.vdisplay_url()),
siteName, siteName,
qs(data.vtitle().value_or_empty()), qs(data.vtitle().value_or_empty()),
description, (story ? story->caption() : description),
photo ? processPhoto(*photo).get() : nullptr, storyId,
document ? processDocument(*document).get() : lookupThemeDocument(), (story
? story->photo()
: photo
? processPhoto(*photo).get()
: nullptr),
(story
? story->document()
: document
? processDocument(*document).get()
: lookupThemeDocument()),
WebPageCollage(this, data), WebPageCollage(this, data),
data.vduration().value_or_empty(), data.vduration().value_or_empty(),
qs(data.vauthor().value_or_empty()), qs(data.vauthor().value_or_empty()),
@ -3345,6 +3386,7 @@ void Session::webpageApplyFields(
const QString &siteName, const QString &siteName,
const QString &title, const QString &title,
const TextWithEntities &description, const TextWithEntities &description,
FullStoryId storyId,
PhotoData *photo, PhotoData *photo,
DocumentData *document, DocumentData *document,
WebPageCollage &&collage, WebPageCollage &&collage,
@ -3359,6 +3401,7 @@ void Session::webpageApplyFields(
siteName, siteName,
title, title,
description, description,
storyId,
photo, photo,
document, document,
std::move(collage), std::move(collage),

View File

@ -811,6 +811,7 @@ private:
const QString &siteName, const QString &siteName,
const QString &title, const QString &title,
const TextWithEntities &description, const TextWithEntities &description,
FullStoryId storyId,
PhotoData *photo, PhotoData *photo,
DocumentData *document, DocumentData *document,
WebPageCollage &&collage, WebPageCollage &&collage,

View File

@ -455,6 +455,17 @@ void Stories::apply(not_null<PeerData*> peer, const MTPUserStories *data) {
} }
} }
Story *Stories::applyFromWebpage(PeerId peerId, const MTPstoryItem &story) {
const auto idDates = parseAndApply(
_owner->peer(peerId),
story,
base::unixtime::now());
const auto value = idDates
? lookup({ peerId, idDates.id })
: base::make_unexpected(NoStory::Deleted);
return value ? value->get() : nullptr;
}
void Stories::requestUserStories(not_null<UserData*> user) { void Stories::requestUserStories(not_null<UserData*> user) {
if (!_requestingUserStories.emplace(user).second) { if (!_requestingUserStories.emplace(user).second) {
return; return;

View File

@ -244,6 +244,7 @@ public:
void loadMore(StorySourcesList list); void loadMore(StorySourcesList list);
void apply(const MTPDupdateStory &data); void apply(const MTPDupdateStory &data);
void apply(not_null<PeerData*> peer, const MTPUserStories *data); void apply(not_null<PeerData*> peer, const MTPUserStories *data);
Story *applyFromWebpage(PeerId peerId, const MTPstoryItem &story);
void loadAround(FullStoryId id, StoriesContext context); void loadAround(FullStoryId id, StoriesContext context);
const StoriesSource *source(PeerId id) const; const StoriesSource *source(PeerId id) const;

View File

@ -152,6 +152,8 @@ WebPageType ParseWebPageType(
return WebPageType::WallPaper; return WebPageType::WallPaper;
} else if (type == u"telegram_theme"_q) { } else if (type == u"telegram_theme"_q) {
return WebPageType::Theme; return WebPageType::Theme;
} else if (type == u"telegram_story"_q) {
return WebPageType::Story;
} else if (type == u"telegram_channel"_q) { } else if (type == u"telegram_channel"_q) {
return WebPageType::Channel; return WebPageType::Channel;
} else if (type == u"telegram_channel_request"_q) { } else if (type == u"telegram_channel_request"_q) {
@ -214,6 +216,7 @@ bool WebPageData::applyChanges(
const QString &newSiteName, const QString &newSiteName,
const QString &newTitle, const QString &newTitle,
const TextWithEntities &newDescription, const TextWithEntities &newDescription,
FullStoryId newStoryId,
PhotoData *newPhoto, PhotoData *newPhoto,
DocumentData *newDocument, DocumentData *newDocument,
WebPageCollage &&newCollage, WebPageCollage &&newCollage,
@ -254,6 +257,7 @@ bool WebPageData::applyChanges(
&& siteName == resultSiteName && siteName == resultSiteName
&& title == resultTitle && title == resultTitle
&& description.text == newDescription.text && description.text == newDescription.text
&& storyId == newStoryId
&& photo == newPhoto && photo == newPhoto
&& document == newDocument && document == newDocument
&& collage.items == newCollage.items && collage.items == newCollage.items
@ -271,6 +275,7 @@ bool WebPageData::applyChanges(
siteName = resultSiteName; siteName = resultSiteName;
title = resultTitle; title = resultTitle;
description = newDescription; description = newDescription;
storyId = newStoryId;
photo = newPhoto; photo = newPhoto;
document = newDocument; document = newDocument;
collage = std::move(newCollage); collage = std::move(newCollage);

View File

@ -35,6 +35,7 @@ enum class WebPageType {
WallPaper, WallPaper,
Theme, Theme,
Story,
Article, Article,
ArticleWithIV, ArticleWithIV,
@ -70,6 +71,7 @@ struct WebPageData {
const QString &newSiteName, const QString &newSiteName,
const QString &newTitle, const QString &newTitle,
const TextWithEntities &newDescription, const TextWithEntities &newDescription,
FullStoryId newStoryId,
PhotoData *newPhoto, PhotoData *newPhoto,
DocumentData *newDocument, DocumentData *newDocument,
WebPageCollage &&newCollage, WebPageCollage &&newCollage,
@ -89,6 +91,7 @@ struct WebPageData {
QString siteName; QString siteName;
QString title; QString title;
TextWithEntities description; TextWithEntities description;
FullStoryId storyId;
int duration = 0; int duration = 0;
QString author; QString author;
PhotoData *photo = nullptr; PhotoData *photo = nullptr;

View File

@ -52,6 +52,8 @@ inline auto WebPageToPhrase(not_null<WebPageData*> webpage) {
const auto type = webpage->type; const auto type = webpage->type;
return Ui::Text::Upper((type == WebPageType::Theme) return Ui::Text::Upper((type == WebPageType::Theme)
? tr::lng_view_button_theme(tr::now) ? tr::lng_view_button_theme(tr::now)
: (type == WebPageType::Story)
? tr::lng_view_button_story(tr::now)
: (type == WebPageType::Message) : (type == WebPageType::Message)
? tr::lng_view_button_message(tr::now) ? tr::lng_view_button_message(tr::now)
: (type == WebPageType::Group) : (type == WebPageType::Group)
@ -139,6 +141,8 @@ bool ViewButton::MediaHasViewButton(
|| ((type == WebPageType::Theme) || ((type == WebPageType::Theme)
&& webpage->document && webpage->document
&& webpage->document->isTheme()) && webpage->document->isTheme())
|| ((type == WebPageType::Story)
&& (webpage->photo || webpage->document))
|| ((type == WebPageType::WallPaper) || ((type == WebPageType::WallPaper)
&& webpage->document && webpage->document
&& webpage->document->isWallPaper()); && webpage->document->isWallPaper());

View File

@ -168,6 +168,7 @@ QSize WebPage::countOptimalSize() {
&& _data->photo && _data->photo
&& _data->type != WebPageType::Photo && _data->type != WebPageType::Photo
&& _data->type != WebPageType::Document && _data->type != WebPageType::Document
&& _data->type != WebPageType::Story
&& _data->type != WebPageType::Video) { && _data->type != WebPageType::Video) {
if (_data->type == WebPageType::Profile) { if (_data->type == WebPageType::Profile) {
_asArticle = true; _asArticle = true;