Skip to content
Open
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
2 changes: 2 additions & 0 deletions src/webapp/api-client/clients/FeedClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class FeedClient extends BaseClient {
if (params?.beforeActivityId)
searchParams.set('beforeActivityId', params.beforeActivityId);
if (params?.urlType) searchParams.set('urlType', params.urlType);
if (params?.source) searchParams.set('source', params.source);

const queryString = searchParams.toString();
const endpoint = queryString
Expand All @@ -31,6 +32,7 @@ export class FeedClient extends BaseClient {
if (params?.page) searchParams.set('page', params.page.toString());
if (params?.limit) searchParams.set('limit', params.limit.toString());
if (params?.urlType) searchParams.set('urlType', params.urlType);
if (params?.source) searchParams.set('source', params.source);

const queryString = searchParams.toString();
const endpoint = queryString
Expand Down
124 changes: 73 additions & 51 deletions src/webapp/components/CollectionSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
import { ApiClient, Collection } from '@/api-client';
import { useCollectionSearch } from '@/hooks/useCollectionSearch';
import { CreateCollectionModal } from './CreateCollectionModal';
import { isMarginUri, getMarginUrl } from '@/lib/utils/margin';
import MarginLogo from '@/components/MarginLogo';

interface LocalCollection extends Collection {
authorId: string; // Extended for local component use
Expand Down Expand Up @@ -105,11 +107,22 @@ export function CollectionSelector({
{existingCollections.length !== 1 && 's'}:
</Text>
<Group gap="xs">
{existingCollections.map((collection) => (
<Badge key={collection.id} variant="light" color="blue">
{collection.name}
</Badge>
))}
{existingCollections.map((collection) => {
const marginUrl = getMarginUrl(
collection.uri,
collection.author?.handle,
);
return (
<Badge key={collection.id} variant="light" color="blue">
<Group gap={4}>
{collection.name}
{isMarginUri(collection.uri) && (
<MarginLogo size={10} marginUrl={marginUrl} />
)}
</Group>
</Badge>
);
})}
</Group>
</Box>
)}
Expand Down Expand Up @@ -163,52 +176,61 @@ export function CollectionSelector({
</Group>
</Box>
)}
{availableCollections.map((collection, index) => (
<Box
key={collection.id}
p="sm"
style={{
cursor: 'pointer',
backgroundColor: selectedCollectionIds.includes(
collection.id,
)
? 'var(--mantine-color-blue-0)'
: index % 2 === 0
? 'var(--mantine-color-gray-0)'
: 'transparent',
borderRadius: '4px',
border: selectedCollectionIds.includes(collection.id)
? '1px solid var(--mantine-color-blue-4)'
: '1px solid transparent',
}}
onClick={() => handleCollectionToggle(collection.id)}
>
<Group justify="space-between" align="center" wrap="nowrap">
<Stack gap={2} style={{ flex: 1, minWidth: 0 }}>
<Group gap="xs" align="center">
<Text fw={500} size="sm" truncate>
{collection.name}
</Text>
<Text size="xs" c="dimmed">
{collection.cardCount} cards
</Text>
</Group>
{collection.description && (
<Text size="xs" c="dimmed" lineClamp={1}>
{collection.description}
</Text>
)}
</Stack>
<Checkbox
checked={selectedCollectionIds.includes(collection.id)}
onChange={() => handleCollectionToggle(collection.id)}
disabled={disabled}
onClick={(e) => e.stopPropagation()}
size="sm"
/>
</Group>
</Box>
))}
{availableCollections.map((collection, index) => {
const marginUrl = getMarginUrl(
collection.uri,
collection.author?.handle,
);
return (
<Box
key={collection.id}
p="sm"
style={{
cursor: 'pointer',
backgroundColor: selectedCollectionIds.includes(
collection.id,
)
? 'var(--mantine-color-blue-0)'
: index % 2 === 0
? 'var(--mantine-color-gray-0)'
: 'transparent',
borderRadius: '4px',
border: selectedCollectionIds.includes(collection.id)
? '1px solid var(--mantine-color-blue-4)'
: '1px solid transparent',
}}
onClick={() => handleCollectionToggle(collection.id)}
>
<Group justify="space-between" align="center" wrap="nowrap">
<Stack gap={2} style={{ flex: 1, minWidth: 0 }}>
<Group gap="xs" align="center">
<Text fw={500} size="sm" truncate>
{collection.name}
</Text>
{isMarginUri(collection.uri) && (
<MarginLogo size={12} marginUrl={marginUrl} />
)}
<Text size="xs" c="dimmed">
{collection.cardCount} cards
</Text>
</Group>
{collection.description && (
<Text size="xs" c="dimmed" lineClamp={1}>
{collection.description}
</Text>
)}
</Stack>
<Checkbox
checked={selectedCollectionIds.includes(collection.id)}
onChange={() => handleCollectionToggle(collection.id)}
disabled={disabled}
onClick={(e) => e.stopPropagation()}
size="sm"
/>
</Group>
</Box>
);
})}
</Stack>
) : searchText.trim() ? (
<Stack gap="sm" py="md">
Expand Down
46 changes: 46 additions & 0 deletions src/webapp/components/MarginLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Anchor, Box, Tooltip } from '@mantine/core';
import { MouseEvent } from 'react';

interface Props {
size?: number;
marginUrl?: string | null;
}

export default function MarginLogo({ size = 16, marginUrl }: Props) {
const handleClick = (e: MouseEvent) => {
e.stopPropagation();
};

const logo = (
<Box
component="svg"
width={size}
height={size}
viewBox="0 0 265 231"
fill="#6366f1"
xmlns="http://www.w3.org/2000/svg"
style={{ display: 'inline-block', verticalAlign: 'middle' }}
>
<path d="M0 230 V0 H199 V65.7156 H149.5 V115.216 H182.5 L199 131.716 V230 Z" />
<path d="M215 214.224 V230 H264.5 V0 H215.07 V16.2242 H248.5 V214.224 H215 Z" />
</Box>
);

if (!marginUrl) {
return logo;
}

return (
<Tooltip label="View on Margin">
<Anchor
href={marginUrl}
target="_blank"
rel="noopener noreferrer"
onClick={handleClick}
style={{ display: 'inline-flex', alignItems: 'center', lineHeight: 0 }}
>
{logo}
</Anchor>
</Tooltip>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { IoMdLink } from 'react-icons/io';
import { DEFAULT_OVERLAY_PROPS } from '@/styles/overlays';
import { track } from '@vercel/analytics';
import useMyCollections from '@/features/collections/lib/queries/useMyCollections';
import { isMarginUri, getMarginUrl } from '@/lib/utils/margin';
import MarginLogo from '@/components/MarginLogo';

interface Props {
isOpen: boolean;
Expand Down Expand Up @@ -152,29 +154,40 @@ export default function AddCardDrawer(props: Props) {
: 'Manage/View all'}
</Button>

{myCollections.map((col) => (
<Button
key={col.id}
variant="light"
color={
selectedCollections.some((c) => c.id === col.id)
? 'grape'
: 'gray'
}
onClick={() => {
setSelectedCollections((prev) => {
// already selected, remove
if (prev.some((c) => c.id === col.id)) {
return prev.filter((c) => c.id !== col.id);
}
// not selected, add it
return [...prev, col];
});
}}
>
{col.name}
</Button>
))}
{myCollections.map((col) => {
const marginUrl = getMarginUrl(
col.uri,
col.author?.handle,
);
return (
<Button
key={col.id}
variant="light"
color={
selectedCollections.some((c) => c.id === col.id)
? 'grape'
: 'gray'
}
onClick={() => {
setSelectedCollections((prev) => {
// already selected, remove
if (prev.some((c) => c.id === col.id)) {
return prev.filter((c) => c.id !== col.id);
}
// not selected, add it
return [...prev, col];
});
}}
rightSection={
isMarginUri(col.uri) ? (
<MarginLogo size={12} marginUrl={marginUrl} />
) : undefined
}
>
{col.name}
</Button>
);
})}
</Group>
</ScrollArea.Autosize>
</Stack>
Expand Down
8 changes: 7 additions & 1 deletion src/webapp/features/cards/components/urlCard/UrlCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import UrlCardDebugView from '../UrlCardDebugView/UrlCardDebugView';
interface Props {
id: string;
url: string;
uri?: string;
cardContent: UrlCard['cardContent'];
note?: UrlCard['note'];
currentCollection?: Collection;
Expand Down Expand Up @@ -65,7 +66,12 @@ export default function UrlCard(props: Props) {
onAuxClick={handleAuxClick}
>
<Stack justify="space-between" gap={'sm'} flex={1}>
<UrlCardContent url={props.url} cardContent={props.cardContent} />
<UrlCardContent
url={props.url}
uri={props.uri}
cardContent={props.cardContent}
authorHandle={props.cardAuthor?.handle}
/>

{settings.tinkerMode && (
<UrlCardDebugView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,43 @@ import {
import { UrlCard } from '@semble/types';
import Link from 'next/link';
import { useState } from 'react';
import { isMarginUri, getMarginUrl } from '@/lib/utils/margin';
import MarginLogo from '@/components/MarginLogo';

interface Props {
cardContent: UrlCard['cardContent'];
uri?: string;
authorHandle?: string;
}

export default function LinkCardContent(props: Props) {
const domain = getDomain(props.cardContent.url);
const [imageError, setImageError] = useState(false);
const { settings } = useUserSettings();
const marginUrl = getMarginUrl(props.uri, props.authorHandle);

return (
<Group justify="space-between" align="start" gap={'lg'}>
<Stack gap={0} flex={1}>
<Tooltip label={props.cardContent.url}>
<Anchor
onClick={(e) => e.stopPropagation()}
component={Link}
href={props.cardContent.url}
target="_blank"
c={'gray'}
lineClamp={1}
w={'fit-content'}
fz={'sm'}
>
{domain}
</Anchor>
</Tooltip>
<Group gap={4}>
<Tooltip label={props.cardContent.url}>
<Anchor
onClick={(e) => e.stopPropagation()}
component={Link}
href={props.cardContent.url}
target="_blank"
c={'gray'}
lineClamp={1}
w={'fit-content'}
fz={'sm'}
>
{domain}
</Anchor>
</Tooltip>
{isMarginUri(props.uri) && (
<MarginLogo size={12} marginUrl={marginUrl} />
)}
</Group>
{props.cardContent.title && (
<Text c={'bright'} lineClamp={2} fw={500}>
{props.cardContent.title}
Expand Down
Loading