mirror of
https://git.shipoclu.com/moon/plspace.git
synced 2026-08-13 02:42:30 +00:00
Compare commits
1
Commits
80c6134359
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c76bab55dd |
@@ -74,9 +74,7 @@
|
||||
</form>
|
||||
|
||||
<p class="site-account-links">
|
||||
<a href="#/settings">Settings</a>
|
||||
{#if session.signedIn}
|
||||
<span aria-hidden="true">|</span>
|
||||
<button
|
||||
type="button"
|
||||
class="link-button site-header-logout"
|
||||
@@ -85,7 +83,6 @@
|
||||
LogOut
|
||||
</button>
|
||||
{:else}
|
||||
<span aria-hidden="true">|</span>
|
||||
<a href="#/login">LogIn</a>
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { render } from '@testing-library/svelte'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { APP_SERVICES } from '$lib/app-services'
|
||||
import { account, session, testServices } from '$test/fixtures'
|
||||
import SiteHeader from './SiteHeader.svelte'
|
||||
|
||||
describe('SiteHeader account controls', () => {
|
||||
it('shows one visible logout control and leaves Settings to the main navigation', () => {
|
||||
const services = testServices({
|
||||
session: session({ signedIn: true, token: 'token', me: account() }),
|
||||
})
|
||||
const view = render(SiteHeader, {
|
||||
context: new Map([[APP_SERVICES, services]]),
|
||||
})
|
||||
|
||||
expect(view.getByRole('button', { name: 'LogOut' })).toHaveClass('site-header-logout')
|
||||
expect(view.queryByRole('link', { name: 'Settings' })).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -9,7 +9,6 @@
|
||||
ownerName: string
|
||||
ownerEmojis?: CustomEmoji[]
|
||||
accounts: Account[]
|
||||
viewAllHref: string
|
||||
missing?: string[]
|
||||
loading?: boolean
|
||||
}
|
||||
@@ -18,7 +17,6 @@
|
||||
ownerName,
|
||||
ownerEmojis,
|
||||
accounts,
|
||||
viewAllHref,
|
||||
missing = [],
|
||||
loading = false,
|
||||
}: Props = $props()
|
||||
@@ -45,7 +43,4 @@
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<p class="top-eight-view-all">
|
||||
<a href={viewAllHref}>View All of <EmojiText text={ownerName} emojis={ownerEmojis} />'s Friends</a>
|
||||
</p>
|
||||
</Module>
|
||||
|
||||
@@ -200,6 +200,12 @@
|
||||
return fallbackMood(status.account.id)
|
||||
}
|
||||
|
||||
/** The compact home row otherwise has no output for a media-only status. */
|
||||
function imagePreviewFor(status: Status) {
|
||||
if (toPlainText(status.spoiler_text || status.content)) return null
|
||||
return status.media_attachments.find((attachment) => attachment.type === 'image') ?? null
|
||||
}
|
||||
|
||||
function messageOf(cause: unknown): string {
|
||||
return cause instanceof Error ? cause.message : 'Could not load that.'
|
||||
}
|
||||
@@ -332,6 +338,7 @@
|
||||
{#each friendStatus as status (status.id)}
|
||||
{@const entry = status.reblog ?? status}
|
||||
{@const author = accountForStatus(entry, preferences.heleneposting)}
|
||||
{@const imagePreview = imagePreviewFor(entry)}
|
||||
<li class="status-line" data-account={entry.account.acct}>
|
||||
<Avatar account={author} />
|
||||
<div class="status-line-body">
|
||||
@@ -345,6 +352,22 @@
|
||||
tags={entry.tags}
|
||||
inline
|
||||
/>
|
||||
{#if imagePreview}
|
||||
<a
|
||||
class="status-line-media"
|
||||
data-sensitive={entry.sensitive ? 'true' : 'false'}
|
||||
href={`#/blog/${entry.id}`}
|
||||
aria-label={imagePreview.description || 'View image post'}
|
||||
>
|
||||
<img
|
||||
class="status-line-media-image"
|
||||
src={imagePreview.preview_url ?? imagePreview.url}
|
||||
alt={imagePreview.description ?? ''}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
<a class="status-line-time" href={`#/blog/${entry.id}`}>
|
||||
{relativeTime(entry.created_at)}
|
||||
</a>
|
||||
|
||||
+39
-1
@@ -2,7 +2,7 @@ import { render, waitFor } from '@testing-library/svelte'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { APP_SERVICES } from '$lib/app-services'
|
||||
import { TIMELINE_REFRESH, TimelineRefreshController } from '$lib/timeline-refresh'
|
||||
import { account, session, testServices } from '$test/fixtures'
|
||||
import { account, session, status, testServices } from '$test/fixtures'
|
||||
import Home from './Home.svelte'
|
||||
|
||||
describe('Home timeline refresh', () => {
|
||||
@@ -38,3 +38,41 @@ describe('Home timeline refresh', () => {
|
||||
expect(fetchNotifications).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Home Friend Status previews', () => {
|
||||
it('shows a small preview for an image-only post', async () => {
|
||||
const imagePost = status({
|
||||
id: 'image-only',
|
||||
content: '',
|
||||
media_attachments: [
|
||||
{
|
||||
id: 'photo',
|
||||
type: 'image',
|
||||
url: 'https://media.example/full.jpg',
|
||||
preview_url: 'https://media.example/small.jpg',
|
||||
description: 'A tiny cat',
|
||||
},
|
||||
],
|
||||
})
|
||||
const fetchTimeline = vi.fn(async (_api, kind: string) => ({
|
||||
items: kind === 'home' ? [imagePost] : [],
|
||||
links: {},
|
||||
}))
|
||||
const services = testServices({
|
||||
session: session({ token: 'token', me: account(), signedIn: true }),
|
||||
endpoints: {
|
||||
fetchTimeline,
|
||||
fetchFollowing: vi.fn().mockResolvedValue({ items: [], links: {} }),
|
||||
fetchNotifications: vi.fn().mockResolvedValue({ items: [], links: {} }),
|
||||
},
|
||||
})
|
||||
const view = render(Home, {
|
||||
context: new Map([[APP_SERVICES, services]]),
|
||||
})
|
||||
|
||||
const preview = await view.findByRole('img', { name: 'A tiny cat' })
|
||||
expect(preview).toHaveAttribute('src', 'https://media.example/small.jpg')
|
||||
expect(preview).toHaveClass('status-line-media-image')
|
||||
expect(preview.closest('a')).toHaveAttribute('href', '#/blog/image-only')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -436,7 +436,6 @@
|
||||
ownerName={firstName}
|
||||
ownerEmojis={account.emojis}
|
||||
accounts={topEightAccounts}
|
||||
viewAllHref={`#/@${account.acct}/friends`}
|
||||
missing={topEightMissing}
|
||||
loading={topEightLoading}
|
||||
/>
|
||||
|
||||
@@ -144,10 +144,8 @@ describe('Profile', () => {
|
||||
expect(await view.findByText('Bob')).toBeInTheDocument()
|
||||
expect(await view.findByText('Carol')).toBeInTheDocument()
|
||||
expect(view.container.querySelector('.top-eight-space .friend-count')).not.toBeInTheDocument()
|
||||
expect(view.getByRole('link', { name: "View All of Alice's Friends" })).toHaveAttribute(
|
||||
'href',
|
||||
'#/@alice/friends',
|
||||
)
|
||||
expect(view.queryByRole('link', { name: "View All of Alice's Friends" })).not.toBeInTheDocument()
|
||||
expect(view.getByRole('link', { name: '[view all]' })).toHaveAttribute('href', '#/@alice/friends')
|
||||
expect(view.getAllByText('Hello from my profile.').length).toBeGreaterThan(0)
|
||||
expect(view.queryByText('My top 8:')).not.toBeInTheDocument()
|
||||
expect(view.queryByText('@bob@remote.test')).not.toBeInTheDocument()
|
||||
|
||||
+28
-6
@@ -32,19 +32,22 @@
|
||||
}
|
||||
|
||||
.blog-entry-header {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 6px;
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.blog-entry-avatar {
|
||||
flex: 0 0 auto;
|
||||
position: absolute;
|
||||
inset: 0 auto auto 0;
|
||||
}
|
||||
|
||||
.blog-entry-byline {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
margin-left: calc(var(--ms-avatar-size) + 8px);
|
||||
}
|
||||
|
||||
.blog-entry[data-compact='true'] .blog-entry-byline {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.blog-entry-author {
|
||||
@@ -704,3 +707,22 @@
|
||||
color: var(--ms-muted-fg);
|
||||
font-size: var(--ms-font-size-small);
|
||||
}
|
||||
|
||||
.status-line-media {
|
||||
display: block;
|
||||
width: min(96px, 100%);
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.status-line-media-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-height: 72px;
|
||||
object-fit: cover;
|
||||
border: 1px solid var(--ms-avatar-border);
|
||||
background: var(--ms-table-stripe-bg);
|
||||
}
|
||||
|
||||
.status-line-media[data-sensitive='true'] .status-line-media-image {
|
||||
filter: blur(8px);
|
||||
}
|
||||
|
||||
@@ -132,6 +132,15 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* `.link-button` normally uses the page's navy link colour. In the navy
|
||||
utility bar that makes LogOut disappear, so mutations in this region use
|
||||
the same high-contrast token as its anchors. */
|
||||
.site-header-logout,
|
||||
.site-header-logout:hover,
|
||||
.site-header-logout:focus-visible {
|
||||
color: var(--ms-chrome-link);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- nav row */
|
||||
|
||||
.site-nav {
|
||||
|
||||
@@ -260,12 +260,6 @@
|
||||
font-size: var(--ms-font-size-small);
|
||||
}
|
||||
|
||||
.top-eight-view-all {
|
||||
margin: 22px 0 2px;
|
||||
text-align: right;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.top-eight-grid {
|
||||
grid-template-columns: repeat(2, minmax(64px, 1fr));
|
||||
|
||||
Reference in New Issue
Block a user