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 @@ @@ -5,7 +5,7 @@
import FeedPost from '../feed/FeedPost.svelte';
import CommentComponent from '../comments/Comment.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 { relayManager } from '../../services/nostr/relay-manager.js';
import { config } from '../../services/nostr/config.js';
@ -132,6 +132,34 @@ @@ -132,6 +132,34 @@
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) {
if (!isMounted) return;
try {
@ -144,7 +172,8 @@ @@ -144,7 +172,8 @@
}
// 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(
[{ kinds: [KIND.METADATA], authors: [pubkey], limit: 1 }],
relays,
@ -632,11 +661,18 @@ @@ -632,11 +661,18 @@
loading = true;
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
// fetchProfile uses parseProfile which prioritizes tags over JSON content
const profilePromise = fetchProfile(pubkey);
const statusPromise = fetchUserStatus(pubkey);
const statusEventPromise = fetchUserStatusEvent(pubkey);
// Use profile owner's relays if available, otherwise fall back to default
const relaysPromise = profileRelaysPromise.catch(() => relayManager.getProfileReadRelays());
const relays = await relaysPromise;
const profilePromise = fetchProfile(pubkey, relays);
const statusPromise = fetchUserStatus(pubkey, relays);
const statusEventPromise = fetchUserStatusEvent(pubkey, relays);
activeFetchPromises.add(profilePromise);
activeFetchPromises.add(statusPromise);
activeFetchPromises.add(statusEventPromise);

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

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

37
src/routes/about/+page.svelte

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
<script lang="ts">
import Header from '../../lib/components/layout/Header.svelte';
import PageHeader from '../../lib/components/layout/PageHeader.svelte';
import Icon from '../../lib/components/ui/Icon.svelte';
import { onMount } from 'svelte';
import { getAppVersion } from '../../lib/services/version-manager.js';
import { getAllVersions, loadChangelog } from '../../lib/services/changelog.js';
@ -159,42 +160,6 @@ @@ -159,42 +160,6 @@
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 {
display: flex;
flex-direction: column;

7
src/routes/discussions/+page.svelte

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

64
src/routes/feed/+page.svelte

@ -57,10 +57,10 @@ @@ -57,10 +57,10 @@
<Header />
<main class="container mx-auto px-4 py-8">
<main class="feed-main container mx-auto">
<div class="feed-content">
<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-filter">
<label class="feed-filter-label">
@ -103,6 +103,16 @@ @@ -103,6 +103,16 @@
</main>
<style>
.feed-main {
padding: 1rem 0.5rem;
}
@media (min-width: 640px) {
.feed-main {
padding: 2rem 1rem;
}
}
.feed-content {
max-width: var(--content-width);
margin: 0 auto;
@ -112,32 +122,45 @@ @@ -112,32 +122,45 @@
display: flex;
flex-direction: column;
gap: 1rem;
padding: 0 1rem;
padding: 0 0.5rem;
margin-bottom: 1rem;
margin-top: -1rem;
}
@media (min-width: 640px) {
.feed-header {
padding: 0 1rem;
}
}
.feed-controls {
display: flex;
justify-content: flex-end;
align-items: center;
flex-direction: column;
gap: 1rem;
align-items: stretch;
}
@media (max-width: 640px) {
@media (min-width: 640px) {
.feed-controls {
justify-content: flex-start;
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
}
.feed-filter {
display: flex;
align-items: center;
margin-right: 1rem;
flex-shrink: 0;
min-width: fit-content;
}
@media (min-width: 640px) {
.feed-filter {
margin-right: 1rem;
}
}
.feed-filter-label {
display: flex;
align-items: center;
@ -176,6 +199,11 @@ @@ -176,6 +199,11 @@
width: 100%;
justify-content: flex-start;
}
.feed-header-buttons > * {
flex: 1 1 auto;
min-width: 0;
}
}
.see-new-events-btn-header {
@ -188,6 +216,16 @@ @@ -188,6 +216,16 @@
transition: all 0.2s;
white-space: nowrap;
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 {
@ -217,6 +255,16 @@ @@ -217,6 +255,16 @@
white-space: nowrap;
text-decoration: none;
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) {

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

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

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

@ -252,19 +252,6 @@ @@ -252,19 +252,6 @@
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,
.empty-state {
padding: 2rem;

4
static/changelog.yaml

@ -1,7 +1,9 @@ @@ -1,7 +1,9 @@
versions:
'0.3.2':
- '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':
- 'Media attachments rendering in all feeds and views'
- 'NIP-92/NIP-94 image tags support'

4
static/healthz.json

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