Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Loading