2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-22 10:17:10 +00:00

Support story album links.

This commit is contained in:
John Preston 2025-07-29 13:35:48 +04:00
parent 3c89907403
commit 985324ac12
10 changed files with 71 additions and 19 deletions

View File

@ -6255,6 +6255,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_view_button_emojipack" = "View emoji"; "lng_view_button_emojipack" = "View emoji";
"lng_view_button_collectible" = "View collectible"; "lng_view_button_collectible" = "View collectible";
"lng_view_button_call" = "Join call"; "lng_view_button_call" = "Join call";
"lng_view_button_storyalbum" = "View Album";
"lng_sponsored_hide_ads" = "Hide"; "lng_sponsored_hide_ads" = "Hide";
"lng_sponsored_title" = "What are sponsored messages?"; "lng_sponsored_title" = "What are sponsored messages?";
@ -6466,6 +6467,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stories_album_empty_text" = "Add some of your stories to this album."; "lng_stories_album_empty_text" = "Add some of your stories to this album.";
"lng_stories_album_all" = "All Stories"; "lng_stories_album_all" = "All Stories";
"lng_stories_album_add_title" = "Add Stories"; "lng_stories_album_add_title" = "Add Stories";
"lng_stories_album_share" = "Share Album";
"lng_stories_album_edit" = "Edit Name"; "lng_stories_album_edit" = "Edit Name";
"lng_stories_album_limit_title" = "Limit Reached"; "lng_stories_album_limit_title" = "Limit Reached";
"lng_stories_album_limit_text" = "Please remove one of the existing albums to add a new one."; "lng_stories_album_limit_text" = "Please remove one of the existing albums to add a new one.";

View File

@ -633,6 +633,8 @@ bool ResolveUsernameOrPhone(
} }
const auto storyParam = params.value(u"story"_q); const auto storyParam = params.value(u"story"_q);
const auto storyId = storyParam.toInt(); const auto storyId = storyParam.toInt();
const auto storyAlbumParam = params.value(u"album"_q);
const auto storyAlbumId = storyAlbumParam.toInt();
const auto appname = webChannelPreviewLink ? QString() : appnameParam; const auto appname = webChannelPreviewLink ? QString() : appnameParam;
const auto commentParam = params.value(u"comment"_q); const auto commentParam = params.value(u"comment"_q);
const auto commentId = commentParam.toInt(); const auto commentId = commentParam.toInt();
@ -659,6 +661,7 @@ bool ResolveUsernameOrPhone(
.phone = phone, .phone = phone,
.messageId = post, .messageId = post,
.storyId = storyId, .storyId = storyId,
.storyAlbumId = storyAlbumId,
.videoTimestamp = (!videot.isEmpty() .videoTimestamp = (!videot.isEmpty()
? ParseVideoTimestamp(videot) ? ParseVideoTimestamp(videot)
: std::optional<TimeId>()), : std::optional<TimeId>()),
@ -1827,6 +1830,7 @@ QString TryConvertUrlToLocal(QString url) {
"/[a-zA-Z0-9\\.\\_\\-]+/?(\\?|$)|" "/[a-zA-Z0-9\\.\\_\\-]+/?(\\?|$)|"
"/\\d+/?(\\?|$)|" "/\\d+/?(\\?|$)|"
"/s/\\d+/?(\\?|$)|" "/s/\\d+/?(\\?|$)|"
"/a/\\d+/?(\\?|$)|"
"/\\d+/\\d+/?(\\?|$)" "/\\d+/\\d+/?(\\?|$)"
")"_q, query, matchOptions)) { ")"_q, query, matchOptions)) {
const auto domain = usernameMatch->captured(1); const auto domain = usernameMatch->captured(1);
@ -1849,6 +1853,8 @@ QString TryConvertUrlToLocal(QString url) {
added = u"&post="_q + postMatch->captured(1); added = u"&post="_q + postMatch->captured(1);
} else if (const auto storyMatch = regex_match(u"^/s/(\\d+)(/?\\?|/?$)"_q, usernameMatch->captured(2))) { } else if (const auto storyMatch = regex_match(u"^/s/(\\d+)(/?\\?|/?$)"_q, usernameMatch->captured(2))) {
added = u"&story="_q + storyMatch->captured(1); added = u"&story="_q + storyMatch->captured(1);
} else if (const auto storyMatch = regex_match(u"^/a/(\\d+)(/?\\?|/?$)"_q, usernameMatch->captured(2))) {
added = u"&album="_q + storyMatch->captured(1);
} else if (const auto appNameMatch = regex_match(u"^/([a-zA-Z0-9\\.\\_\\-]+)(/?\\?|/?$)"_q, usernameMatch->captured(2))) { } else if (const auto appNameMatch = regex_match(u"^/([a-zA-Z0-9\\.\\_\\-]+)(/?\\?|/?$)"_q, usernameMatch->captured(2))) {
added = u"&appname="_q + appNameMatch->captured(1); added = u"&appname="_q + appNameMatch->captured(1);
} }

View File

