improved component composition and added tests.

This commit is contained in:
Moon.eth
2026-07-29 10:30:28 +09:00
parent 95f9d73681
commit 264ae17a8d
39 changed files with 1811 additions and 147 deletions
+8 -6
View File
@@ -7,8 +7,7 @@
* rather than disappearing, so the box keeps its shape.
*/
import type { Account, Relationship } from '$lib/api/types'
import { session } from '$lib/stores/session.svelte'
import { blockAccount, followAccount, unblockAccount, unfollowAccount } from '$lib/api/endpoints'
import { useAppServices } from '$lib/app-services'
import { displayNameOf } from '$lib/util/profile'
import Module from '../common/Module.svelte'
@@ -20,6 +19,7 @@
let { account, relationship, onrelationship }: Props = $props()
const { endpoints, session } = useAppServices()
let busy = $state(false)
let error = $state<string | null>(null)
@@ -56,17 +56,19 @@
function toggleFollow(): void {
if (blocking) {
void run(() => unblockAccount(session.api, account.id))
void run(() => endpoints.unblockAccount(session.api, account.id))
} else if (following || requested) {
void run(() => unfollowAccount(session.api, account.id))
void run(() => endpoints.unfollowAccount(session.api, account.id))
} else {
void run(() => followAccount(session.api, account.id))
void run(() => endpoints.followAccount(session.api, account.id))
}
}
function toggleBlock(): void {
void run(() =>
blocking ? unblockAccount(session.api, account.id) : blockAccount(session.api, account.id),
blocking
? endpoints.unblockAccount(session.api, account.id)
: endpoints.blockAccount(session.api, account.id),
)
}
</script>
+1 -1
View File
@@ -21,7 +21,7 @@
<Module {title} flush>
<table class="data-table details-table">
<tbody>
{#each fields as field (field.name)}
{#each fields as field, index (`${field.name}:${index}`)}
<tr class="details-row" data-verified={field.verified ? 'true' : 'false'}>
<th class="data-table-label details-label" scope="row">{field.name}</th>
<td class="data-table-value details-value" data-verified={field.verified ? 'true' : 'false'}>
+2 -7
View File
@@ -9,6 +9,7 @@
import type { Account } from '$lib/api/types'
import { displayNameOf, formatCount, profilePath } from '$lib/util/profile'
import Module from '../common/Module.svelte'
import Avatar from '../common/Avatar.svelte'
interface Props {
title: string
@@ -66,13 +67,7 @@
<li class="friend-card" data-account={friend.acct}>
<a class="friend-card-link" href={profilePath(friend)}>
<span class="friend-card-name">{displayNameOf(friend)}</span>
<img
class="friend-card-photo"
src={friend.avatar_static || friend.avatar}
alt=""
loading="lazy"
decoding="async"
/>
<Avatar account={friend} plain size="friend" class="friend-card-photo" />
</a>
</li>
{/each}
+1 -1
View File
@@ -20,7 +20,7 @@
<Module {title} flush>
<table class="data-table interests-table">
<tbody>
{#each interests as entry (entry.row)}
{#each interests as entry, index (`${entry.row}:${index}`)}
<tr class="interests-row" data-row={entry.row.toLowerCase()}>
<th class="data-table-label interests-label" scope="row">{entry.row}</th>
<td class="data-table-value interests-value">
@@ -12,7 +12,7 @@
import { displayNameOf, fullHandle } from '$lib/util/profile'
import { renderDisplayName } from '$lib/util/html'
import { relativeTime, shortDate, yearsSince } from '$lib/util/time'
import { session } from '$lib/stores/session.svelte'
import { useAppServices } from '$lib/app-services'
interface Props {
profile: ProfileView
@@ -20,6 +20,7 @@
let { profile }: Props = $props()
const { session } = useAppServices()
const account = $derived(profile.account)
const name = $derived(renderDisplayName(displayNameOf(account), account.emojis))
const handle = $derived(fullHandle(account, session.host))
@@ -0,0 +1,32 @@
import { render } from '@testing-library/svelte'
import { describe, expect, it } from 'vitest'
import DetailsTable from './DetailsTable.svelte'
import InterestsTable from './InterestsTable.svelte'
describe('profile tables', () => {
it('renders duplicate interest aliases without duplicate keyed-each failures', () => {
const view = render(InterestsTable, {
title: 'Interests',
interests: [
{ row: 'Music', value: 'Synthpop' },
{ row: 'Music', value: 'Shoegaze' },
],
})
expect(view.getByText('Synthpop')).toBeInTheDocument()
expect(view.getByText('Shoegaze')).toBeInTheDocument()
})
it('renders repeated free-form field names', () => {
const view = render(DetailsTable, {
title: 'Details',
fields: [
{ name: 'Website', value: 'One', verified: false },
{ name: 'Website', value: 'Two', verified: true },
],
})
expect(view.getByText('One')).toBeInTheDocument()
expect(view.getByText('Two')).toBeInTheDocument()
})
})