Skip to content

Commit 723202b

Browse files
Allow setting paste title
1 parent 662ce5a commit 723202b

File tree

11 files changed

+91
-18
lines changed

11 files changed

+91
-18
lines changed

src/db.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ static MIGRATIONS: LazyLock<Migrations> = LazyLock::new(|| {
4343
),
4444
M::up(include_str!("migrations/0005-drop-text-column.sql")),
4545
M::up(include_str!("migrations/0006-add-nonce-column.sql")),
46+
M::up(include_str!("migrations/0007-add-title-column.sql")),
4647
])
4748
});
4849

@@ -86,6 +87,8 @@ pub mod write {
8687
pub uid: Option<i64>,
8788
/// Optional password to encrypt the entry
8889
pub password: Option<String>,
90+
/// Title
91+
pub title: Option<String>,
8992
}
9093

9194
/// A compressed entry to be inserted.
@@ -163,6 +166,8 @@ pub mod read {
163166
pub uid: Option<i64>,
164167
/// Nonce for this entry
165168
pub nonce: Option<Vec<u8>>,
169+
/// Title
170+
pub title: Option<String>,
166171
}
167172

168173
/// Potentially decrypted but still compressed entry
@@ -173,6 +178,8 @@ pub mod read {
173178
must_be_deleted: bool,
174179
/// User identifier that inserted the entry
175180
uid: Option<i64>,
181+
/// Title
182+
title: Option<String>,
176183
}
177184

178185
/// An entry read from the database.
@@ -183,6 +190,8 @@ pub mod read {
183190
pub must_be_deleted: bool,
184191
/// User identifier that inserted the entry
185192
pub uid: Option<i64>,
193+
/// Title
194+
pub title: Option<String>,
186195
}
187196

