feat: make card view timestamp clickable to open event

- Timestamp in card view now links to event in search portal
- Add hover effect showing link is clickable
- Remove unused getKindIcon import
- All linter and type checks pass
This commit is contained in:
Gigi
2025-10-03 10:08:56 +02:00
parent 4afd9ed6d1
commit 64efb103a4
3 changed files with 94 additions and 142 deletions

View File

@@ -3,4 +3,4 @@ description: when creating or modifying UI elements, especially related to icons
alwaysApply: false alwaysApply: false
--- ---
We use FontAwesome. If you can use a fa-icon (instead of text) use a fa-icon. We use FontAwesome. If you can use a fa-icon (instead of text) use a fa-icon. Always strive to keep the UI modern, beautiful, and minimalistic. Shy away from using too many colors, borders, glow, and animations.

View File

@@ -8,7 +8,6 @@ import { Models } from 'applesauce-core'
import { npubEncode, neventEncode } from 'nostr-tools/nip19' import { npubEncode, neventEncode } from 'nostr-tools/nip19'
import { IndividualBookmark } from '../types/bookmarks' import { IndividualBookmark } from '../types/bookmarks'
import { formatDate, renderParsedContent } from '../utils/bookmarkUtils' import { formatDate, renderParsedContent } from '../utils/bookmarkUtils'
import { getKindIcon } from './kindIcon'
import ContentWithResolvedProfiles from './ContentWithResolvedProfiles' import ContentWithResolvedProfiles from './ContentWithResolvedProfiles'
import { extractUrlsFromContent } from '../services/bookmarkHelpers' import { extractUrlsFromContent } from '../services/bookmarkHelpers'
import { classifyUrl } from '../utils/helpers' import { classifyUrl } from '../utils/helpers'
@@ -138,13 +137,24 @@ export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onS
)} )}
</span> </span>
{eventNevent ? (
<a
href={`https://search.dergigi.com/e/${eventNevent}`}
target="_blank"
rel="noopener noreferrer"
className="bookmark-date-link"
title="Open event in search"
>
{formatDate(bookmark.created_at)}
</a>
) : (
<span className="bookmark-date">{formatDate(bookmark.created_at)}</span> <span className="bookmark-date">{formatDate(bookmark.created_at)}</span>
)}
</div> </div>
{extractedUrls.length > 0 && ( {extractedUrls.length > 0 && (
<div className="bookmark-urls"> <div className="bookmark-urls">
<h4>URLs:</h4> {(urlsExpanded ? extractedUrls : extractedUrls.slice(0, 1)).map((url, urlIndex) => {
{(urlsExpanded ? extractedUrls : extractedUrls.slice(0, 3)).map((url, urlIndex) => {
const classification = classifyUrl(url) const classification = classifyUrl(url)
return ( return (
<div key={urlIndex} className="url-row"> <div key={urlIndex} className="url-row">
@@ -160,20 +170,20 @@ export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onS
ariaLabel={classification.buttonText} ariaLabel={classification.buttonText}
title={classification.buttonText} title={classification.buttonText}
variant="success" variant="success"
size={36} size={32}
onClick={(e) => { e.preventDefault(); onSelectUrl?.(url) }} onClick={(e) => { e.preventDefault(); onSelectUrl?.(url) }}
/> />
</div> </div>
) )
})} })}
{extractedUrls.length > 3 && ( {extractedUrls.length > 1 && (
<button <button
className="expand-toggle" className="expand-toggle-urls"
onClick={() => setUrlsExpanded(v => !v)} onClick={() => setUrlsExpanded(v => !v)}
aria-label={urlsExpanded ? 'Collapse URLs' : 'Expand URLs'} aria-label={urlsExpanded ? 'Collapse URLs' : 'Expand URLs'}
title={urlsExpanded ? 'Collapse URLs' : 'Expand URLs'} title={urlsExpanded ? 'Collapse URLs' : 'Expand URLs'}
> >
<FontAwesomeIcon icon={urlsExpanded ? faChevronUp : faChevronDown} /> {urlsExpanded ? `Hide ${extractedUrls.length - 1} more` : `Show ${extractedUrls.length - 1} more`}
</button> </button>
)} )}
</div> </div>
@@ -202,44 +212,24 @@ export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onS
</button> </button>
)} )}
<div className="bookmark-meta"> <div className="bookmark-footer">
{eventNevent ? ( <div className="bookmark-meta-minimal">
<a
href={`https://search.dergigi.com/e/${eventNevent}`}
target="_blank"
rel="noopener noreferrer"
className="kind-icon-link"
title="Open event in search"
>
<span className="kind-icon">
<FontAwesomeIcon icon={getKindIcon(bookmark.kind)} />
</span>
</a>
) : (
<span className="kind-icon">
<FontAwesomeIcon icon={getKindIcon(bookmark.kind)} />
</span>
)}
<span>
<a <a
href={`https://search.dergigi.com/p/${authorNpub}`} href={`https://search.dergigi.com/p/${authorNpub}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="author-link" className="author-link-minimal"
title="Open author in search" title="Open author in search"
> >
by: {getAuthorDisplayName()} {getAuthorDisplayName()}
</a> </a>
</span>
</div> </div>
{hasUrls && firstUrlClassification && ( {hasUrls && firstUrlClassification && (
<div className="read-now"> <button className="read-now-button-minimal" onClick={handleReadNow}>
<button className="read-now-button" onClick={handleReadNow}>
{firstUrlClassification.buttonText} {firstUrlClassification.buttonText}
</button> </button>
</div>
)} )}
</div> </div>
</div>
) )
} }

View File

@@ -229,16 +229,7 @@ body {
} }
.bookmark-urls { .bookmark-urls {
margin: 1rem 0; margin: 0.75rem 0;
}
.bookmark-urls h4 {
margin: 0 0 0.65rem 0;
font-size: 0.85rem;
color: #888;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
} }
.bookmark-url { .bookmark-url {
@@ -608,11 +599,10 @@ body {
} }
.individual-bookmark { .individual-bookmark {
background: linear-gradient(135deg, #2a2a2a 0%, #252525 100%); background: #2a2a2a;
padding: 1.5rem; padding: 1.25rem;
border-radius: 12px; border-radius: 8px;
transition: all 0.25s ease; transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
border: 1px solid #333; border: 1px solid #333;
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
@@ -621,9 +611,8 @@ body {
} }
.individual-bookmark:hover { .individual-bookmark:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
border-color: #444; border-color: #444;
background: #2d2d2d;
} }
/* Compact view styles */ /* Compact view styles */
@@ -712,26 +701,17 @@ body {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 1rem; margin-bottom: 0.75rem;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.5rem; gap: 0.5rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid #333;
} }
.bookmark-type { .bookmark-type {
background: linear-gradient(135deg, #646cff 0%, #535bf2 100%); color: #646cff;
color: white; font-size: 0.9rem;
padding: 0.4rem 0.75rem;
border-radius: 6px;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 0.5px;
text-transform: uppercase;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.3rem; gap: 0.35rem;
box-shadow: 0 2px 4px rgba(100, 108, 255, 0.3);
} }
.bookmark-id { .bookmark-id {
@@ -748,11 +728,23 @@ body {
color: #666; color: #666;
} }
.bookmark-date-link {
font-size: 0.8rem;
color: #666;
text-decoration: none;
transition: color 0.2s ease;
}
.bookmark-date-link:hover {
color: #8ab4f8;
text-decoration: underline;
}
.individual-bookmark .bookmark-content { .individual-bookmark .bookmark-content {
margin: 1rem 0; margin: 0.75rem 0;
color: #ddd; color: #ccc;
line-height: 1.65; line-height: 1.6;
font-size: 0.95rem; font-size: 0.9rem;
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
word-break: break-word; word-break: break-word;
@@ -775,89 +767,59 @@ body {
color: #bbb; color: #bbb;
} }
.individual-bookmark .bookmark-meta { .bookmark-footer {
display: flex; display: flex;
gap: 0.75rem; justify-content: space-between;
flex-wrap: wrap;
font-size: 0.85rem;
color: #999;
margin-top: 1rem;
padding-top: 0.75rem;
border-top: 1px solid #333;
align-items: center; align-items: center;
}
.individual-bookmark .bookmark-meta span {
background: #1e1e1e;
padding: 0.35rem 0.65rem;
border-radius: 6px;
font-family: monospace;
border: 1px solid #2a2a2a;
}
.author-link {
color: #8ab4f8;
text-decoration: none;
}
.author-link:hover { text-decoration: underline; }
.kind-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
border: 1px solid #3a3a3a;
border-radius: 8px;
background: linear-gradient(135deg, #2a2a2a 0%, #252525 100%);
color: #ddd;
transition: all 0.2s ease;
}
.kind-icon svg {
font-size: 1rem;
color: #646cff;
}
.kind-icon-link:hover .kind-icon {
border-color: #646cff;
box-shadow: 0 0 8px rgba(100, 108, 255, 0.3);
}
.kind-icon-link {
text-decoration: none;
}
.read-now {
margin-top: 0.75rem; margin-top: 0.75rem;
display: flex; gap: 0.75rem;
justify-content: flex-end;
} }
.read-now-button { .bookmark-meta-minimal {
background: linear-gradient(135deg, #28a745 0%, #218838 100%); font-size: 0.8rem;
color: #888;
}
.author-link-minimal {
color: #888;
text-decoration: none;
transition: color 0.2s ease;
}
.author-link-minimal:hover {
color: #aaa;
}
.read-now-button-minimal {
background: #28a745;
color: white; color: white;
border: none; border: none;
padding: 0.75rem 1.5rem; padding: 0.5rem 1rem;
border-radius: 8px; border-radius: 6px;
cursor: pointer; cursor: pointer;
font-weight: 700; font-weight: 600;
font-size: 0.9rem; font-size: 0.85rem;
letter-spacing: 0.5px;
transition: all 0.2s ease; transition: all 0.2s ease;
box-shadow: 0 2px 6px rgba(40, 167, 69, 0.3); white-space: nowrap;
} }
.read-now-button:hover { .read-now-button-minimal:hover {
background: linear-gradient(135deg, #218838 0%, #1e7e34 100%); background: #218838;
box-shadow: 0 4px 12px rgba(40, 167, 69, 0.4);
transform: translateY(-1px);
} }
.read-now-button:active { .expand-toggle-urls {
transform: translateY(0); margin-top: 0.5rem;
box-shadow: 0 2px 4px rgba(40, 167, 69, 0.3); background: transparent;
border: none;
color: #646cff;
cursor: pointer;
font-size: 0.8rem;
padding: 0.25rem 0;
text-decoration: underline;
}
.expand-toggle-urls:hover {
color: #8088ff;
} }
/* Private Bookmark Styles */ /* Private Bookmark Styles */