mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 22:53:25 +00:00
feat: add shared collection page
This commit is contained in:
24
app/controllers/shared_collections_controller.ts
Normal file
24
app/controllers/shared_collections_controller.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ export default class Collection extends AppBaseModel {
|
||||
@column()
|
||||
declare authorId: number;
|
||||
|
||||
@belongsTo(() => User, { foreignKey: 'author_id' })
|
||||
@belongsTo(() => User, { foreignKey: 'authorId' })
|
||||
declare author: BelongsTo<typeof User>;
|
||||
|
||||
@hasMany(() => Link)
|
||||
|
||||
11
app/validators/shared_collection.ts
Normal file
11
app/validators/shared_collection.ts
Normal 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,
|
||||
})
|
||||
);
|
||||
@@ -7,9 +7,10 @@ import Footer from '~/components/footer/footer';
|
||||
import useActiveCollection from '~/hooks/use_active_collection';
|
||||
|
||||
export interface CollectionHeaderProps {
|
||||
openNavigationItem: ReactNode;
|
||||
openCollectionItem: ReactNode;
|
||||
showButtons: boolean;
|
||||
showControls?: boolean;
|
||||
openNavigationItem?: ReactNode;
|
||||
openCollectionItem?: ReactNode;
|
||||
}
|
||||
|
||||
const CollectionContainerStyle = styled.div({
|
||||
|
||||
@@ -15,7 +15,7 @@ const CollectionHeaderWrapper = styled.div(({ theme }) => ({
|
||||
minWidth: 0,
|
||||
width: '100%',
|
||||
paddingInline: `${paddingLeft} ${paddingRight}`,
|
||||
marginBottom: 0,
|
||||
marginBottom: '0.5em',
|
||||
|
||||
[`@media (max-width: ${theme.media.tablet})`]: {
|
||||
paddingInline: 0,
|
||||
@@ -53,9 +53,10 @@ const LinksCount = styled.div(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export default function CollectionHeader({
|
||||
showButtons,
|
||||
showControls = true,
|
||||
openNavigationItem,
|
||||
openCollectionItem,
|
||||
showButtons,
|
||||
}: CollectionHeaderProps) {
|
||||
const { t } = useTranslation('common');
|
||||
const { activeCollection } = useActiveCollection();
|
||||
@@ -75,7 +76,9 @@ export default function CollectionHeader({
|
||||
visibility={visibility}
|
||||
/>
|
||||
</CollectionName>
|
||||
<CollectionControls collectionId={activeCollection.id} />
|
||||
{showControls && (
|
||||
<CollectionControls collectionId={activeCollection.id} />
|
||||
)}
|
||||
{showButtons && openCollectionItem && openCollectionItem}
|
||||
</CollectionHeaderStyle>
|
||||
{activeCollection.description && <CollectionDescription />}
|
||||
|
||||
@@ -17,7 +17,13 @@ const LinkListStyle = styled.ul({
|
||||
overflowY: 'scroll',
|
||||
});
|
||||
|
||||
export default function LinkList({ links }: { links: Link[] }) {
|
||||
export default function LinkList({
|
||||
links,
|
||||
showControls = true,
|
||||
}: {
|
||||
links: Link[];
|
||||
showControls?: boolean;
|
||||
}) {
|
||||
if (links.length === 0) {
|
||||
return <NoLink />;
|
||||
}
|
||||
@@ -25,7 +31,7 @@ export default function LinkList({ links }: { links: Link[] }) {
|
||||
return (
|
||||
<LinkListStyle>
|
||||
{sortByCreationDate(links).map((link) => (
|
||||
<LinkItem link={link} key={link.id} showUserControls />
|
||||
<LinkItem link={link} key={link.id} showUserControls={showControls} />
|
||||
))}
|
||||
</LinkListStyle>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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,
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Link } from '@inertiajs/react';
|
||||
import ExternalLink from '~/components/common/external_link';
|
||||
import { rgba } from '~/lib/color';
|
||||
|
||||
export const Item = styled.div(({ theme }) => ({
|
||||
@@ -27,3 +28,4 @@ export const Item = styled.div(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export const ItemLink = Item.withComponent(Link);
|
||||
export const ItemExternalLink = Item.withComponent(ExternalLink);
|
||||
|
||||
@@ -3,10 +3,11 @@ import styled from '@emotion/styled';
|
||||
import { IoEarthOutline } from 'react-icons/io5';
|
||||
|
||||
const VisibilityStyle = styled.span(({ theme }) => ({
|
||||
userSelect: 'none',
|
||||
fontWeight: 300,
|
||||
fontSize: '0.6em',
|
||||
color: theme.colors.lightBlue,
|
||||
border: `1px solid ${theme.colors.lightBlue}`,
|
||||
color: theme.colors.primary,
|
||||
border: `1px solid ${theme.colors.primary}`,
|
||||
borderRadius: '50px',
|
||||
padding: '0.15em 0.65em',
|
||||
display: 'flex',
|
||||
|
||||
@@ -109,7 +109,6 @@ function DashboardPage(props: Readonly<DashboardPageProps>) {
|
||||
}
|
||||
}, [isMobile, isTablet, closeCollectionList, closeNavigation]);
|
||||
|
||||
console.log(isMobile, isTablet, isNavigationOpen, isCollectionListOpen);
|
||||
return (
|
||||
<DashboardProviders
|
||||
collections={props.collections}
|
||||
|
||||
24
inertia/pages/shared.tsx
Normal file
24
inertia/pages/shared.tsx
Normal 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;
|
||||
@@ -23,7 +23,7 @@ export const lightTheme: Theme = {
|
||||
white: '#ffffff',
|
||||
|
||||
lightGrey: '#dadce0',
|
||||
grey: '#888888',
|
||||
grey: '#777777',
|
||||
|
||||
lightestBlue,
|
||||
lightBlue,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import './routes/admin.js';
|
||||
import './routes/app.js';
|
||||
import './routes/auth.js';
|
||||
import './routes/collection.js';
|
||||
import './routes/favicon.js';
|
||||
import './routes/link.js';
|
||||
import './routes/search.js';
|
||||
import './routes/admin.js';
|
||||
import './routes/shared_collection.js';
|
||||
|
||||
@@ -3,9 +3,6 @@ import router from '@adonisjs/core/services/router';
|
||||
const CollectionsController = () =>
|
||||
import('#controllers/collections_controller');
|
||||
|
||||
/**
|
||||
* Routes for authenticated users
|
||||
*/
|
||||
router
|
||||
.group(() => {
|
||||
router.get('/dashboard', [CollectionsController, 'index']).as('dashboard');
|
||||
|
||||
6
start/routes/shared_collection.ts
Normal file
6
start/routes/shared_collection.ts
Normal 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');
|
||||
Reference in New Issue
Block a user