Skip to content

Commit 92bbafa

Browse files
authored
Add download patch functionality (#17)
Signed-off-by: Kai Wagner <[email protected]>
1 parent 69581ff commit 92bbafa

File tree

5 files changed

+105
-4
lines changed

5 files changed

+105
-4
lines changed

app/assets/stylesheets/components/messages.css

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,45 @@
229229
font-size: var(--font-size-sm);
230230
}
231231

232+
summary.attachment-info {
233+
cursor: pointer;
234+
}
235+
236+
.attachment-summary-row {
237+
display: flex;
238+
gap: var(--spacing-3);
239+
align-items: center;
240+
width: 100%;
241+
}
242+
243+
.attachment-download {
244+
margin-left: auto;
245+
font-size: var(--font-size-xs);
246+
color: var(--color-text-link);
247+
text-decoration: none;
248+
}
249+
250+
.attachment-download:hover {
251+
color: var(--color-text-link-hover);
252+
text-decoration: underline;
253+
}
254+
255+
.attachment-content {
256+
margin-top: var(--spacing-2);
257+
padding: var(--spacing-2);
258+
background: var(--color-bg-container);
259+
border: var(--border-width) solid var(--color-border);
260+
border-radius: var(--border-radius-sm);
261+
font-size: var(--font-size-xs);
262+
overflow-x: auto;
263+
}
264+
265+
.attachment-content code {
266+
display: block;
267+
font-family: var(--font-family-mono);
268+
white-space: pre;
269+
}
270+
232271
.filename {
233272
font-weight: var(--font-weight-medium);
234273
color: var(--color-text-primary);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class AttachmentsController < ApplicationController
2+
def show
3+
attachment = Attachment.find(params[:id])
4+
data = attachment.decoded_body
5+
return head :not_found unless data
6+
7+
filename = attachment.file_name.presence || "attachment-#{attachment.id}"
8+
content_type = attachment.content_type.presence || "application/octet-stream"
9+
10+
send_data data, filename: filename, type: content_type, disposition: "attachment"
11+
end
12+
end
13+

app/views/topics/_message.html.slim

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,22 @@
4141
.message-attachments
4242
h4 Attachments:
4343
- message.attachments.each do |attachment|
44-
.attachment
45-
.attachment-info
46-
span.filename = attachment.file_name
47-
span.content-type = attachment.content_type if attachment.content_type
44+
- if attachment.patch?
45+
details.attachment
46+
summary.attachment-info
47+
span.attachment-summary-row
48+
span.filename = attachment.file_name
49+
span.content-type = attachment.content_type if attachment.content_type
50+
= link_to "Download", attachment_path(attachment), class: "attachment-download", download: attachment.file_name, data: { turbo: false }
51+
pre.attachment-content
52+
code.language-diff
53+
= attachment.decoded_body
54+
- else
55+
.attachment
56+
.attachment-info
57+
span.filename = attachment.file_name
58+
span.content-type = attachment.content_type if attachment.content_type
59+
= link_to "Download", attachment_path(attachment), class: "attachment-download", download: attachment.file_name, data: { turbo: false }
4860

4961
- if message.import_log.present?
5062
.import-metadata

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
get '/auth/:provider/callback', to: 'omniauth_callbacks#google_oauth2'
5656

5757
post "messages/:id/read", to: "messages#read", as: :read_message
58+
resources :attachments, only: [:show]
5859

5960
if defined?(PgHero)
6061
constraints AdminConstraint.new do

spec/requests/topics_spec.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,17 @@
7979
reply_position = response.body.index(reply_message.body)
8080
expect(root_position).to be < reply_position
8181
end
82+
83+
it "renders patch attachments inline as expandable diff blocks" do
84+
create(:attachment, :patch_file, message: root_message)
85+
86+
get topic_path(topic)
87+
88+
expect(response).to have_http_status(:success)
89+
expect(response.body).to include("Attachments:")
90+
expect(response.body).to include('class="language-diff"')
91+
expect(response.body).to include("diff --git")
92+
end
8293
end
8394

8495
context "with nonexistent topic" do
@@ -89,6 +100,31 @@
89100
end
90101
end
91102

103+
describe "GET /attachments/:id" do
104+
let!(:creator) { create(:alias) }
105+
let!(:topic) { create(:topic, creator: creator) }
106+
let!(:message) { create(:message, topic: topic, sender: creator, reply_to: nil, created_at: 2.hours.ago) }
107+
108+
it "streams the attachment as a download with the right filename" do
109+
attachment = create(:attachment, :patch_file, message: message)
110+
111+
get attachment_path(attachment)
112+
113+
expect(response).to have_http_status(:success)
114+
expect(response.headers["Content-Disposition"]).to include("attachment")
115+
expect(response.headers["Content-Disposition"]).to include(attachment.file_name)
116+
expect(response.body).to include("diff --git")
117+
end
118+
119+
it "returns 404 when the attachment body is missing" do
120+
attachment = create(:attachment, body: nil, message: message)
121+
122+
get attachment_path(attachment)
123+
124+
expect(response).to have_http_status(:not_found)
125+
end
126+
end
127+
92128
describe "GET /topics/search" do
93129
let!(:creator1) { create(:alias) }
94130
let!(:creator2) { create(:alias) }

0 commit comments

Comments
 (0)