Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 35 additions & 6 deletions app/web/feeds/responder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ class << self
# @param identifier [String]
# @return [String] serialized feed body.
def call(request:, target_kind:, identifier:)
feed_request = Request.call(request:, target_kind:, identifier:)
resolved_source = SourceResolver.call(feed_request)
result = Service.call(resolved_source)
normalized_identifier = feed_request.feed_name || identifier
feed_request, resolved_source, result = resolve_request(request:, target_kind:, identifier:)
body = write_response(response: request.response, representation: feed_request.representation, result:)

emit_result(target_kind:, identifier: normalized_identifier, resolved_source:, result:)
emit_response_result(target_kind:, identifier:, feed_request:, resolved_source:, result:)
body
rescue StandardError => error
emit_failure(target_kind:, identifier:, error:)
Expand All @@ -27,6 +23,39 @@ def call(request:, target_kind:, identifier:)

private

# @param request [Rack::Request]
# @param target_kind [Symbol]
# @param identifier [String]
# @return [Array<(Html2rss::Web::Feeds::Contracts::Request, Html2rss::Web::Feeds::Contracts::ResolvedSource, Html2rss::Web::Feeds::Contracts::RenderResult)>]
def resolve_request(request:, target_kind:, identifier:)
feed_request = Request.call(request:, target_kind:, identifier:)
resolved_source = SourceResolver.call(feed_request)
result = Service.call(resolved_source)
[feed_request, resolved_source, result]
end

# @param feed_request [Html2rss::Web::Feeds::Contracts::Request]
# @param identifier [String]
# @return [String]
def normalized_identifier(feed_request, identifier)
feed_request.feed_name || identifier
end

# @param target_kind [Symbol]
# @param identifier [String]
# @param feed_request [Html2rss::Web::Feeds::Contracts::Request]
# @param resolved_source [Html2rss::Web::Feeds::Contracts::ResolvedSource]
# @param result [Html2rss::Web::Feeds::Contracts::RenderResult]
# @return [void]
def emit_response_result(target_kind:, identifier:, feed_request:, resolved_source:, result:)
emit_result(
target_kind:,
identifier: normalized_identifier(feed_request, identifier),
resolved_source:,
result:
)
end

