Browse Source

bug-fixes

master
Silberengel 4 weeks ago
parent
commit
37dd93116b
  1. 46
      src/lib/modules/profiles/ProfilePage.svelte
  2. 14
      src/lib/services/nostr/nostr-client.ts
  3. 37
      src/routes/about/+page.svelte
  4. 7
      src/routes/discussions/+page.svelte
  5. 64
      src/routes/feed/+page.svelte
  6. 4
      src/routes/repos/[naddr]/+page.svelte
  7. 13
      src/routes/topics/[name]/+page.svelte
  8. 4
      static/changelog.yaml
  9. 4
      static/healthz.json

46
src/lib/modules/profiles/ProfilePage.svelte

@ -5,7 +5,7 @@
import FeedPost from '../feed/FeedPost.svelte'; import FeedPost from '../feed/FeedPost.svelte';
import CommentComponent from '../comments/Comment.svelte'; import CommentComponent from '../comments/Comment.svelte';
import ProfileMenu from '../../components/profile/ProfileMenu.svelte'; import ProfileMenu from '../../components/profile/ProfileMenu.svelte';
import { fetchProfile, fetchUserStatus, fetchUserStatusEvent, type ProfileData } from '../../services/user-data.js'; import { fetchProfile, fetchUserStatus, fetchUserStatusEvent, fetchRelayLists, type ProfileData } from '../../services/user-data.js';
import { nostrClient } from '../../services/nostr/nostr-client.js'; import { nostrClient } from '../../services/nostr/nostr-client.js';
import { relayManager } from '../../services/nostr/relay-manager.js'; import { relayManager } from '../../services/nostr/relay-manager.js';
import { config } from '../../services/nostr/config.js'; import { config } from '../../services/nostr/config.js';
@ -132,6 +132,34 @@
return unsubscribe; return unsubscribe;
}); });
/**
* Get relays for loading a profile, including the profile owner's relay lists
*/
async function getProfileRelaysForPubkey(pubkey: string): Promise<string[]> {
// Start with base profile relays
const baseRelays = relayManager.getProfileReadRelays();
// Try to fetch relay lists from the profile owner (non-blocking)
try {
const { inbox } = await Promise.race([
fetchRelayLists(pubkey, baseRelays),
new Promise<{ inbox: string[]; outbox: string[] }>((resolve) =>
setTimeout(() => resolve({ inbox: [], outbox: [] }), 2000)
)
]);
// Combine base relays with profile owner's inbox relays
const allRelays = [...baseRelays, ...inbox];
// Normalize and deduplicate
const normalized = allRelays.map(r => r.toLowerCase().trim()).filter(r => r.length > 0);
return Array.from(new Set(normalized));
} catch (error) {
// If fetching relay lists fails, just use base relays
return baseRelays;
}
}
async function loadProfileEvent(pubkey: string) { async function loadProfileEvent(pubkey: string) {
if (!isMounted) return; if (!isMounted) return;
try { try {
@ -144,7 +172,8 @@
} }
// Fetch from relays if not in cache - shorter timeout // Fetch from relays if not in cache - shorter timeout
const relays = relayManager.getProfileReadRelays(); // Include profile owner's relay lists
const relays = await getProfileRelaysForPubkey(pubkey);
const events = await nostrClient.fetchEvents( const events = await nostrClient.fetchEvents(
[{ kinds: [KIND.METADATA], authors: [pubkey], limit: 1 }], [{ kinds: [KIND.METADATA], authors: [pubkey], limit: 1 }],
relays, relays,
@ -632,11 +661,18 @@
loading = true; loading = true;
try { try {
// Step 0: Get relays including profile owner's relay lists (non-blocking, with timeout)
const profileRelaysPromise = getProfileRelaysForPubkey(pubkey);
// Step 1: Load profile and status first (fast from cache) - display immediately // Step 1: Load profile and status first (fast from cache) - display immediately
// fetchProfile uses parseProfile which prioritizes tags over JSON content // fetchProfile uses parseProfile which prioritizes tags over JSON content
const profilePromise = fetchProfile(pubkey); // Use profile owner's relays if available, otherwise fall back to default
const statusPromise = fetchUserStatus(pubkey); const relaysPromise = profileRelaysPromise.catch(() => relayManager.getProfileReadRelays());
const statusEventPromise = fetchUserStatusEvent(pubkey); const relays = await relaysPromise;
const profilePromise = fetchProfile(pubkey, relays);
const statusPromise = fetchUserStatus(pubkey, relays);
const statusEventPromise = fetchUserStatusEvent(pubkey, relays);
activeFetchPromises.add(profilePromise); activeFetchPromises.add(profilePromise);
activeFetchPromises.add(statusPromise); activeFetchPromises.add(statusPromise);
activeFetchPromises.add(statusEventPromise); activeFetchPromises.add(statusEventPromise);

14
src/lib/services/nostr/nostr-client.ts

@ -1360,21 +1360,23 @@ class NostrClient {
*/ */
private sanitizeFilters(filters: Filter[]): Filter[] { private sanitizeFilters(filters: Filter[]): Filter[] {
return filters.map(filter => { return filters.map(filter => {
if (filter.authors && filter.authors.length > 0) { const filterWithAuthors = filter as Filter & { authors?: string[] };
if (filterWithAuthors.authors && filterWithAuthors.authors.length > 0) {
// Filter out invalid pubkeys // Filter out invalid pubkeys
const validAuthors = filter.authors.filter(author => this.isValidPubkey(author)); const validAuthors = filterWithAuthors.authors.filter(author => this.isValidPubkey(author));
if (validAuthors.length === 0) { if (validAuthors.length === 0) {
// If no valid authors remain, remove the authors field entirely // If no valid authors remain, remove the authors field entirely
const { authors, ...rest } = filter; const { authors, ...rest } = filterWithAuthors;
return rest; return rest as Filter;
} }
return { ...filter, authors: validAuthors }; return { ...filter, authors: validAuthors } as Filter;
} }
return filter; return filter;
}).filter(filter => { }).filter(filter => {
const filterWithAuthors = filter as Filter & { authors?: string[] };
// Remove filters that have no valid query parameters // Remove filters that have no valid query parameters
return (filter.ids && filter.ids.length > 0) || return (filter.ids && filter.ids.length > 0) ||
(filter.authors && filter.authors.length > 0) || (filterWithAuthors.authors && filterWithAuthors.authors.length > 0) ||
(filter.kinds && filter.kinds.length > 0) || (filter.kinds && filter.kinds.length > 0) ||
(filter['#e'] && filter['#e'].length > 0) || (filter['#e'] && filter['#e'].length > 0) ||
(filter['#p'] && filter['#p'].length > 0) || (filter['#p'] && filter['#p'].length > 0) ||

37
src/routes/about/+page.svelte

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import Header from '../../lib/components/layout/Header.svelte'; import Header from '../../lib/components/layout/Header.svelte';
import PageHeader from '../../lib/components/layout/PageHeader.svelte'; import PageHeader from '../../lib/components/layout/PageHeader.svelte';
import Icon from '../../lib/components/ui/Icon.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { getAppVersion } from '../../lib/services/version-manager.js'; import { getAppVersion } from '../../lib/services/version-manager.js';
import { getAllVersions, loadChangelog } from '../../lib/services/changelog.js'; import { getAllVersions, loadChangelog } from '../../lib/services/changelog.js';
@ -159,42 +160,6 @@
padding: 0; padding: 0;
} }
.about-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
margin-bottom: 2rem;
}
.back-button {
padding: 0.5rem 1rem;
border: 1px solid var(--fog-border, #cbd5e1);
border-radius: 4px;
background: var(--fog-post, #ffffff);
color: var(--fog-text, #1e293b);
cursor: pointer;
transition: all 0.2s;
font-size: 0.875em;
white-space: nowrap;
}
.back-button:hover {
background: var(--fog-highlight, #f1f5f9);
border-color: var(--fog-accent, #94a3b8);
}
:global(.dark) .back-button {
background: var(--fog-dark-post, #334155);
border-color: var(--fog-dark-border, #475569);
color: var(--fog-dark-text, #f1f5f9);
}
:global(.dark) .back-button:hover {
background: var(--fog-dark-highlight, #475569);
border-color: var(--fog-dark-accent, #64748b);
}
.about-content { .about-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

7
src/routes/discussions/+page.svelte

@ -155,13 +155,6 @@
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.discussions-header-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1rem;
}
.discussions-controls { .discussions-controls {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

64
src/routes/feed/+page.svelte

@ -57,10 +57,10 @@
<Header /> <Header />
<main class="container mx-auto px-4 py-8"> <main class="feed-main container mx-auto">
<div class="feed-content"> <div class="feed-content">
<PageHeader title="/Feed" onRefresh={handleRefresh} refreshLoading={feedPageComponent?.loadingMore || false} /> <PageHeader title="/Feed" onRefresh={handleRefresh} refreshLoading={feedPageComponent?.loadingMore || false} />
<div class="feed-header mb-6"> <div class="feed-header">
<div class="feed-controls"> <div class="feed-controls">
<div class="feed-filter"> <div class="feed-filter">
<label class="feed-filter-label"> <label class="feed-filter-label">
@ -103,6 +103,16 @@
</main> </main>
<style> <style>
.feed-main {
padding: 1rem 0.5rem;
}
@media (min-width: 640px) {
.feed-main {
padding: 2rem 1rem;
}
}
.feed-content { .feed-content {
max-width: var(--content-width); max-width: var(--content-width);
margin: 0 auto; margin: 0 auto;
@ -112,32 +122,45 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
padding: 0 1rem; padding: 0 0.5rem;
margin-bottom: 1rem; margin-bottom: 1rem;
margin-top: -1rem; margin-top: -1rem;
} }
@media (min-width: 640px) {
.feed-header {
padding: 0 1rem;
}
}
.feed-controls { .feed-controls {
display: flex; display: flex;
justify-content: flex-end; flex-direction: column;
align-items: center;
gap: 1rem; gap: 1rem;
align-items: stretch;
} }
@media (max-width: 640px) { @media (min-width: 640px) {
.feed-controls { .feed-controls {
justify-content: flex-start; flex-direction: row;
justify-content: flex-end;
align-items: center;
} }
} }
.feed-filter { .feed-filter {
display: flex; display: flex;
align-items: center; align-items: center;
margin-right: 1rem;
flex-shrink: 0; flex-shrink: 0;
min-width: fit-content; min-width: fit-content;
} }
@media (min-width: 640px) {
.feed-filter {
margin-right: 1rem;
}
}
.feed-filter-label { .feed-filter-label {
display: flex; display: flex;
align-items: center; align-items: center;
@ -176,6 +199,11 @@
width: 100%; width: 100%;
justify-content: flex-start; justify-content: flex-start;
} }
.feed-header-buttons > * {
flex: 1 1 auto;
min-width: 0;
}
} }
.see-new-events-btn-header { .see-new-events-btn-header {
@ -188,6 +216,16 @@
transition: all 0.2s; transition: all 0.2s;
white-space: nowrap; white-space: nowrap;
font-weight: 500; font-weight: 500;
text-align: center;
}
@media (max-width: 640px) {
.see-new-events-btn-header {
white-space: normal;
word-break: break-word;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
}
} }
.see-new-events-btn-header:hover { .see-new-events-btn-header:hover {
@ -217,6 +255,16 @@
white-space: nowrap; white-space: nowrap;
text-decoration: none; text-decoration: none;
display: inline-block; display: inline-block;
text-align: center;
}
@media (max-width: 640px) {
.see-more-events-btn-header {
white-space: normal;
word-break: break-word;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
}
} }
.see-more-events-btn-header:hover:not(:disabled) { .see-more-events-btn-header:hover:not(:disabled) {

4
src/routes/repos/[naddr]/+page.svelte

@ -1585,10 +1585,6 @@
flex-wrap: wrap; flex-wrap: wrap;
} }
.repo-title-row h1 {
flex: 1;
min-width: 0;
}

13
src/routes/topics/[name]/+page.svelte

@ -252,19 +252,6 @@
margin-bottom: 1rem; margin-bottom: 1rem;
} }
@media (max-width: 640px) {
.topic-header {
padding: 0 0.5rem;
padding-bottom: 0.75rem;
width: 100%;
max-width: 100%;
}
}
:global(.dark) .topic-header {
border-bottom-color: var(--fog-dark-border, #374151);
}
.loading-state, .loading-state,
.empty-state { .empty-state {
padding: 2rem; padding: 2rem;

4
static/changelog.yaml

@ -1,7 +1,9 @@
versions: versions:
'0.3.2': '0.3.2':
- 'Expanded /repos to handle GitLab, Gitea, and OneDev repositories' - 'Expanded /repos to handle GitLab, Gitea, and OneDev repositories'
- 'Improved code block highlighting for pure black background' - 'Added back and refresh buttons to all pages'
- 'Handle GRASP repository management'
- 'Support image, documentation, banner, and primary tags on repositories'
'0.3.1': '0.3.1':
- 'Media attachments rendering in all feeds and views' - 'Media attachments rendering in all feeds and views'
- 'NIP-92/NIP-94 image tags support' - 'NIP-92/NIP-94 image tags support'

4
static/healthz.json

@ -2,7 +2,7 @@
"status": "ok", "status": "ok",
"service": "aitherboard", "service": "aitherboard",
"version": "0.3.2", "version": "0.3.2",
"buildTime": "2026-02-14T16:25:59.663Z", "buildTime": "2026-02-14T17:51:24.287Z",
"gitCommit": "unknown", "gitCommit": "unknown",
"timestamp": 1771086359663 "timestamp": 1771091484287
} }
Loading…
Cancel
Save