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_theme" = "View theme";
"lng_view_button_message" = "View message";
"lng_view_button_story" = "View story";
"lng_view_button_voice_chat" = "Voice chat";
"lng_view_button_voice_chat_channel" = "Live stream";
"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 base = user->username();
const auto story = QString::number(storyId.story);
const auto query = base + "/s" + story;
const auto query = base + "/s/" + story;
return session().createInternalLinkFull(query);
};
const auto i = _unlikelyStoryLinks.find(storyId);

View File

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

View File

@ -3215,6 +3215,7 @@ not_null<WebPageData*> Session::processWebpage(const MTPDwebPagePending &data) {
QString(),
QString(),
TextWithEntities(),
FullStoryId(),
nullptr,
nullptr,
WebPageCollage(),
@ -3269,6 +3270,7 @@ not_null<WebPageData*> Session::webpage(
siteName,
title,
description,
FullStoryId(),
photo,
document,
std::move(collage),
@ -3321,16 +3323,55 @@ void Session::webpageApplyFields(
}
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(
page,
ParseWebPageType(data),
(story ? WebPageType::Story : ParseWebPageType(data)),
qs(data.vurl()),
qs(data.vdisplay_url()),
siteName,
qs(data.vtitle().value_or_empty()),
description,
photo ? processPhoto(*photo).get() : nullptr,
document ? processDocument(*document).get() : lookupThemeDocument(),
(story ? story->caption() : description),
storyId,
(story
? story->photo()
: photo
? processPhoto(*photo).get()
: nullptr),
(story
? story->document()
: document
? processDocument(*document).get()
: lookupThemeDocument()),
WebPageCollage(this, data),
data.vduration().value_or_empty(),
qs(data.vauthor().value_or_empty()),
@ -3345,6 +3386,7 @@ void Session::webpageApplyFields(
const QString &siteName,
const QString &title,
const TextWithEntities &description,
FullStoryId storyId,
PhotoData *photo,
DocumentData *document,
WebPageCollage &&collage,
@ -3359,6 +3401,7 @@ void Session::webpageApplyFields(
siteName,
title,
description,
storyId,
photo,
document,
std::move(collage),

View File

@ -811,6 +811,7 @@ private:
const QString &siteName,
const QString &title,
const TextWithEntities &description,
FullStoryId storyId,
PhotoData *photo,
DocumentData *document,
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) {
if (!_requestingUserStories.emplace(user).second) {
return;

View File

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

View File

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

View File

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

View File

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

View File

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