feat: add shared collection page

This commit is contained in:
Sonny
2024-06-02 18:35:10 +02:00
committed by Sonny
parent dc54a1197d
commit 8a4f895853
15 changed files with 93 additions and 18 deletions

View File

@@ -0,0 +1,24 @@
import { Visibility } from '#enums/visibility';
import Collection from '#models/collection';
import { getSharedCollectionValidator } from '#validators/shared_collection';
import type { HttpContext } from '@adonisjs/core/http';
export default class SharedCollectionsController {
async index({ request, inertia }: HttpContext) {
const { params } = await request.validateUsing(
getSharedCollectionValidator
);
const collection = await this.getSharedCollectionById(params.id);
return inertia.render('shared', { collection });
}
private async getSharedCollectionById(id: Collection['id']) {
return await Collection.query()
.where('id', id)
.andWhere('visibility', Visibility.PUBLIC)
.preload('links')
.preload('author')
.firstOrFail();
}
}

View File

@@ -21,7 +21,7 @@ export default class Collection extends AppBaseModel {
@column() @column()
declare authorId: number; declare authorId: number;
@belongsTo(() => User, { foreignKey: 'author_id' }) @belongsTo(() => User, { foreignKey: 'authorId' })
declare author: BelongsTo<typeof User>; declare author: BelongsTo<typeof User>;
@hasMany(() => Link) @hasMany(() => Link)

View File

@@ -0,0 +1,11 @@
import vine from '@vinejs/vine';
const params = vine.object({
id: vine.number(),
});
export const getSharedCollectionValidator = vine.compile(
vine.object({
params,
})
);

View File

@@ -7,9 +7,10 @@ import Footer from '~/components/footer/footer';
import useActiveCollection from '~/hooks/use_active_collection'; import useActiveCollection from '~/hooks/use_active_collection';
export interface CollectionHeaderProps { export interface CollectionHeaderProps {
openNavigationItem: ReactNode;
openCollectionItem: ReactNode;
showButtons: boolean; showButtons: boolean;
showControls?: boolean;
openNavigationItem?: ReactNode;
openCollectionItem?: ReactNode;
} }
const CollectionContainerStyle = styled.div({ const CollectionContainerStyle = styled.div({

View File

@@ -15,7 +15,7 @@ const CollectionHeaderWrapper = styled.div(({ theme }) => ({
minWidth: 0, minWidth: 0,
width: '100%', width: '100%',
paddingInline: `${paddingLeft} ${paddingRight}`, paddingInline: `${paddingLeft} ${paddingRight}`,
marginBottom: 0, marginBottom: '0.5em',
[`@media (max-width: ${theme.media.tablet})`]: { [`@media (max-width: ${theme.media.tablet})`]: {
paddingInline: 0, paddingInline: 0,
@@ -53,9 +53,10 @@ const LinksCount = styled.div(({ theme }) => ({
})); }));
export default function CollectionHeader({ export default function CollectionHeader({
showButtons,
showControls = true,
openNavigationItem, openNavigationItem,
openCollectionItem, openCollectionItem,
showButtons,
}: CollectionHeaderProps) { }: CollectionHeaderProps) {
const { t } = useTranslation('common'); const { t } = useTranslation('common');
const { activeCollection } = useActiveCollection(); const { activeCollection } = useActiveCollection();
@@ -75,7 +76,9 @@ export default function CollectionHeader({
visibility={visibility} visibility={visibility}
/> />
</CollectionName> </CollectionName>
<CollectionControls collectionId={activeCollection.id} /> {showControls && (
<CollectionControls collectionId={activeCollection.id} />
)}
{showButtons && openCollectionItem && openCollectionItem} {showButtons && openCollectionItem && openCollectionItem}
</CollectionHeaderStyle> </CollectionHeaderStyle>
{activeCollection.description && <CollectionDescription />} {activeCollection.description && <CollectionDescription />}

View File

@@ -17,7 +17,13 @@ const LinkListStyle = styled.ul({
overflowY: 'scroll', overflowY: 'scroll',
}); });
export default function LinkList({ links }: { links: Link[] }) { export default function LinkList({
links,
showControls = true,
}: {
links: Link[];
showControls?: boolean;
}) {
if (links.length === 0) { if (links.length === 0) {
return <NoLink />; return <NoLink />;
} }
@@ -25,7 +31,7 @@ export default function LinkList({ links }: { links: Link[] }) {
return ( return (
<LinkListStyle> <LinkListStyle>
{sortByCreationDate(links).map((link) => ( {sortByCreationDate(links).map((link) => (
<LinkItem link={link} key={link.id} showUserControls /> <LinkItem link={link} key={link.id} showUserControls={showControls} />
))} ))}
</LinkListStyle> </LinkListStyle>
); );

View File

@@ -1,7 +1,7 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { ItemLink } from '~/components/dashboard/side_nav/nav_item'; import { ItemExternalLink } from '~/components/dashboard/side_nav/nav_item';
const FavoriteItem = styled(ItemLink)(({ theme }) => ({ const FavoriteItem = styled(ItemExternalLink)(({ theme }) => ({
backgroundColor: theme.colors.secondary, backgroundColor: theme.colors.secondary,
})); }));

View File

@@ -1,5 +1,6 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Link } from '@inertiajs/react'; import { Link } from '@inertiajs/react';
import ExternalLink from '~/components/common/external_link';
import { rgba } from '~/lib/color'; import { rgba } from '~/lib/color';
export const Item = styled.div(({ theme }) => ({ export const Item = styled.div(({ theme }) => ({
@@ -27,3 +28,4 @@ export const Item = styled.div(({ theme }) => ({
})); }));
export const ItemLink = Item.withComponent(Link); export const ItemLink = Item.withComponent(Link);
export const ItemExternalLink = Item.withComponent(ExternalLink);

View File

@@ -3,10 +3,11 @@ import styled from '@emotion/styled';
import { IoEarthOutline } from 'react-icons/io5'; import { IoEarthOutline } from 'react-icons/io5';
const VisibilityStyle = styled.span(({ theme }) => ({ const VisibilityStyle = styled.span(({ theme }) => ({
userSelect: 'none',
fontWeight: 300, fontWeight: 300,
fontSize: '0.6em', fontSize: '0.6em',
color: theme.colors.lightBlue, color: theme.colors.primary,
border: `1px solid ${theme.colors.lightBlue}`, border: `1px solid ${theme.colors.primary}`,
borderRadius: '50px', borderRadius: '50px',
padding: '0.15em 0.65em', padding: '0.15em 0.65em',
display: 'flex', display: 'flex',

View File

@@ -109,7 +109,6 @@ function DashboardPage(props: Readonly<DashboardPageProps>) {
} }
}, [isMobile, isTablet, closeCollectionList, closeNavigation]); }, [isMobile, isTablet, closeCollectionList, closeNavigation]);
console.log(isMobile, isTablet, isNavigationOpen, isCollectionListOpen);
return ( return (
<DashboardProviders <DashboardProviders
collections={props.collections} collections={props.collections}

24
inertia/pages/shared.tsx Normal file
View File

@@ -0,0 +1,24 @@
import { ReactNode } from 'react';
import CollectionHeader from '~/components/dashboard/collection/header/collection_header';
import LinkList from '~/components/dashboard/link/link_list';
import ContentLayout from '~/components/layouts/content_layout';
import { ActiveCollectionContext } from '~/contexts/active_collection_context';
import { CollectionWithLinks } from '~/types/app';
const SharedCollectionPage = ({
collection,
}: {
collection: CollectionWithLinks;
}) => (
<ActiveCollectionContext.Provider
value={{ activeCollection: collection, setActiveCollection: () => {} }}
>
<CollectionHeader showButtons={false} showControls={false} />
<LinkList links={collection.links} showControls={false} />
</ActiveCollectionContext.Provider>
);
SharedCollectionPage.layout = (page: ReactNode) => (
<ContentLayout css={{ width: '900px' }} children={page} />
);
export default SharedCollectionPage;

View File

@@ -23,7 +23,7 @@ export const lightTheme: Theme = {
white: '#ffffff', white: '#ffffff',
lightGrey: '#dadce0', lightGrey: '#dadce0',
grey: '#888888', grey: '#777777',
lightestBlue, lightestBlue,
lightBlue, lightBlue,

View File

@@ -1,7 +1,8 @@
import './routes/admin.js';
import './routes/app.js'; import './routes/app.js';
import './routes/auth.js'; import './routes/auth.js';
import './routes/collection.js'; import './routes/collection.js';
import './routes/favicon.js'; import './routes/favicon.js';
import './routes/link.js'; import './routes/link.js';
import './routes/search.js'; import './routes/search.js';
import './routes/admin.js'; import './routes/shared_collection.js';

View File

@@ -3,9 +3,6 @@ import router from '@adonisjs/core/services/router';
const CollectionsController = () => const CollectionsController = () =>
import('#controllers/collections_controller'); import('#controllers/collections_controller');
/**
* Routes for authenticated users
*/
router router
.group(() => { .group(() => {
router.get('/dashboard', [CollectionsController, 'index']).as('dashboard'); router.get('/dashboard', [CollectionsController, 'index']).as('dashboard');

View File

@@ -0,0 +1,6 @@
import router from '@adonisjs/core/services/router';
const SharedCollectionsController = () =>
import('#controllers/shared_collections_controller');
router.get('/shared/:id', [SharedCollectionsController, 'index']).as('shared');