Add simple pagination to user feeds.

This is simply based on sequence numbers and the `gt`/`lt` arguments of
`createUserStream`.
This commit is contained in:
Daan Wynen 2020-05-23 05:44:26 +02:00
parent 68ed94e7dd
commit e8c8aa49a7
4 changed files with 117 additions and 7 deletions

View File

@ -264,11 +264,20 @@ router
})
.get("/author/:feed", async (ctx) => {
const { feed } = ctx.params;
const gt = Number(ctx.request.query['gt'] || -1);
const lt = Number(ctx.request.query['lt'] || -1);
if (lt > 0 && gt > 0 && gt >= lt)
throw new Error("Given search range is empty");
const author = async (feedId) => {
const description = await about.description(feedId);
const name = await about.name(feedId);
const image = await about.image(feedId);
const messages = await post.fromPublicFeed(feedId);
const messages = await post.fromPublicFeed(feedId, gt, lt);
const firstPost = await post.firstBy(feedId);
const lastPost = await post.latestBy(feedId);
const relationship = await friend.getRelationship(feedId);
const avatarUrl = `/image/256/${encodeURIComponent(image)}`;
@ -276,6 +285,8 @@ router
return authorView({
feedId,
messages,
firstPost,
lastPost,
name,
description,
avatarUrl,
@ -342,17 +353,27 @@ router
.get("/profile/", async (ctx) => {
const myFeedId = await meta.myFeedId();
const gt = Number(ctx.request.query['gt'] || -1);
const lt = Number(ctx.request.query['lt'] || -1);
if (lt > 0 && gt > 0 && gt >= lt)
throw new Error("Given search range is empty");
const description = await about.description(myFeedId);
const name = await about.name(myFeedId);
const image = await about.image(myFeedId);
const messages = await post.fromPublicFeed(myFeedId);
const messages = await post.fromPublicFeed(myFeedId, gt, lt);
const firstPost = await post.firstBy(myFeedId);
const lastPost = await post.latestBy(myFeedId);
const avatarUrl = `/image/256/${encodeURIComponent(image)}`;
ctx.body = await authorView({
feedId: myFeedId,
messages,
firstPost,
lastPost,
name,
description,
avatarUrl,

View File

@ -627,14 +627,45 @@ module.exports = ({ cooler, isPublic }) => {
})
);
const getLimitPost = async (feedId, reverse) => {
const ssb = await cooler.open();
const source = ssb.createUserStream({ id: feedId, reverse: reverse });
const messages = await new Promise((resolve, reject) => {
pull(
source,
pull.filter((msg) => isDecrypted(msg) === false && isPost(msg)),
pull.take(1),
pull.collect((err, collectedMessages) => {
if (err) {
reject(err);
} else {
resolve(transform(ssb, collectedMessages, feedId));
}
})
);
});
return messages.length ? messages[0] : undefined;
};
const post = {
fromPublicFeed: async (feedId, customOptions = {}) => {
firstBy: async (feedId) => {
return getLimitPost(feedId, false);
},
latestBy: async (feedId) => {
return getLimitPost(feedId, true);
},
fromPublicFeed: async (feedId, gt = -1, lt = -1, customOptions = {}) => {
const ssb = await cooler.open();
const myFeedId = ssb.id;
const options = configure({ id: feedId }, customOptions);
let defaultOptions = { id: feedId };
if (lt >= 0)
defaultOptions.lt = lt;
if (gt >= 0)
defaultOptions.gt = gt;
defaultOptions.reverse = !(gt >= 0 && lt < 0);
const options = configure(defaultOptions, customOptions);
const { blocking } = await models.friend.getRelationship(feedId);
// Avoid streaming any messages from this feed. If we used the social
@ -661,7 +692,10 @@ module.exports = ({ cooler, isPublic }) => {
);
});
return messages;
if (!defaultOptions.reverse)
return messages.reverse();
else
return messages;
},
mentionsMe: async (customOptions = {}) => {
const ssb = await cooler.open();

View File

@ -62,6 +62,13 @@ const i18n = {
follow: "Follow",
block: "Block",
unblock: "Unblock",
newerPosts: "Newer posts",
olderPosts: "Older posts",
feedRangeEmpty: "The given range is empty for this feed. Try viewing the ",
seeFullFeed: "full feed",
feedEmpty: "The local client has never seen posts from this account.",
beginningOfFeed: "This is the beginning of the feed",
noNewerPosts: "No newer posts have been reveived yet.",
relationshipFollowing: "You are following",
relationshipYou: "This is you",
relationshipBlocking: "You are blocking",

View File

@ -512,6 +512,8 @@ exports.authorView = ({
description,
feedId,
messages,
firstPost,
lastPost,
name,
relationship,
}) => {
@ -600,10 +602,56 @@ exports.authorView = ({
)
);
const linkUrl = relationship.me ? '/profile/' : `/author/${encodeURIComponent(feedId)}/`;
let items = messages.map((msg) => post({ msg }));
if (items.length === 0) {
if (lastPost === undefined) {
items.push(
section(
div(
span(i18n.feedEmpty)
)
)
);
} else {
items.push(
section(
div(
span(i18n.feedRangeEmpty),
a({ href: `${linkUrl}` }, i18n.seeFullFeed)
)
)
);
}
} else {
const highestSeqNum = messages[0].value.sequence;
const lowestSeqNum = messages[messages.length-1].value.sequence;
let newerPostsLink;
if (lastPost !== undefined && highestSeqNum < lastPost.value.sequence)
newerPostsLink = a({ href: `${linkUrl}?gt=${highestSeqNum}` }, i18n.newerPosts);
else
newerPostsLink = span(i18n.newerPosts, { title: i18n.noNewerPosts });
let olderPostsLink;
if (lowestSeqNum > firstPost.value.sequence)
olderPostsLink = a({ href: `${linkUrl}?lt=${lowestSeqNum}` }, i18n.olderPosts);
else
olderPostsLink = span(i18n.olderPosts, { title: i18n.beginningOfFeed });
const pagination = section(
{ class: "message" },
footer(
div(newerPostsLink, olderPostsLink),
br()
)
);
items.unshift(pagination);
items.push(pagination);
}
return template(
i18n.profile,
prefix,
messages.map((msg) => post({ msg }))
items
);
};