# @param response [Rack::Response]
# @param representation [Symbol]
# @param result [Html2rss::Web::Feeds::Contracts::RenderResult]
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/__tests__/App.contract.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('App contract', () => {
fireEvent.click(screen.getByRole('button', { name: 'Generate feed URL' }));

await waitFor(() => {
expect(screen.getByText('Your feed is ready')).toBeInTheDocument();
expect(screen.getByText('Feed ready')).toBeInTheDocument();
expect(screen.getByText('Example Feed')).toBeInTheDocument();
expect(screen.getByLabelText('Feed URL')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Copy feed URL' })).toBeInTheDocument();
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/__tests__/ResultDisplay.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ describe('ResultDisplay', () => {
it('renders the success state actions and richer preview cards', async () => {
render(<ResultDisplay result={mockResult} onCreateAnother={mockOnCreateAnother} />);

expect(screen.getByText('Your feed is ready')).toBeInTheDocument();
expect(screen.getByText('Feed ready')).toBeInTheDocument();
expect(screen.getByText('Test Feed')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Copy feed URL' })).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Subscribe in reader' })).toHaveAttribute(
expect(screen.getByRole('link', { name: 'Open in feed reader' })).toHaveAttribute(
'href',
'feed:https://example.com/feed.xml'
);
Expand Down Expand Up @@ -96,7 +96,7 @@ describe('ResultDisplay', () => {
);

await waitFor(() => {
expect(screen.getByText('Your feed is ready')).toBeInTheDocument();
expect(screen.getByText('Feed ready')).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Open feed' })).toBeInTheDocument();
expect(screen.getByText('Loading preview…')).toBeInTheDocument();
});
Expand Down
33 changes: 20 additions & 13 deletions frontend/src/components/ResultDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,28 @@ export function ResultDisplay({ result, onCreateAnother }: ResultDisplayProps) {
return (
<section class="result-shell layout-stack" aria-live="polite">
<header
class="result-hero layout-rail-reading layout-stack"
class="result-hero ui-card ui-card--roomy ui-hero layout-rail-reading layout-stack"
style={{ '--stack-gap': 'var(--space-3)' }}
>
<p class="result-kicker ui-eyebrow">Feed created</p>
<h1 class="result-title">Your feed is ready</h1>
<p class="result-meta layout-rail-copy">{feed.name}</p>
<p class="result-lede layout-rail-copy">Subscribe to this URL in your RSS reader.</p>
<div class="result-hero__masthead ui-hero__masthead">
<div class="result-hero__icon-wrap ui-hero__icon-wrap" aria-hidden="true">
<img class="result-hero__icon ui-hero__icon" src="/feed.svg" alt="" />
</div>
<div class="layout-stack layout-stack--tight">
<h1 class="result-title ui-display-title">Feed ready</h1>
<p class="result-meta layout-rail-copy">{feed.name}</p>
</div>
</div>
<div class="result-hero__actions ui-hero__actions">
{subscribeUrl && (
<a href={subscribeUrl} class="btn btn--ghost result-hero__reader">
Open in feed reader
</a>
)}
<a href={fullUrl} class="btn btn--ghost" target="_blank" rel="noopener noreferrer">
Open feed
</a>
</div>
{result.retry && (
<p class="field-help">
{`Retried automatically with ${result.retry.to} after ${result.retry.from} could not finish the page.`}
Expand All @@ -67,14 +82,6 @@ export function ResultDisplay({ result, onCreateAnother }: ResultDisplayProps) {
/>

<div class="result-actions result-actions--quiet layout-rail-reading">
{subscribeUrl && (
<a href={subscribeUrl} class="btn btn--ghost">
Subscribe in reader
</a>
)}
<a href={fullUrl} class="btn btn--ghost" target="_blank" rel="noopener noreferrer">
Open feed
</a>
<a href={jsonFeedUrl} class="btn btn--ghost" target="_blank" rel="noopener noreferrer">
Open JSON Feed
</a>
Expand Down
86 changes: 3 additions & 83 deletions frontend/src/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -263,71 +263,6 @@ a:focus-visible {
box-shadow: var(--focus-ring);
}

.btn {
min-height: 3rem;
padding: 0 1.25rem;
display: inline-flex;
align-items: center;
justify-content: center;
border: var(--border-width) solid transparent;
border-radius: 999px;
background: transparent;
color: var(--text-strong);
text-decoration: none;
cursor: pointer;
font-weight: 600;
transition:
transform var(--transition-fast),
background-color var(--transition-fast),
border-color var(--transition-fast),
color var(--transition-fast),
opacity var(--transition-fast);
}

.btn:hover:not(:disabled) {
transform: translateY(-0.04rem);
}

.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}

.btn--primary {
background: var(--accent);
color: var(--text-inverse);
}

.btn--primary:hover:not(:disabled) {
background: var(--accent-strong);
}

.btn--ghost {
border-color: var(--border-subtle);
background: var(--surface-elevated);
}

.btn--ghost:hover:not(:disabled) {
border-color: var(--border-strong);
background: rgba(255, 255, 255, 0.08);
}

.btn--quiet,
.btn--linkish {
min-height: auto;
padding: 0;
border: 0;
border-radius: 0;
background: transparent;
color: var(--text-muted);
}

.btn--quiet:hover:not(:disabled),
.btn--linkish:hover:not(:disabled) {
background: transparent;
color: var(--text-strong);
}

.notice {
display: grid;
gap: var(--space-2);
Expand Down Expand Up @@ -504,18 +439,9 @@ a:focus-visible {
text-align: left;
}

.result-hero {
justify-items: start;
text-align: left;
}

.result-title {
margin: 0;
color: var(--text-strong);
font-family: var(--font-family-display);
font-size: clamp(1.9rem, 4.2vw, 2.85rem);
line-height: 0.98;
letter-spacing: -0.03em;
.result-hero__reader {
border-color: rgba(255, 147, 0, 0.24);
background: rgba(255, 147, 0, 0.12);
}

.result-meta {
Expand All @@ -526,12 +452,6 @@ a:focus-visible {
text-align: left;
}

.result-lede {
margin: 0;
color: var(--text-muted);
font-size: var(--font-size-1);
}

.result-preview {
justify-items: start;
padding-top: var(--section-gap);
Expand Down
6 changes: 6 additions & 0 deletions public/feed-reader-link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
document.addEventListener('DOMContentLoaded', function () {
var readerLink = document.querySelector('[data-feed-reader-link]');
if (!readerLink) return;

readerLink.setAttribute('href', 'feed:' + window.location.href);
});
Loading
Loading