A little app to serve my photography from my personal website

refactoring

+187 -164
+111 -102
Cargo.lock
··· 3 3 version = 4 4 4 5 5 [[package]] 6 - name = "addr2line" 7 - version = "0.25.1" 8 - source = "registry+https://github.com/rust-lang/crates.io-index" 9 - checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" 10 - dependencies = [ 11 - "gimli", 12 - ] 13 - 14 - [[package]] 15 - name = "adler2" 16 - version = "2.0.1" 17 - source = "registry+https://github.com/rust-lang/crates.io-index" 18 - checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 19 - 20 - [[package]] 21 6 name = "allocator-api2" 22 7 version = "0.2.21" 23 8 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 137 122 ] 138 123 139 124 [[package]] 140 - name = "backtrace" 141 - version = "0.3.76" 142 - source = "registry+https://github.com/rust-lang/crates.io-index" 143 - checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" 144 - dependencies = [ 145 - "addr2line", 146 - "cfg-if", 147 - "libc", 148 - "miniz_oxide", 149 - "object", 150 - "rustc-demangle", 151 - "windows-link", 152 - ] 153 - 154 - [[package]] 155 125 name = "base64" 156 126 version = "0.22.1" 157 127 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 201 171 202 172 [[package]] 203 173 name = "cc" 204 - version = "1.2.38" 174 + version = "1.2.41" 205 175 source = "registry+https://github.com/rust-lang/crates.io-index" 206 - checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" 176 + checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" 207 177 dependencies = [ 208 178 "find-msvc-tools", 209 179 "shlex", ··· 211 181 212 182 [[package]] 213 183 name = "cfg-if" 214 - version = "1.0.3" 184 + version = "1.0.4" 215 185 source = "registry+https://github.com/rust-lang/crates.io-index" 216 - checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 186 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 217 187 218 188 [[package]] 219 189 name = "chrono" ··· 383 353 384 354 [[package]] 385 355 name = "find-msvc-tools" 386 - version = "0.1.2" 356 + version = "0.1.4" 387 357 source = "registry+https://github.com/rust-lang/crates.io-index" 388 - checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" 358 + checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" 389 359 390 360 [[package]] 391 361 name = "flume" ··· 493 463 494 464 [[package]] 495 465 name = "generic-array" 496 - version = "0.14.7" 466 + version = "0.14.9" 497 467 source = "registry+https://github.com/rust-lang/crates.io-index" 498 - checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 468 + checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" 499 469 dependencies = [ 500 470 "typenum", 501 471 "version_check", ··· 511 481 "libc", 512 482 "wasi", 513 483 ] 514 - 515 - [[package]] 516 - name = "gimli" 517 - version = "0.32.3" 518 - source = "registry+https://github.com/rust-lang/crates.io-index" 519 - checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" 520 484 521 485 [[package]] 522 486 name = "hashbrown" ··· 805 769 806 770 [[package]] 807 771 name = "indexmap" 808 - version = "2.11.4" 772 + version = "2.12.0" 809 773 source = "registry+https://github.com/rust-lang/crates.io-index" 810 - checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" 774 + checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" 811 775 dependencies = [ 812 776 "equivalent", 813 777 "hashbrown 0.16.0", 814 - ] 815 - 816 - [[package]] 817 - name = "io-uring" 818 - version = "0.7.10" 819 - source = "registry+https://github.com/rust-lang/crates.io-index" 820 - checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" 821 - dependencies = [ 822 - "bitflags", 823 - "cfg-if", 824 - "libc", 825 778 ] 826 779 827 780 [[package]] ··· 851 804 852 805 [[package]] 853 806 name = "libc" 854 - version = "0.2.176" 807 + version = "0.2.177" 855 808 source = "registry+https://github.com/rust-lang/crates.io-index" 856 - checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" 809 + checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" 857 810 858 811 [[package]] 859 812 name = "libm" ··· 960 913 ] 961 914 962 915 [[package]] 963 - name = "miniz_oxide" 964 - version = "0.8.9" 965 - source = "registry+https://github.com/rust-lang/crates.io-index" 966 - checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 967 - dependencies = [ 968 - "adler2", 969 - ] 970 - 971 - [[package]] 972 916 name = "mio" 973 - version = "1.0.4" 917 + version = "1.1.0" 974 918 source = "registry+https://github.com/rust-lang/crates.io-index" 975 - checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 919 + checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" 976 920 dependencies = [ 977 921 "libc", 978 922 "wasi", 979 - "windows-sys 0.59.0", 923 + "windows-sys 0.61.2", 980 924 ] 981 925 982 926 [[package]] ··· 1027 971 ] 1028 972 1029 973 [[package]] 1030 - name = "object" 1031 - version = "0.37.3" 1032 - source = "registry+https://github.com/rust-lang/crates.io-index" 1033 - checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" 1034 - dependencies = [ 1035 - "memchr", 1036 - ] 1037 - 1038 - [[package]] 1039 974 name = "once_cell" 1040 975 version = "1.21.3" 1041 976 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1237 1172 ] 1238 1173 1239 1174 [[package]] 1240 - name = "rustc-demangle" 1241 - version = "0.1.26" 1242 - source = "registry+https://github.com/rust-lang/crates.io-index" 1243 - checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 1244 - 1245 - [[package]] 1246 1175 name = "rustversion" 1247 1176 version = "1.0.22" 1248 1177 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1400 1329 1401 1330 [[package]] 1402 1331 name = "socket2" 1403 - version = "0.6.0" 1332 + version = "0.6.1" 1404 1333 source = "registry+https://github.com/rust-lang/crates.io-index" 1405 - checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" 1334 + checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" 1406 1335 dependencies = [ 1407 1336 "libc", 1408 - "windows-sys 0.59.0", 1337 + "windows-sys 0.60.2", 1409 1338 ] 1410 1339 1411 1340 [[package]] ··· 1617 1546 1618 1547 [[package]] 1619 1548 name = "stable_deref_trait" 1620 - version = "1.2.0" 1549 + version = "1.2.1" 1621 1550 source = "registry+https://github.com/rust-lang/crates.io-index" 1622 - checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1551 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 1623 1552 1624 1553 [[package]] 1625 1554 name = "stringprep" ··· 1640 1569 1641 1570 [[package]] 1642 1571 name = "syn" 1643 - version = "2.0.106" 1572 + version = "2.0.107" 1644 1573 source = "registry+https://github.com/rust-lang/crates.io-index" 1645 - checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 1574 + checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" 1646 1575 dependencies = [ 1647 1576 "proc-macro2", 1648 1577 "quote", ··· 1713 1642 1714 1643 [[package]] 1715 1644 name = "tokio" 1716 - version = "1.47.1" 1645 + version = "1.48.0" 1717 1646 source = "registry+https://github.com/rust-lang/crates.io-index" 1718 - checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" 1647 + checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" 1719 1648 dependencies = [ 1720 - "backtrace", 1721 1649 "bytes", 1722 - "io-uring", 1723 1650 "libc", 1724 1651 "mio", 1725 1652 "pin-project-lite", 1726 - "slab", 1727 1653 "socket2", 1728 1654 "tokio-macros", 1729 - "windows-sys 0.59.0", 1655 + "windows-sys 0.61.2", 1730 1656 ] 1731 1657 1732 1658 [[package]] 1733 1659 name = "tokio-macros" 1734 - version = "2.5.0" 1660 + version = "2.6.0" 1735 1661 source = "registry+https://github.com/rust-lang/crates.io-index" 1736 - checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1662 + checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" 1737 1663 dependencies = [ 1738 1664 "proc-macro2", 1739 1665 "quote", ··· 2078 2004 ] 2079 2005 2080 2006 [[package]] 2007 + name = "windows-sys" 2008 + version = "0.60.2" 2009 + source = "registry+https://github.com/rust-lang/crates.io-index" 2010 + checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 2011 + dependencies = [ 2012 + "windows-targets 0.53.5", 2013 + ] 2014 + 2015 + [[package]] 2016 + name = "windows-sys" 2017 + version = "0.61.2" 2018 + source = "registry+https://github.com/rust-lang/crates.io-index" 2019 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 2020 + dependencies = [ 2021 + "windows-link", 2022 + ] 2023 + 2024 + [[package]] 2081 2025 name = "windows-targets" 2082 2026 version = "0.48.5" 2083 2027 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2101 2045 "windows_aarch64_gnullvm 0.52.6", 2102 2046 "windows_aarch64_msvc 0.52.6", 2103 2047 "windows_i686_gnu 0.52.6", 2104 - "windows_i686_gnullvm", 2048 + "windows_i686_gnullvm 0.52.6", 2105 2049 "windows_i686_msvc 0.52.6", 2106 2050 "windows_x86_64_gnu 0.52.6", 2107 2051 "windows_x86_64_gnullvm 0.52.6", ··· 2109 2053 ] 2110 2054 2111 2055 [[package]] 2056 + name = "windows-targets" 2057 + version = "0.53.5" 2058 + source = "registry+https://github.com/rust-lang/crates.io-index" 2059 + checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 2060 + dependencies = [ 2061 + "windows-link", 2062 + "windows_aarch64_gnullvm 0.53.1", 2063 + "windows_aarch64_msvc 0.53.1", 2064 + "windows_i686_gnu 0.53.1", 2065 + "windows_i686_gnullvm 0.53.1", 2066 + "windows_i686_msvc 0.53.1", 2067 + "windows_x86_64_gnu 0.53.1", 2068 + "windows_x86_64_gnullvm 0.53.1", 2069 + "windows_x86_64_msvc 0.53.1", 2070 + ] 2071 + 2072 + [[package]] 2112 2073 name = "windows_aarch64_gnullvm" 2113 2074 version = "0.48.5" 2114 2075 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2119 2080 version = "0.52.6" 2120 2081 source = "registry+https://github.com/rust-lang/crates.io-index" 2121 2082 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2083 + 2084 + [[package]] 2085 + name = "windows_aarch64_gnullvm" 2086 + version = "0.53.1" 2087 + source = "registry+https://github.com/rust-lang/crates.io-index" 2088 + checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" 2122 2089 2123 2090 [[package]] 2124 2091 name = "windows_aarch64_msvc" ··· 2133 2100 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2134 2101 2135 2102 [[package]] 2103 + name = "windows_aarch64_msvc" 2104 + version = "0.53.1" 2105 + source = "registry+https://github.com/rust-lang/crates.io-index" 2106 + checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" 2107 + 2108 + [[package]] 2136 2109 name = "windows_i686_gnu" 2137 2110 version = "0.48.5" 2138 2111 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2145 2118 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2146 2119 2147 2120 [[package]] 2121 + name = "windows_i686_gnu" 2122 + version = "0.53.1" 2123 + source = "registry+https://github.com/rust-lang/crates.io-index" 2124 + checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" 2125 + 2126 + [[package]] 2148 2127 name = "windows_i686_gnullvm" 2149 2128 version = "0.52.6" 2150 2129 source = "registry+https://github.com/rust-lang/crates.io-index" 2151 2130 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2131 + 2132 + [[package]] 2133 + name = "windows_i686_gnullvm" 2134 + version = "0.53.1" 2135 + source = "registry+https://github.com/rust-lang/crates.io-index" 2136 + checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" 2152 2137 2153 2138 [[package]] 2154 2139 name = "windows_i686_msvc" ··· 2163 2148 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2164 2149 2165 2150 [[package]] 2151 + name = "windows_i686_msvc" 2152 + version = "0.53.1" 2153 + source = "registry+https://github.com/rust-lang/crates.io-index" 2154 + checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" 2155 + 2156 + [[package]] 2166 2157 name = "windows_x86_64_gnu" 2167 2158 version = "0.48.5" 2168 2159 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2175 2166 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2176 2167 2177 2168 [[package]] 2169 + name = "windows_x86_64_gnu" 2170 + version = "0.53.1" 2171 + source = "registry+https://github.com/rust-lang/crates.io-index" 2172 + checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" 2173 + 2174 + [[package]] 2178 2175 name = "windows_x86_64_gnullvm" 2179 2176 version = "0.48.5" 2180 2177 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2187 2184 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2188 2185 2189 2186 [[package]] 2187 + name = "windows_x86_64_gnullvm" 2188 + version = "0.53.1" 2189 + source = "registry+https://github.com/rust-lang/crates.io-index" 2190 + checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" 2191 + 2192 + [[package]] 2190 2193 name = "windows_x86_64_msvc" 2191 2194 version = "0.48.5" 2192 2195 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2197 2200 version = "0.52.6" 2198 2201 source = "registry+https://github.com/rust-lang/crates.io-index" 2199 2202 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2203 + 2204 + [[package]] 2205 + name = "windows_x86_64_msvc" 2206 + version = "0.53.1" 2207 + source = "registry+https://github.com/rust-lang/crates.io-index" 2208 + checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" 2200 2209 2201 2210 [[package]] 2202 2211 name = "writeable"
+47 -58
src/routes/photos.rs src/routes/photos/index.rs
··· 1 1 use std::{collections::HashSet, sync::Arc}; 2 2 3 - use axum::{ 4 - extract::{Path, State}, 5 - response::Html, 6 - }; 3 + use axum::{extract::State, response::Html}; 7 4 use axum_extra::extract::Query; 8 5 use minijinja::context; 9 6 use serde::{self, Deserialize, Serialize}; ··· 24 21 25 22 #[derive(Serialize)] 26 23 struct SelectedTag { 27 - tag: String, 24 + name: String, 28 25 action: String, 29 26 } 30 27 ··· 105 102 Ok(Html(rendered)) 106 103 } 107 104 108 - pub async fn show( 109 - Path(id): Path<String>, 110 - State(state): State<Arc<AppState>>, 111 - ) -> Result<Html<String>, AppError> { 112 - let photo = db::get_photo(&state.pool, id).await?; 113 - let template = state.template_env.get_template("photos/show")?; 114 - let rendered = template.render(context! { 115 - photo => photo 116 - })?; 117 - 118 - Ok(Html(rendered)) 119 - } 120 - 121 - fn get_pagination(query: &IndexParams, num_pages: u32) -> anyhow::Result<Pagination> { 122 - let page = query.page.unwrap_or(1); 123 - let prev_query = if page > 1 { 124 - let prev_page = if page == 1 { None } else { Some(page - 1) }; 125 - Some(serde_html_form::to_string(IndexParams { 126 - page: prev_page, 127 - tags: query.tags.clone(), 128 - ..*query 129 - })?) 130 - } else { 131 - None 132 - }; 133 - 134 - let next_query = if page < num_pages { 135 - let next_page = if page < num_pages { 136 - Some(page + 1) 137 - } else { 138 - None 139 - }; 140 - Some(serde_html_form::to_string(IndexParams { 141 - page: next_page, 142 - tags: query.tags.clone(), 143 - ..*query 144 - })?) 145 - } else { 146 - None 147 - }; 148 - 149 - Ok(Pagination { 150 - page, 151 - num_pages, 152 - prev_query, 153 - next_query, 154 - }) 155 - } 156 - 157 105 fn process_tags( 158 106 all_tags: &[Tag], 159 107 query: &IndexParams, ··· 162 110 let tags = tag_difference(all_tags, &selected); 163 111 let selected_tags: Vec<SelectedTag> = selected 164 112 .iter() 165 - .map(|tag| { 166 - let tags_removed = selected.iter().filter(|i| *i != tag).cloned().collect(); 113 + .enumerate() 114 + .map(|(i, tag)| { 115 + let tags_removed = selected 116 + .iter() 117 + .enumerate() 118 + .filter_map(|(j, s)| if i == j { None } else { Some(s.clone()) }) 119 + .collect(); 167 120 let action = serde_html_form::to_string(IndexParams { 168 121 tags: Some(tags_removed), 169 122 ..*query 170 123 })?; 171 124 172 125 Ok(SelectedTag { 173 - tag: tag.clone(), 126 + name: tag.clone(), 174 127 action, 175 128 }) 176 129 }) ··· 197 150 } 198 151 199 152 fn tag_difference(tags: &[Tag], selected: &[String]) -> Vec<Tag> { 200 - let selected: HashSet<String> = HashSet::from_iter(selected.iter().cloned()); 153 + let selected: HashSet<&String> = HashSet::from_iter(selected.iter()); 201 154 202 155 tags.iter() 203 156 .filter(|tag| !selected.contains(&tag.name)) 204 157 .cloned() 205 158 .collect() 206 159 } 160 + 161 + fn get_pagination(query: &IndexParams, num_pages: u32) -> anyhow::Result<Pagination> { 162 + let page = query.page.unwrap_or(1); 163 + let prev_query = if page > 1 { 164 + let prev_page = if page == 1 { None } else { Some(page - 1) }; 165 + Some(serde_html_form::to_string(IndexParams { 166 + page: prev_page, 167 + tags: query.tags.clone(), 168 + ..*query 169 + })?) 170 + } else { 171 + None 172 + }; 173 + 174 + let next_query = if page < num_pages { 175 + let next_page = if page < num_pages { 176 + Some(page + 1) 177 + } else { 178 + None 179 + }; 180 + Some(serde_html_form::to_string(IndexParams { 181 + page: next_page, 182 + tags: query.tags.clone(), 183 + ..*query 184 + })?) 185 + } else { 186 + None 187 + }; 188 + 189 + Ok(Pagination { 190 + page, 191 + num_pages, 192 + prev_query, 193 + next_query, 194 + }) 195 + }
+5
src/routes/photos/mod.rs
··· 1 + mod index; 2 + mod show; 3 + 4 + pub use index::index; 5 + pub use show::show;
+20
src/routes/photos/show.rs
··· 1 + use crate::{AppError, AppState, db}; 2 + use axum::{ 3 + extract::{Path, State}, 4 + response::Html, 5 + }; 6 + use minijinja::context; 7 + use std::sync::Arc; 8 + 9 + pub async fn show( 10 + Path(id): Path<String>, 11 + State(state): State<Arc<AppState>>, 12 + ) -> Result<Html<String>, AppError> { 13 + let photo = db::get_photo(&state.pool, id).await?; 14 + let template = state.template_env.get_template("photos/show")?; 15 + let rendered = template.render(context! { 16 + photo => photo 17 + })?; 18 + 19 + Ok(Html(rendered)) 20 + }
+4 -4
templates/photos/index.jinja
··· 5 5 {{ dir }} 6 6 <ul> 7 7 {% for tag in current_tags %} 8 - <li>{{ tag.tag }} <a href="/?{{ tag.action }}">🅇</a></li> 8 + <li>{{ tag.name }} <a href="/?{{ tag.action }}">🅇</a></li> 9 9 {% endfor %} 10 - {% for tag in tags %} 10 + {% for selectable in tags %} 11 11 <li> 12 - <a href="?{{ tag.action }}"> 13 - {{ tag.tag.name }} ({{ tag.tag.count }}) 12 + <a href="?{{ selectable.action }}"> 13 + {{ selectable.tag.name }} ({{ selectable.tag.count }}) 14 14 </a> 15 15 </li> 16 16 {% endfor %}