66 lines
1.6 KiB
TypeScript
66 lines
1.6 KiB
TypeScript
// Utility functions
|
|
|
|
export function generateId(): string {
|
|
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
return crypto.randomUUID();
|
|
}
|
|
// Fallback for environments without crypto
|
|
return `id_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
}
|
|
|
|
export function formatDate(date: Date | string): string {
|
|
const d = typeof date === 'string' ? new Date(date) : date;
|
|
return d.toLocaleDateString('en-US', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
});
|
|
}
|
|
|
|
export function truncateText(text: string, maxLength: number): string {
|
|
if (text.length <= maxLength) return text;
|
|
return text.substring(0, maxLength).trim() + '...';
|
|
}
|
|
|
|
export function stripHtml(html: string): string {
|
|
const tmp = document.createElement('div');
|
|
tmp.innerHTML = html;
|
|
return tmp.textContent || tmp.innerText || '';
|
|
}
|
|
|
|
export function parseFeedUrl(url: string): { host: string; path: string } | null {
|
|
try {
|
|
const parsed = new URL(url);
|
|
return {
|
|
host: parsed.hostname,
|
|
path: parsed.pathname,
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function guessFeedUrl(baseUrl: string): string[] {
|
|
const commonFeedPaths = [
|
|
'/feed',
|
|
'/rss',
|
|
'/rss.xml',
|
|
'/feed.xml',
|
|
'/atom.xml',
|
|
'/atom',
|
|
'/opml',
|
|
];
|
|
|
|
try {
|
|
const url = new URL(baseUrl);
|
|
// Ensure trailing slash for path concatenation
|
|
const base = url.pathname.endsWith('/') ? url.pathname : `${url.pathname}/`;
|
|
|
|
return commonFeedPaths.map((path) => `${url.origin}${base}${path}`);
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|