188197
impl DatabaseEntry {
@@ -196,6 +205,7 @@ pub mod read {
196205
data: self.data,
197206
must_be_deleted: self.must_be_deleted,
198207
uid: self.uid,
208+
title: self.title,
199209
}),
200210
(Some(nonce), Some(password)) => {
201211
let encrypted = Encrypted::new(self.data, nonce);
@@ -204,6 +214,7 @@ pub mod read {
204214
data: decrypted,
205215
must_be_deleted: self.must_be_deleted,
206216
uid: self.uid,
217+
title: self.title,
207218
})
208219
}
209220
}
@@ -225,6 +236,7 @@ pub mod read {
225236
text,
226237
uid: self.uid,
227238
must_be_deleted: self.must_be_deleted,
239+
title: self.title,
228240
})
229241
}
230242
}
@@ -255,18 +267,19 @@ impl Database {
255267

256268
spawn_blocking(move || match entry.expires {
257269
None => conn.lock().execute(
258-
"INSERT INTO entries (id, uid, data, burn_after_reading, nonce) VALUES (?1, ?2, ?3, ?4, ?5)",
259-
params![id, entry.uid, data, entry.burn_after_reading, nonce],
270+
"INSERT INTO entries (id, uid, data, burn_after_reading, nonce, title) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
271+
params![id, entry.uid, data, entry.burn_after_reading, nonce, entry.title],
260272
),
261273
Some(expires) => conn.lock().execute(
262-
"INSERT INTO entries (id, uid, data, burn_after_reading, nonce, expires) VALUES (?1, ?2, ?3, ?4, ?5, datetime('now', ?6))",
274+
"INSERT INTO entries (id, uid, data, burn_after_reading, nonce, expires, title) VALUES (?1, ?2, ?3, ?4, ?5, datetime('now', ?6), ?7)",
263275
params![
264276
id,
265277
entry.uid,
266278
data,
267279
entry.burn_after_reading,
268280
nonce,
269-
format!("{expires} seconds")
281+
format!("{expires} seconds"),
282+
entry.title,
270283
],
271284
),
272285
})
@@ -282,7 +295,7 @@ impl Database {
282295

283296
let entry = spawn_blocking(move || {
284297
conn.lock().query_row(
285-
"SELECT data, burn_after_reading, uid, nonce, expires < datetime('now') FROM entries WHERE id=?1",
298+
"SELECT data, burn_after_reading, uid, nonce, expires < datetime('now'), title FROM entries WHERE id=?1",
286299
params![id_as_u32],
287300
|row| {
288301
Ok(read::DatabaseEntry {
@@ -291,6 +304,7 @@ impl Database {
291304
uid: row.get(2)?,
292305
nonce: row.get(3)?,
293306
expired: row.get::<_, Option<bool>>(4)?.unwrap_or(false),
307+
title: row.get::<_, Option<String>>(5)?,
294308
})
295309
},
296310
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE entries ADD COLUMN title TEXT;

src/pages.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,12 @@ pub struct Paste<'a> {
134134
ext: String,
135135
can_delete: bool,
136136
html: String,
137+
title: String,
137138
}
138139

139140
impl Paste<'_> {
140141
/// Construct new paste view from cache `key` and paste `html`.
141-
pub fn new(key: CacheKey, html: Html, can_delete: bool) -> Self {
142+
pub fn new(key: CacheKey, html: Html, can_delete: bool, title: String) -> Self {
142143
let html = html.into_inner();
143144

144145
Self {
@@ -148,6 +149,7 @@ impl Paste<'_> {
148149
ext: key.ext,
149150
can_delete,
150151
html,
152+
title,
151153
}
152154
}
153155
}
@@ -202,18 +204,20 @@ pub struct Qr<'a> {
202204
ext: String,
203205
can_delete: bool,
204206
code: qrcodegen::QrCode,
207+
title: String,
205208
}
206209

207210
impl Qr<'_> {
208211
/// Construct new QR code view from `code`.
209-
pub fn new(code: qrcodegen::QrCode, key: CacheKey) -> Self {
212+
pub fn new(code: qrcodegen::QrCode, key: CacheKey, title: String) -> Self {
210213
Self {
211214
meta: &env::METADATA,
212215
base_path: &env::BASE_PATH,
213216
id: key.id(),
214217
ext: key.ext,
215218
code,
216219
can_delete: false,
220+
title,
217221
}
218222
}
219223

src/routes/form.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ pub struct Entry {
1616
pub extension: Option<String>,
1717
pub expires: String,
1818
pub password: String,
19+
pub title: String,
1920
}
2021

2122
impl From<Entry> for write::Entry {
2223
fn from(entry: Entry) -> Self {
2324
let burn_after_reading = Some(entry.expires == "burn");
2425
let password = (!entry.password.is_empty()).then_some(entry.password);
26+
let title = (!entry.title.is_empty()).then_some(entry.title);
2527

2628
let expires = match entry.expires.parse::<NonZeroU32>() {
2729
Err(_) => None,
@@ -35,6 +37,7 @@ impl From<Entry> for write::Entry {
3537
burn_after_reading,
3638
uid: None,
3739
password,
40+
title,
3841
}
3942
}
4043
}

src/routes/json.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub struct Entry {
1717
pub expires: Option<NonZeroU32>,
1818
pub burn_after_reading: Option<bool>,
1919
pub password: Option<String>,
20+
pub title: Option<String>,
2021
}
2122

2223
#[derive(Deserialize, Serialize)]
@@ -33,6 +34,7 @@ impl From<Entry> for write::Entry {
3334
burn_after_reading: entry.burn_after_reading,
3435
uid: None,
3536
password: entry.password,
37+
title: entry.title,
3638
}
3739
}
3840
}

src/routes/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ mod tests {
5454
extension: Some("rs".to_string()),
5555
expires: "0".to_string(),
5656
password: "".to_string(),
57+
title: "".to_string(),
5758
};
5859

5960
let res = client.post(BASE_PATH.path()).form(&data).send().await?;
@@ -115,6 +116,7 @@ mod tests {
115116
extension: None,
116117
expires: "burn".to_string(),
117118
password: "".to_string(),
119+
title: "".to_string(),
118120
};
119121

120122
let res = client.post(BASE_PATH.path()).form(&data).send().await?;
@@ -154,6 +156,7 @@ mod tests {
154156
extension: None,
155157
expires: "burn".to_string(),
156158
password: password.to_string(),
159+
title: "".to_string(),
157160
};
158161

159162
let res = client.post(BASE_PATH.path()).form(&data).send().await?;
@@ -271,6 +274,7 @@ mod tests {
271274
extension: None,
272275
expires: "0".to_string(),
273276
password: "".to_string(),
277+
title: "".to_string(),
274278
};
275279

276280
let res = client.post(BASE_PATH.path()).form(&data).send().await?;
@@ -311,6 +315,7 @@ mod tests {
311315
extension: None,
312316
expires: "0".to_string(),
313317
password: "".to_string(),
318+
title: "".to_string(),
314319
};
315320

316321
let res = client.post(BASE_PATH.path()).form(&data).send().await?;

src/routes/paste.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,14 @@ async fn get_qr(
6666
state: AppState,
6767
key: CacheKey,
6868
headers: HeaderMap,
69+
title: String,
6970
) -> Result<pages::Qr<'static>, pages::ErrorResponse<'static>> {
7071
let id = key.id();
7172
let qr_code = tokio::task::spawn_blocking(move || qr_code_from(state, &headers, &id))
7273
.await
7374
.map_err(Error::from)??;
7475

75-
Ok(pages::Qr::new(qr_code, key))
76+
Ok(pages::Qr::new(qr_code, key, title))
7677
}
7778

7879
fn get_download(
@@ -116,21 +117,25 @@ async fn get_html(
116117

117118
if let Some(html) = state.cache.get(&key) {
118119
tracing::trace!(?key, "found cached item");
119-
return Ok(pages::Paste::new(key, html, can_delete).into_response());
120+
return Ok(
121+
pages::Paste::new(key, html, can_delete, entry.title.unwrap_or_default())
122+
.into_response(),
123+
);
120124
}
121125

122126
// TODO: turn this upside-down, i.e. cache it but only return a cached version if we were able
123127
// to decrypt the content. Highlighting is probably still much slower than decryption.
124128
let can_be_cached = !entry.must_be_deleted;
125129
let ext = key.ext.clone();
130+
let title = entry.title.clone().unwrap_or_default();
126131
let html = Html::from(entry, ext).await?;
127132

128133
if can_be_cached && !is_protected {
129134
tracing::trace!(?key, "cache item");
130135
state.cache.put(key.clone(), html.clone());
131136
}
132137

133-
Ok(pages::Paste::new(key, html, can_delete).into_response())
138+
Ok(pages::Paste::new(key, html, can_delete, title).into_response())
134139
}
135140

136141
pub async fn get(
@@ -161,7 +166,13 @@ pub async fn get(
161166

162167
match query.fmt {
163168
Some(Format::Raw) => return Ok(entry.text.into_response()),
164-
Some(Format::Qr) => return Ok(get_qr(state, key, headers).await.into_response()),
169+
Some(Format::Qr) => {
170+
return Ok(
171+
get_qr(state, key, headers, entry.title.clone().unwrap_or_default())
172+
.await
173+
.into_response(),
174+
)
175+
}
165176
None => (),
166177
}
167178

src/themes/style.css

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
border-bottom: 1px solid #d9d7d7;
3131
border-right: 1px solid #d9d7d7;
3232
}
33+
34+
h1 {
35+
color: #555;
36+
}
3337
}
3438

3539
@media screen and (max-width: 720px) {
@@ -58,7 +62,7 @@ body {
5862

5963
header {
6064
display: flex;
61-
justify-content: flex-end;
65+
justify-content: space-between;
6266
align-items: center;
6367
padding: 0 1em 0 1em;
6468
user-select: none;
@@ -74,14 +78,17 @@ main {
7478
}
7579

7680
#nav-title {
77-
margin-right: auto;
78-
margin-top: 16px;
79-
margin-bottom: 16px;
81+
/* to cut off very long titles */
82+
overflow: hidden;
83+
display: flex;
84+
flex-direction: row;
85+
align-items: baseline;
8086
}
8187

8288
header ul {
8389
margin: 0;
8490
padding: 0;
91+
display: flex;
8592
}
8693

8794
header li {
@@ -108,6 +115,24 @@ header .navigation:hover {
108115
height: 100%;
109116
}
110117

118+
h1 {
119+
display: inline-block;
120+
font-weight: normal;
121+
font-size: 1.125rem;
122+
123+
margin-top: 0px;
124+
margin-bottom: 0px;
125+
padding-left: 16px;
126+
padding-right: 16px;
127+
128+
max-width: 80vw;
129+
text-overflow: ellipsis;
130+
overflow: hidden;
131+
vertical-align: text-bottom;
132+
text-wrap-mode: nowrap;
133+
user-select: text;
134+
}
135+
111136
button {
112137
font-family: "JetBrains Mono", monospace;
113138
font-size: 1.125rem;
@@ -205,7 +230,7 @@ a, a:visited, a:hover {
205230
grid-row: 1/2;
206231
}
207232

208-
.expiration-list, .password, .paste-button {
233+
.expiration-list, .password, .paste-button, .title {
209234
margin-top: 2em;
210235
}
211236

templates/base.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<meta charset="utf-8">
55
<meta name="generator" content="wastebin {{ meta.version }}">
66
<meta name="viewport" content="width=device-width,initial-scale=1">
7-
<title>{{ meta.title }}</title>
7+
<title>{{ meta.title }}{% block title_content %}{% endblock %}</title>
88
<link rel="preload" as="style" href="dark.css">
99
<link rel="preload" as="style" href="light.css">
1010
<link rel="stylesheet" href="{{ base_path.join(meta.highlight.style.name) }}">
@@ -15,7 +15,7 @@
1515
{% block body_top %}{% endblock %}
1616
<div id="main-container">
1717
<header>
18-
<span id="nav-title"><a href="{{ base_path.path() }}" class="navigation">home</a></span>
18+
<span id="nav-title"><a href="{{ base_path.path() }}" class="navigation">home</a>{% block title %}{% endblock %}</span>
1919
<nav>
2020
<ul>
2121
{% block nav %}{% endblock %}

templates/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
<div class="password">
3636
<input type="password" name="password" id="password" placeholder="Password ...">
3737
</div>
38+
<div class="title">
39+
<input type="text" name="title" id="title" placeholder="Title ...">
40+
</div>
3841
<div class="paste-button">
3942
<button type="submit" title="Paste" class="button">Paste</button>
4043
</div>

0 commit comments

Comments
 (0)