feat(bookmarks): classify URLs by type and adjust action buttons

- Add URL classification system (article, video, youtube, image)
- Classify based on domain (youtube) and file extensions
- Update button text: 'READ NOW' for articles, 'WATCH NOW' for videos, 'VIEW NOW' for images
- Update icons: faBookOpen for articles, faPlay for videos, faEye for images
- Apply classification to both individual URL buttons and main action button
This commit is contained in:
Gigi
2025-10-03 01:53:49 +02:00
parent 85695b5934
commit 448c4dac1c
2 changed files with 73 additions and 23 deletions

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faBookmark, faUserLock } from '@fortawesome/free-solid-svg-icons'
import { faChevronDown, faChevronUp, faBookOpen } from '@fortawesome/free-solid-svg-icons'
import { faChevronDown, faChevronUp, faBookOpen, faPlay, faEye } from '@fortawesome/free-solid-svg-icons'
import IconButton from './IconButton'
import { useEventModel } from 'applesauce-react/hooks'
import { Models } from 'applesauce-core'
@@ -11,6 +11,7 @@ import { formatDate, renderParsedContent } from '../utils/bookmarkUtils'
import { getKindIcon } from './kindIcon'
import ContentWithResolvedProfiles from './ContentWithResolvedProfiles'
import { extractUrlsFromContent } from '../services/bookmarkHelpers'
import { classifyUrl } from '../utils/helpers'
interface BookmarkItemProps {
bookmark: IndividualBookmark
@@ -47,6 +48,19 @@ export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onS
// use helper from kindIcon.ts
const getIconForUrlType = (url: string) => {
const classification = classifyUrl(url)
switch (classification.type) {
case 'youtube':
case 'video':
return faPlay
case 'image':
return faEye
default:
return faBookOpen
}
}
const handleReadNow = (event: React.MouseEvent<HTMLButtonElement>) => {
if (!hasUrls) return
const firstUrl = extractedUrls[0]
@@ -58,6 +72,9 @@ export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onS
}
}
// Get classification for the first URL (for the main button)
const firstUrlClassification = hasUrls ? classifyUrl(extractedUrls[0]) : null
return (
<div key={`${bookmark.id}-${index}`} className={`individual-bookmark ${bookmark.isPrivate ? 'private-bookmark' : ''}`}>
<div className="bookmark-header">
@@ -78,26 +95,29 @@ export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onS
{extractedUrls.length > 0 && (
<div className="bookmark-urls">
<h4>URLs:</h4>
{(urlsExpanded ? extractedUrls : extractedUrls.slice(0, 3)).map((url, urlIndex) => (
<div key={urlIndex} className="url-row">
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="bookmark-url"
>
{url}
</a>
<IconButton
icon={faBookOpen}
ariaLabel="Read now"
title="Read now"
variant="success"
size={36}
onClick={(e) => { e.preventDefault(); onSelectUrl?.(url) }}
/>
</div>
))}
{(urlsExpanded ? extractedUrls : extractedUrls.slice(0, 3)).map((url, urlIndex) => {
const classification = classifyUrl(url)
return (
<div key={urlIndex} className="url-row">
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="bookmark-url"
>
{url}
</a>
<IconButton
icon={getIconForUrlType(url)}
ariaLabel={classification.buttonText}
title={classification.buttonText}
variant="success"
size={36}
onClick={(e) => { e.preventDefault(); onSelectUrl?.(url) }}
/>
</div>
)
})}
{extractedUrls.length > 3 && (
<button
className="expand-toggle"
@@ -165,10 +185,10 @@ export const BookmarkItem: React.FC<BookmarkItemProps> = ({ bookmark, index, onS
</span>
</div>
{hasUrls && (
{hasUrls && firstUrlClassification && (
<div className="read-now">
<button className="read-now-button" onClick={handleReadNow}>
READ NOW
{firstUrlClassification.buttonText}
</button>
</div>
)}

View File

@@ -10,4 +10,34 @@ export const extractNprofilePubkeys = (content: string): string[] => {
return Array.from(unique)
}
export type UrlType = 'video' | 'image' | 'youtube' | 'article'
export interface UrlClassification {
type: UrlType
buttonText: string
}
export const classifyUrl = (url: string): UrlClassification => {
const urlLower = url.toLowerCase()
// Check for YouTube
if (urlLower.includes('youtube.com') || urlLower.includes('youtu.be')) {
return { type: 'youtube', buttonText: 'WATCH NOW' }
}
// Check for video extensions
const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv', '.m4v']
if (videoExtensions.some(ext => urlLower.includes(ext))) {
return { type: 'video', buttonText: 'WATCH NOW' }
}
// Check for image extensions
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp', '.ico']
if (imageExtensions.some(ext => urlLower.includes(ext))) {
return { type: 'image', buttonText: 'VIEW NOW' }
}
// Default to article
return { type: 'article', buttonText: 'READ NOW' }
}