Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/juxtaposition-ui/src/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"activity_feed": "Activity Feed",
"my_feed": "My Feed",
"global_feed": "Global Feed",
"global_feed_short": "Global",
"people_feed": "People Feed",
"people_feed_short": "People",
"communities": "Communities",
"messages": "Messages",
"notifications": "Notifications",
Expand Down Expand Up @@ -71,6 +74,7 @@
"follow_user": "Follow",
"following_user": "Following",
"settings": "Settings",
"friend_requests": "Requests",
"deleted": "Deleted User",
"banned": "Banned User"
},
Expand Down
12 changes: 6 additions & 6 deletions apps/juxtaposition-ui/src/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,27 +304,27 @@ async function getFollowedUsers(content) {
});
}

async function getNewsFeed(content, numberOfPosts) {
async function getNewsFeed(content, numberOfPosts, includeCommunities) {
verifyConnected();
return POST.find({
$or: [
{ pid: content.followed_users },
{ pid: content.pid },
{ community_id: content.followed_communities }
{ community_id: includeCommunities ? content.followed_communities : [] }
],
parent: null,
message_to_pid: null,
removed: false
}).limit(numberOfPosts).sort({ created_at: -1 });
}

async function getNewsFeedAfterTimestamp(content, numberOfPosts, post) {
async function getNewsFeedAfterTimestamp(content, numberOfPosts, post, includeCommunities) {
verifyConnected();
return POST.find({
$or: [
{ pid: content.followed_users },
{ pid: content.pid },
{ community_id: content.followed_communities }
{ community_id: includeCommunities ? content.followed_communities : [] }
],
created_at: { $lt: post.created_at },
parent: null,
Expand All @@ -333,13 +333,13 @@ async function getNewsFeedAfterTimestamp(content, numberOfPosts, post) {
}).limit(numberOfPosts).sort({ created_at: -1 });
}

async function getNewsFeedOffset(content, limit, offset) {
async function getNewsFeedOffset(content, limit, offset, includeCommunities) {
verifyConnected();
return POST.find({
$or: [
{ pid: content.followed_users },
{ pid: content.pid },
{ community_id: content.followed_communities }
{ community_id: includeCommunities ? content.followed_communities : [] }
],
parent: null,
message_to_pid: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { database } from '@/database';
import { POST } from '@/models/post';
import { config } from '@/config';
import { parseReq } from '@/services/juxt-web/routes/routeUtils';
import { WebGlobalFeedView, WebPersonalFeedView } from '@/services/juxt-web/views/web/feed';
import { WebGlobalFeedView, WebPeopleFeedView, WebPersonalFeedView } from '@/services/juxt-web/views/web/feed';
import { WebPostListView } from '@/services/juxt-web/views/web/postList';
import { CtrGlobalFeedView, CtrPersonalFeedView } from '@/services/juxt-web/views/ctr/feed';
import { CtrGlobalFeedView, CtrPeopleFeedView, CtrPersonalFeedView } from '@/services/juxt-web/views/ctr/feed';
import { CtrPostListView } from '@/services/juxt-web/views/ctr/postList';
import { PortalGlobalFeedView, PortalPersonalFeedView } from '@/services/juxt-web/views/portal/feed';
import { PortalGlobalFeedView, PortalPeopleFeedView, PortalPersonalFeedView } from '@/services/juxt-web/views/portal/feed';
import { PortalPostListView } from '@/services/juxt-web/views/portal/postList';

export const feedRouter = express.Router();
Expand All @@ -23,7 +23,7 @@ feedRouter.get('/', async function (req, res) {
if (!userContent) {
return res.redirect('/404');
}
const posts = await database.getNewsFeed(userContent, config.postLimit);
const posts = await database.getNewsFeed(userContent, config.postLimit, true);

const nextLink = `/feed/more?offset=${posts.length}&pjax=true`;

Expand All @@ -42,6 +42,35 @@ feedRouter.get('/', async function (req, res) {
});
});

feedRouter.get('/people', async function (req, res) {
const { auth, query } = parseReq(req, {
query: z.object({
pjax: z.stringbool().optional()
})
});
const userContent = await database.getUserContent(auth().pid);
if (!userContent) {
return res.redirect('/404');
}
const posts = await database.getNewsFeed(userContent, config.postLimit, false);

const nextLink = `/feed/people/more?offset=${posts.length}&pjax=true`;

if (query.pjax) {
return res.jsxForDirectory({
web: <WebPostListView nextLink={nextLink} posts={posts} userContent={userContent} />,
portal: <PortalPostListView nextLink={nextLink} posts={posts} userContent={userContent} />,
ctr: <CtrPostListView nextLink={nextLink} posts={posts} userContent={userContent} />
});
}

return res.jsxForDirectory({
web: <WebPeopleFeedView nextLink={nextLink} posts={posts} userContent={userContent} />,
portal: <PortalPeopleFeedView nextLink={nextLink} posts={posts} userContent={userContent} />,
ctr: <CtrPeopleFeedView nextLink={nextLink} posts={posts} userContent={userContent} />
});
});

feedRouter.get('/all', async function (req, res) {
const { auth, query } = parseReq(req, {
query: z.object({
Expand Down Expand Up @@ -86,7 +115,7 @@ feedRouter.get('/more', async function (req, res) {
if (!userContent) {
return res.redirect('/404');
}
const posts = await database.getNewsFeedOffset(userContent, config.postLimit, query.offset);
const posts = await database.getNewsFeedOffset(userContent, config.postLimit, query.offset, true);

const nextLink = `/feed/more?offset=${query.offset + posts.length}&pjax=true`;

Expand All @@ -101,6 +130,32 @@ feedRouter.get('/more', async function (req, res) {
});
});

feedRouter.get('/people/more', async function (req, res) {
const { auth, query } = parseReq(req, {
query: z.object({
pjax: z.stringbool().optional(),
offset: z.coerce.number().nonnegative().default(0)
})
});
const userContent = await database.getUserContent(auth().pid);
if (!userContent) {
return res.redirect('/404');
}
const posts = await database.getNewsFeedOffset(userContent, config.postLimit, query.offset, false);

const nextLink = `/feed/people/more?offset=${query.offset + posts.length}&pjax=true`;

if (posts.length === 0) {
return res.sendStatus(204);
}

return res.jsxForDirectory({
web: <WebPostListView nextLink={nextLink} posts={posts} userContent={userContent} />,
portal: <PortalPostListView nextLink={nextLink} posts={posts} userContent={userContent} />,
ctr: <CtrPostListView nextLink={nextLink} posts={posts} userContent={userContent} />
});
});

feedRouter.get('/all/more', async function (req, res) {
const { auth, query } = parseReq(req, {
query: z.object({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root';
import { CtrPostListClosedView } from '@/services/juxt-web/views/ctr/postList';
import { useUrl } from '@/services/juxt-web/views/common/hooks/useUrl';
import { T } from '@/services/juxt-web/views/common/components/T';
import { CtrNavTab, CtrNavTabs, CtrNavTabsRow } from '@/services/juxt-web/views/ctr/components/ui/CtrNavTabs';
import type { ReactNode } from 'react';
import type { CommunityViewProps } from '@/services/juxt-web/views/web/communityView';

Expand All @@ -19,7 +20,7 @@ export function CtrCommunityView(props: CommunityViewProps): ReactNode {
style={{
background: `url('${bannerUrl}')`
}}
className={cx({
className={cx('buttons', {
'header-legacy': legacy
})}

Expand Down Expand Up @@ -96,14 +97,16 @@ export function CtrCommunityView(props: CommunityViewProps): ReactNode {
<div className="body-content tab2-content" id="community-post-list">
<div className="community-info info-content with-header-banner">
</div>
<menu className="tab-header">
<li id="tab-header-post" className={cx('tab-button', { selected: props.feedType === 0 })}>
<a href={`/titles/${community.olive_community_id}/new`} data-sound="SE_WAVE_SELECT_TAB"><span className="new-post"><T k="community.recent" /></span></a>
</li>
<li id="tab-header-hot-post" className={cx('tab-button', { selected: props.feedType === 1 })}>
<a href={`/titles/${community.olive_community_id}/hot`} data-sound="SE_WAVE_SELECT_TAB"><span><T k="community.popular" /></span></a>
</li>
</menu>
<CtrNavTabs target=".tab-body">
<CtrNavTabsRow>
<CtrNavTab href={`/titles/${community.olive_community_id}/new`} selected={props.feedType === 0}>
<T k="community.recent" />
</CtrNavTab>
<CtrNavTab href={`/titles/${community.olive_community_id}/hot`} selected={props.feedType === 1}>
<T k="community.popular" />
</CtrNavTab>
</CtrNavTabsRow>
</CtrNavTabs>
<div className="tab-body post-list">
{!community.permissions.open ? <CtrPostListClosedView /> : null}
{props.children}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import cx from 'classnames';
import type { ReactNode } from 'react';
import type { NavTabProps, NavTabsProps, NavTabsRowProps } from '@/services/juxt-web/views/web/components/ui/WebNavTabs';

export function CtrNavTab(props: NavTabProps): ReactNode {
const { selected, href } = props;
return (
<a className={cx('nav-tab', { selected })} href={href} data-nav-tab>
{props.children}
</a>
);
}

export function CtrNavTabsRow(props: NavTabsRowProps): ReactNode {
return (
<div className="nav-tabs-row">
{props.children}
</div>
);
}

export function CtrNavTabs(props: NavTabsProps): ReactNode {
return (
<div className="nav-tabs" data-nav-tabs={props.target}>
{props.children}
</div>
);
}
63 changes: 38 additions & 25 deletions apps/juxtaposition-ui/src/services/juxt-web/views/ctr/feed.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,25 @@
import cx from 'classnames';
import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root';
import { CtrPostListView } from '@/services/juxt-web/views/ctr/postList';
import { T } from '@/services/juxt-web/views/common/components/T';
import { CtrNavTab, CtrNavTabs, CtrNavTabsRow } from '@/services/juxt-web/views/ctr/components/ui/CtrNavTabs';
import type { ReactNode } from 'react';
import type { FeedTabsProps, FeedViewProps } from '@/services/juxt-web/views/web/feed';

export function CtrFeedTabs(props: FeedTabsProps): ReactNode {
return (
<menu className="tab-header no-margin">
<li
id="tab-header-my-feed"
className={cx('tab-button', {
selected: props.selected === 0
})}
data-show-post-button="1"
>
<a href="/feed" data-pjax-replace="1" data-sound="SE_WAVE_SELECT_TAB">
<span className="new-post"><T k="global.my_feed" /></span>
</a>
</li>
<li
id="tab-header-global-feed"
className={cx('tab-button', {
selected: props.selected === 1
})}
>
<a href="/feed/all" data-pjax-cache-container="#body" data-pjax-replace="1" data-sound="SE_WAVE_SELECT_TAB">
<span><T k="global.global_feed" /></span>
</a>
</li>
</menu>
<CtrNavTabs target=".tab-body">
<CtrNavTabsRow>
<CtrNavTab href="/feed" selected={props.selected === 0}>
<T k="global.my_feed" />
</CtrNavTab>
<CtrNavTab href="/feed/people" selected={props.selected === 1}>
<T k="global.people_feed_short" />
</CtrNavTab>
<CtrNavTab href="/feed/all" selected={props.selected === 2}>
<T k="global.global_feed_short" />
</CtrNavTab>
</CtrNavTabsRow>
</CtrNavTabs>
);
}

Expand All @@ -56,7 +46,7 @@ export function CtrPersonalFeedView(props: FeedViewProps): ReactNode {
);
}

export function CtrGlobalFeedView(props: FeedViewProps): ReactNode {
export function CtrPeopleFeedView(props: FeedViewProps): ReactNode {
const title = T.str('global.activity_feed');
return (
<CtrRoot title={title}>
Expand All @@ -78,3 +68,26 @@ export function CtrGlobalFeedView(props: FeedViewProps): ReactNode {
</CtrRoot>
);
}

export function CtrGlobalFeedView(props: FeedViewProps): ReactNode {
const title = T.str('global.activity_feed');
return (
<CtrRoot title={title}>
<CtrPageBody>
<header
id="header"
data-toolbar-mode="normal"
data-toolbar-active-button="2"
>
<h1 id="page-title">{title}</h1>
</header>
<div className="body-content tab2-content" id="community-post-list">
<CtrFeedTabs selected={2} />
<div className="tab-body post-list">
<CtrPostListView nextLink={props.nextLink} userContent={props.userContent} posts={props.posts} />
</div>
</div>
</CtrPageBody>
</CtrRoot>
);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import cx from 'classnames';
import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root';
import { CtrMiiIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrMiiIcon';
import { CtrIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrIcon';
import { humanFromNow } from '@/util';
import { useCache } from '@/services/juxt-web/views/common/hooks/useCache';
import { T } from '@/services/juxt-web/views/common/components/T';
import { CtrNavTab, CtrNavTabs, CtrNavTabsRow } from '@/services/juxt-web/views/ctr/components/ui/CtrNavTabs';
import type { ReactNode } from 'react';
import type { TranslationKey } from '@/services/juxt-web/views/common/components/T';
import type { NotificationItemProps, NotificationListViewProps, NotificationWrapperViewProps } from '@/services/juxt-web/views/web/notificationListView';
Expand Down Expand Up @@ -101,18 +101,16 @@ export function CtrNotificationWrapperView(props: NotificationWrapperViewProps):
<h1 id="page-title"><T k="global.notifications_and_messages" /></h1>
</header>
<div className="body-content tab2-content" id="news-page">
<menu className="tab-header no-margin">
<li id="tab-header-my-news" className={cx('tab-button', { selected: props.selectedTab === 0 })} data-show-post-button="1">
<a href="/news/my_news" data-pjax-replace="1" data-sound="SE_WAVE_SELECT_TAB">
<span className="new-post"><T k="global.updates" /></span>
</a>
</li>
<li id="tab-header-friend-request" className={cx('tab-button', { selected: props.selectedTab === 1 })}>
<a href="/friend_messages" data-pjax-cache-container="#body" data-pjax-replace="1" data-sound="SE_WAVE_SELECT_TAB">
<span><T k="global.messages" /></span>
</a>
</li>
</menu>
<CtrNavTabs target=".tab-body">
<CtrNavTabsRow>
<CtrNavTab href="/news/my_news" selected={props.selectedTab === 0}>
<T k="global.updates" />
</CtrNavTab>
<CtrNavTab href="/friend_messages" selected={props.selectedTab === 1}>
<T k="global.messages" />
</CtrNavTab>
</CtrNavTabsRow>
</CtrNavTabs>
<div className="tab-body">
{props.children}
</div>
Expand Down
Loading