@ -1862,9 +1862,9 @@ void Stories::loadAlbums(not_null<PeerData*> peer, Albums &albums) {
peer->input, peer->input,
MTP_long(albums.hash) MTP_long(albums.hash)
)).done([=](const MTPstories_Albums &result) { )).done([=](const MTPstories_Albums &result) {
auto &albums = _albums[peer->id];
albums.requestId = 0;
result.match([&](const MTPDstories_albums &data) { result.match([&](const MTPDstories_albums &data) {
auto &albums = _albums[peer->id];
albums.requestId = 0;
albums.hash = data.vhash().v; albums.hash = data.vhash().v;
auto parsed = std::vector<Data::StoryAlbum>(); auto parsed = std::vector<Data::StoryAlbum>();
const auto &list = data.valbums().v; const auto &list = data.valbums().v;

View File

@ -173,6 +173,8 @@ WebPageType ParseWebPageType(
return WebPageType::Giftcode; return WebPageType::Giftcode;
} else if (type == u"telegram_stickerset"_q) { } else if (type == u"telegram_stickerset"_q) {
return WebPageType::StickerSet; return WebPageType::StickerSet;
} else if (type == u"telegram_story_album"_q) {
return WebPageType::StoryAlbum;
} else if (hasIV) { } else if (hasIV) {
return WebPageType::ArticleWithIV; return WebPageType::ArticleWithIV;
} else { } else {

View File

@ -49,6 +49,7 @@ enum class WebPageType : uint8 {
Theme, Theme,
Story, Story,
StickerSet, StickerSet,
StoryAlbum,
Article, Article,
ArticleWithIV, ArticleWithIV,

View File

@ -232,6 +232,8 @@ constexpr auto kSponsoredUserpicLines = 2;
? tr::lng_view_button_emojipack(tr::now) ? tr::lng_view_button_emojipack(tr::now)
: (type == WebPageType::StickerSet) : (type == WebPageType::StickerSet)
? tr::lng_view_button_stickerset(tr::now) ? tr::lng_view_button_stickerset(tr::now)
: (type == WebPageType::StoryAlbum)
? tr::lng_view_button_storyalbum(tr::now)
: QString()); : QString());
if (page->iv) { if (page->iv) {
const auto manager = &page->owner().customEmojiManager(); const auto manager = &page->owner().customEmojiManager();

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/stories/info_stories_inner_widget.h" #include "info/stories/info_stories_inner_widget.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "boxes/share_box.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_stories.h" #include "data/data_stories.h"
@ -210,6 +211,16 @@ InnerWidget::InnerWidget(
_albumId.value( _albumId.value(
) | rpl::start_with_next([=](int albumId) { ) | rpl::start_with_next([=](int albumId) {
if (_albumsTabs
&& (albumId == Data::kStoriesAlbumIdSaved
|| ranges::contains(
_albums,
albumId,
&Data::StoryAlbum::id))) {
_albumsTabs->setActiveTab((albumId == Data::kStoriesAlbumIdSaved)
? u"all"_q
: QString::number(albumId));
}
_controller->replaceKey(Key(Tag(_peer, albumId, _addingToAlbumId))); _controller->replaceKey(Key(Tag(_peer, albumId, _addingToAlbumId)));
reload(); reload();
}, lifetime()); }, lifetime());
@ -699,16 +710,13 @@ void InnerWidget::refreshAlbumsTabs() {
StoryId(), StoryId(),
added)); added));
} else { } else {
_albumsTabs->setActiveTab(id);
_albumIdChanges.fire((id == u"all"_q) ? 0 : id.toInt()); _albumIdChanges.fire((id == u"all"_q) ? 0 : id.toInt());
} }
}, _albumsTabs->lifetime()); }, _albumsTabs->lifetime());
_albumsTabs->contextMenuRequests( _albumsTabs->contextMenuRequests(
) | rpl::start_with_next([=](const QString &id) { ) | rpl::start_with_next([=](const QString &id) {
if (id == u"add"_q if (id == u"add"_q || id == u"all"_q) {
|| id == u"all"_q
|| !_peer->canEditStories()) {
return; return;
} }
showMenuForAlbum(id.toInt()); showMenuForAlbum(id.toInt());
@ -723,24 +731,39 @@ void InnerWidget::refreshAlbumsTabs() {
} }
void InnerWidget::showMenuForAlbum(int id) { void InnerWidget::showMenuForAlbum(int id) {
Expects(id > 0);
if (_menu || _addingToAlbumId) { if (_menu || _addingToAlbumId) {
return; return;
} }
_menu = base::make_unique_q<Ui::PopupMenu>(this, st::popupMenuWithIcons); _menu = base::make_unique_q<Ui::PopupMenu>(this, st::popupMenuWithIcons);
const auto addAction = Ui::Menu::CreateAddActionCallback(_menu); const auto addAction = Ui::Menu::CreateAddActionCallback(_menu);
addAction(tr::lng_stories_album_add_title(tr::now), [=] { if (_peer->canEditStories()) {
editAlbumStories(id); addAction(tr::lng_stories_album_add_title(tr::now), [=] {
}, &st::menuIconStoriesSave); editAlbumStories(id);
addAction(tr::lng_stories_album_edit(tr::now), [=] { }, &st::menuIconStoriesSave);
editAlbumName(id); }
}, &st::menuIconEdit); if (const auto username = _peer->username(); !username.isEmpty()) {
addAction({ addAction(tr::lng_stories_album_share(tr::now), [=] {
.text = tr::lng_stories_album_delete(tr::now), shareAlbumLink(username, id);
.handler = [=] { confirmDeleteAlbum(id); }, }, &st::menuIconShare);
.icon = &st::menuIconDeleteAttention, }
.isAttention = true, if (_peer->canEditStories()) {
}); addAction(tr::lng_stories_album_edit(tr::now), [=] {
_menu->popup(QCursor::pos()); editAlbumName(id);
}, &st::menuIconEdit);
addAction({
.text = tr::lng_stories_album_delete(tr::now),
.handler = [=] { confirmDeleteAlbum(id); },
.icon = &st::menuIconDeleteAttention,
.isAttention = true,
});
}
if (_menu->empty()) {
_menu = nullptr;
} else {
_menu->popup(QCursor::pos());
}
} }
rpl::producer<int> InnerWidget::albumIdChanges() const { rpl::producer<int> InnerWidget::albumIdChanges() const {
@ -771,6 +794,12 @@ void InnerWidget::editAlbumStories(int id) {
_controller->uiShow()->show(std::move(box)); _controller->uiShow()->show(std::move(box));
} }
void InnerWidget::shareAlbumLink(const QString &username, int id) {
const auto url = _controller->session().createInternalLinkFull(
username + u"/a/"_q + QString::number(id));
FastShareLink(_controller->parentController(), url);
}
void InnerWidget::editAlbumName(int id) { void InnerWidget::editAlbumName(int id) {
const auto done = [=](QString name) { const auto done = [=](QString name) {
albumRenamed(id, name); albumRenamed(id, name);

View File

@ -69,6 +69,7 @@ public:
void reload(); void reload();
void editAlbumStories(int id); void editAlbumStories(int id);
void shareAlbumLink(const QString &username, int id);
void editAlbumName(int id); void editAlbumName(int id);
void confirmDeleteAlbum(int id); void confirmDeleteAlbum(int id);
void albumAdded(Data::StoryAlbum result); void albumAdded(Data::StoryAlbum result);

View File

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_filters_menu.h" #include "window/window_filters_menu.h"
#include "window/window_separate_id.h" #include "window/window_separate_id.h"
#include "info/channel_statistics/earn/info_channel_earn_list.h" #include "info/channel_statistics/earn/info_channel_earn_list.h"
#include "info/stories/info_stories_widget.h"
#include "info/info_memento.h" #include "info/info_memento.h"
#include "info/info_controller.h" #include "info/info_controller.h"
#include "inline_bots/bot_attach_web_view.h" #include "inline_bots/bot_attach_web_view.h"
@ -636,6 +637,11 @@ void SessionNavigation::showPeerByLinkResolved(
} }
} else if (info.storyId) { } else if (info.storyId) {
const auto storyId = FullStoryId{ peer->id, info.storyId }; const auto storyId = FullStoryId{ peer->id, info.storyId };
const auto context = (info.storyAlbumId > 0)
? Data::StoriesContext{ Data::StoriesContextAlbum{
info.storyAlbumId,
} }
: Data::StoriesContext{ Data::StoriesContextSingle() };
peer->owner().stories().resolve(storyId, crl::guard(this, [=] { peer->owner().stories().resolve(storyId, crl::guard(this, [=] {
if (peer->owner().stories().lookup(storyId)) { if (peer->owner().stories().lookup(storyId)) {
parentController()->openPeerStory( parentController()->openPeerStory(
@ -646,6 +652,8 @@ void SessionNavigation::showPeerByLinkResolved(
showToast(tr::lng_stories_link_invalid(tr::now)); showToast(tr::lng_stories_link_invalid(tr::now));
} }
})); }));
} else if (info.storyAlbumId > 0) {
showSection(Info::Stories::Make(peer, info.storyAlbumId));
} else if (bot && resolveType == ResolveType::BotApp) { } else if (bot && resolveType == ResolveType::BotApp) {
const auto itemId = info.clickFromMessageId; const auto itemId = info.clickFromMessageId;
const auto item = _session->data().message(itemId); const auto item = _session->data().message(itemId);

View File

@ -40,6 +40,7 @@ struct PeerByLinkInfo {
QString chatLinkSlug; QString chatLinkSlug;
MsgId messageId = ShowAtUnreadMsgId; MsgId messageId = ShowAtUnreadMsgId;
StoryId storyId = 0; StoryId storyId = 0;
int storyAlbumId = 0;
std::optional<TimeId> videoTimestamp; std::optional<TimeId> videoTimestamp;
QString text; QString text;
RepliesByLinkInfo repliesInfo; RepliesByLinkInfo repliesInfo;