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>
|
</form>
|
||||||
|
|
||||||
<p class="site-account-links">
|
<p class="site-account-links">
|
||||||
<a href="#/settings">Settings</a>
|
|
||||||
{#if session.signedIn}
|
{#if session.signedIn}
|
||||||
<span aria-hidden="true">|</span>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="link-button site-header-logout"
|
class="link-button site-header-logout"
|
||||||
@@ -85,7 +83,6 @@
|
|||||||
LogOut
|
LogOut
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
<span aria-hidden="true">|</span>
|
|
||||||
<a href="#/login">LogIn</a>
|
<a href="#/login">LogIn</a>
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
</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
|
ownerName: string
|
||||||
ownerEmojis?: CustomEmoji[]
|
ownerEmojis?: CustomEmoji[]
|
||||||
accounts: Account[]
|
accounts: Account[]
|
||||||
viewAllHref: string
|
|
||||||
missing?: string[]
|
missing?: string[]
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
}
|
}
|
||||||
@@ -18,7 +17,6 @@
|
|||||||
ownerName,
|
ownerName,
|
||||||
ownerEmojis,
|
ownerEmojis,
|
||||||
accounts,
|
accounts,
|
||||||
viewAllHref,
|
|
||||||
missing = [],
|
missing = [],
|
||||||
loading = false,
|
loading = false,
|
||||||
}: Props = $props()
|
}: Props = $props()
|
||||||
@@ -45,7 +43,4 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<p class="top-eight-view-all">
|
|
||||||
<a href={viewAllHref}>View All of <EmojiText text={ownerName} emojis={ownerEmojis} />'s Friends</a>
|
|
||||||
</p>
|
|
||||||
</Module>
|
</Module>
|
||||||
|
|||||||
@@ -200,6 +200,12 @@
|
|||||||
return fallbackMood(status.account.id)
|
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 {
|
function messageOf(cause: unknown): string {
|
||||||
return cause instanceof Error ? cause.message : 'Could not load that.'
|
return cause instanceof Error ? cause.message : 'Could not load that.'
|
||||||
}
|
}
|
||||||
@@ -332,6 +338,7 @@
|
|||||||
{#each friendStatus as status (status.id)}
|
{#each friendStatus as status (status.id)}
|
||||||
{@const entry = status.reblog ?? status}
|
{@const entry = status.reblog ?? status}
|
||||||
{@const author = accountForStatus(entry, preferences.heleneposting)}
|
{@const author = accountForStatus(entry, preferences.heleneposting)}
|
||||||
|
{@const imagePreview = imagePreviewFor(entry)}
|
||||||
<li class="status-line" data-account={entry.account.acct}>
|
<li class="status-line" data-account={entry.account.acct}>
|
||||||
<Avatar account={author} />
|
<Avatar account={author} />
|
||||||
<div class="status-line-body">
|
<div class="status-line-body">
|
||||||
@@ -345,6 +352,22 @@
|
|||||||
tags={entry.tags}
|
tags={entry.tags}
|
||||||
inline
|
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}`}>
|
<a class="status-line-time" href={`#/blog/${entry.id}`}>
|
||||||
{relativeTime(entry.created_at)}
|
{relativeTime(entry.created_at)}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
+39
-1
@@ -2,7 +2,7 @@ import { render, waitFor } from '@testing-library/svelte'
|
|||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
import { APP_SERVICES } from '$lib/app-services'
|
import { APP_SERVICES } from '$lib/app-services'
|
||||||
import { TIMELINE_REFRESH, TimelineRefreshController } from '$lib/timeline-refresh'
|
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'
|
import Home from './Home.svelte'
|
||||||
|
|
||||||
describe('Home timeline refresh', () => {
|
describe('Home timeline refresh', () => {
|
||||||
@@ -38,3 +38,41 @@ describe('Home timeline refresh', () => {
|
|||||||
expect(fetchNotifications).toHaveBeenCalledOnce()
|
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}
|
ownerName={firstName}
|
||||||
ownerEmojis={account.emojis}
|
ownerEmojis={account.emojis}
|
||||||
accounts={topEightAccounts}
|
accounts={topEightAccounts}
|
||||||
viewAllHref={`#/@${account.acct}/friends`}
|
|
||||||
missing={topEightMissing}
|
missing={topEightMissing}
|
||||||
loading={topEightLoading}
|
loading={topEightLoading}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -144,10 +144,8 @@ describe('Profile', () => {
|
|||||||
expect(await view.findByText('Bob')).toBeInTheDocument()
|
expect(await view.findByText('Bob')).toBeInTheDocument()
|
||||||
expect(await view.findByText('Carol')).toBeInTheDocument()
|
expect(await view.findByText('Carol')).toBeInTheDocument()
|
||||||
expect(view.container.querySelector('.top-eight-space .friend-count')).not.toBeInTheDocument()
|
expect(view.container.querySelector('.top-eight-space .friend-count')).not.toBeInTheDocument()
|
||||||
expect(view.getByRole('link', { name: "View All of Alice's Friends" })).toHaveAttribute(
|
expect(view.queryByRole('link', { name: "View All of Alice's Friends" })).not.toBeInTheDocument()
|
||||||
'href',
|
expect(view.getByRole('link', { name: '[view all]' })).toHaveAttribute('href', '#/@alice/friends')
|
||||||
'#/@alice/friends',
|
|
||||||
)
|
|
||||||
expect(view.getAllByText('Hello from my profile.').length).toBeGreaterThan(0)
|
expect(view.getAllByText('Hello from my profile.').length).toBeGreaterThan(0)
|
||||||
expect(view.queryByText('My top 8:')).not.toBeInTheDocument()
|
expect(view.queryByText('My top 8:')).not.toBeInTheDocument()
|
||||||
expect(view.queryByText('@bob@remote.test')).not.toBeInTheDocument()
|
expect(view.queryByText('@bob@remote.test')).not.toBeInTheDocument()
|
||||||
|
|||||||
+28
-6
@@ -32,19 +32,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.blog-entry-header {
|
.blog-entry-header {
|
||||||
display: flex;
|
position: relative;
|
||||||
gap: 8px;
|
margin-bottom: 0;
|
||||||
align-items: flex-start;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.blog-entry-avatar {
|
.blog-entry-avatar {
|
||||||
flex: 0 0 auto;
|
position: absolute;
|
||||||
|
inset: 0 auto auto 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blog-entry-byline {
|
.blog-entry-byline {
|
||||||
min-width: 0;
|
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 {
|
.blog-entry-author {
|
||||||
@@ -704,3 +707,22 @@
|
|||||||
color: var(--ms-muted-fg);
|
color: var(--ms-muted-fg);
|
||||||
font-size: var(--ms-font-size-small);
|
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;
|
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 */
|
/* ---------------------------------------------------------------- nav row */
|
||||||
|
|
||||||
.site-nav {
|
.site-nav {
|
||||||
|
|||||||
@@ -260,12 +260,6 @@
|
|||||||
font-size: var(--ms-font-size-small);
|
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) {
|
@media (max-width: 520px) {
|
||||||
.top-eight-grid {
|
.top-eight-grid {
|
||||||
grid-template-columns: repeat(2, minmax(64px, 1fr));
|
grid-template-columns: repeat(2, minmax(64px, 1fr));
|
||||||
|
|||||||
Reference in New Issue
Block a user