diff --git a/content/spenden/anthony-potdevin.json b/content/spenden/anthony-potdevin.json
new file mode 100644
index 00000000000..2d66fec5284
--- /dev/null
+++ b/content/spenden/anthony-potdevin.json
@@ -0,0 +1,8 @@
+{
+ "name": "Anthony Potdevin",
+ "github": "apotdevin",
+ "twitter": "tonyioi",
+ "spendenURL": "https://github.com/sponsors/apotdevin",
+ "avatar": "https://avatars3.githubusercontent.com/u/31413433?s=460&u=719e165488e0c8092bb234f7180140d53bcc382c&v=4",
+ "beschreibung": "Freier Entwickler aus München, der hauptsächlich an der Entwicklung des Lightning-Node-Managers [ThunderHub](https://github.com/apotdevin/thunderhub) arbeitet. Dabei handelt es sich um eine freie Open-Source Lösung für die Verwaltung der eigenen Lightning-Node auf einem dedizierten Gerät, wie z.B. dem RaspiBlitz."
+}
diff --git a/content/spenden/bitcoin-beach.json b/content/spenden/bitcoin-beach.json
new file mode 100644
index 00000000000..9dec9748250
--- /dev/null
+++ b/content/spenden/bitcoin-beach.json
@@ -0,0 +1,7 @@
+{
+ "name": "Bitcoin Beach",
+ "twitter": "Bitcoinbeach",
+ "spendenURL": "https://platform.engiven.com/give/8/widget/34",
+ "avatar": "/img/spenden/bitcoinbeach.png",
+ "beschreibung": "Erster [Ort / Gemeinschaft](https://www.bitcoinbeach.com/) in El Salvador die an der Pazifikküste eine Kreislaufwirtschaft auf Basis von Bitcoin errichtet haben. Ziel ist es die finanzielle Inklusion zu stärken und der Gemeinschaft wirtschaftlich zu entwickeln."
+}
diff --git a/content/spenden/netpositive-money.json b/content/spenden/netpositive-money.json
new file mode 100644
index 00000000000..64cb5041335
--- /dev/null
+++ b/content/spenden/netpositive-money.json
@@ -0,0 +1,7 @@
+{
+ "name": "netpositive.money",
+ "github": "netpositive-money",
+ "twitter": "netposmon",
+ "spendenURL": "https://netpositive.money/",
+ "beschreibung": "Initiative, um Aufmerksamkeit für den Stromverbrauch und die Emission von Treibhausgasen des Bitcoin-Netzwerks zu schaffen. Aus Basis von unterschiedlichen wissenschaftlichen Quellen wurde ein Kalkulator für den persönlichen ökologischen Fußabdruck durch das Halten von Bitcoin entwickelt und Empfehlungen für die Kompensation aufgezeigt."
+}
diff --git a/content/spenden/raspiblitz.json b/content/spenden/raspiblitz.json
new file mode 100644
index 00000000000..bab7d49bff2
--- /dev/null
+++ b/content/spenden/raspiblitz.json
@@ -0,0 +1,8 @@
+{
+ "name": "RaspiBlitz",
+ "github": "RaspiBlitz",
+ "twitter": "RaspiBlitz",
+ "spendenURL": "https://tallyco.in/s/r5lx23/",
+ "avatar": "/img/spenden/raspiblitz.jpg",
+ "beschreibung": "Open-Source Lösung einer Bitcoin Full-Node mit integrierter Lightning-Node auf Basis des Raspberry-Pi's. Wird überwiegend von deutschen Entwicklern weiterentwickelt und ist komplett als freie Software erhältlich, womit auch eine kommerzielle Nutzung möglich wird. In diesem Fall unterscheidet sich das RaspiBlitz Projekt von anderen Full-Node Projekten am Markt."
+}
diff --git a/content/spenden/rene-pickhardt.json b/content/spenden/rene-pickhardt.json
new file mode 100644
index 00000000000..ba3c8cf8092
--- /dev/null
+++ b/content/spenden/rene-pickhardt.json
@@ -0,0 +1,8 @@
+{
+ "name": "Rene Pickhardt",
+ "github": "renepickhardt",
+ "twitter": "renepickhardt",
+ "spendenURL": "https://donate.ln.rene-pickhardt.de/",
+ "avatar": "https://avatars.githubusercontent.com/u/1926816?s=460",
+ "beschreibung": "Deutscher Entwickler, dem der freie Zugriff auf Bildungsinhalte sehr wichtig ist und sich primär mit der Forschung und Entwicklung rund um das Lightning-Netzwerk beschäftigt. Aktuell wichtigstes Projekt ist ein neuer Ansatz für einen Routing-Algorithmus im Lightning-Netzwerk. Dieser Ansatz wird unter dem Namen Pickhardt-Payments geführt."
+}
diff --git a/helpers.js b/helpers.js
index 79f7c36682a..3e714a886e0 100644
--- a/helpers.js
+++ b/helpers.js
@@ -4,6 +4,7 @@ const { _tr: mdTransformer } = transformer(require('jstransformer-markdown-it'))
const config = {
typographer: true,
+ html: true
}
// monkey-patch render function to pass custom options
@@ -22,8 +23,16 @@ const slugify = str => str.toLowerCase()
.replace(/\s+/g, '-').replace(/[^\w\-]+/g, '')
.replace(/\-\-+/g, '-').replace(/^-+/, '').replace(/-+$/, '')
+const truncate = (str, wordCount) => {
+ const words = str.trim().split(/\s(?![^\[]*\])/g)
+ const head = words.splice(0, wordCount).join(' ')
+ const tail = words.join(' ')
+ return [head, tail]
+}
+
module.exports = {
markdown: mdTransformer.render,
replacements,
- slugify
+ slugify,
+ truncate
}
diff --git a/pug.config.js b/pug.config.js
index 4807af718ce..6f2758fc76c 100644
--- a/pug.config.js
+++ b/pug.config.js
@@ -1,5 +1,5 @@
// initialize markdown rendering
-const { markdown, slugify } = require('./helpers')
+const helpers = require('./helpers')
const IS_DEV = process.env.NODE_ENV === 'development'
const HOST = IS_DEV ? 'localhost:3000' : 'einundzwanzig.space'
@@ -25,10 +25,9 @@ module.exports = {
basedir: './src/includes',
random,
shuffle,
- slugify,
assetUrl,
assetPath,
formatDate,
linkTarget,
- markdown,
+ ...helpers
}
diff --git a/src/css/base/header.css b/src/css/base/header.css
index cbff9a21969..47a0f8bff8a 100644
--- a/src/css/base/header.css
+++ b/src/css/base/header.css
@@ -64,13 +64,15 @@
align-items: center;
justify-content: space-between;
line-height: 1;
- font-size: 4.5vw;
+ font-size: 3.5vw;
@media (--up_to_M) {
margin-bottom: var(--space-s);
+ font-size: 3.85vw;
}
@media (--M_to_L) {
margin-top: var(--space-m);
+ font-size: 3.5vw;
}
@media (--L_and_up) {
margin-top: var(--space-l);
@@ -100,7 +102,7 @@
position: relative;
top: 1px;
- @media (--up_to_S) {
+ @media (--up_to_M) {
visibility: hidden;
}
diff --git a/src/css/main.css b/src/css/main.css
index afc8fa4caa3..0f2adc6ef6d 100644
--- a/src/css/main.css
+++ b/src/css/main.css
@@ -11,4 +11,5 @@
@import 'sections/podcast.css';
@import 'sections/team.css';
@import 'sections/media.css';
+@import 'sections/spenden.css';
@import 'sections/soundboard.css';
diff --git a/src/css/sections/spenden.css b/src/css/sections/spenden.css
new file mode 100644
index 00000000000..1039605244b
--- /dev/null
+++ b/src/css/sections/spenden.css
@@ -0,0 +1,122 @@
+#spenden {
+ & h2 {
+ margin-bottom: var(--space-xl);
+ }
+}
+
+.spenden {
+ list-style: none;
+ display: grid;
+ grid-gap: var(--space-xl);
+ margin: 0;
+
+ @media (--up_to_L) {
+ grid-template-columns: 1fr;
+ }
+ @media (--L_and_up) {
+ grid-template-columns: 1fr 1fr;
+ }
+
+ & li {
+ margin: 0;
+ }
+}
+
+.spende {
+ height: 100%;
+ padding-top: var(--space-xl);
+
+ & .inner {
+ height: 100%;
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ background: var(--color-card-bg);
+ border-radius: var(--space-l);
+ padding: var(--space-xl);
+ }
+
+ & .avatar {
+ display: block;
+ height: 120px;
+ width: 120px;
+ border-radius: 50%;
+ border: 10px solid var(--color-card-bg);
+ margin: calc(var(--space-xxl) * -1) auto 0;
+ background-size: 100%;
+ background-repeat: no-repeat;
+ }
+
+ & .name {
+ padding: var(--space-s) 0 var(--space-m) 0;
+ text-align: center;
+ }
+
+ & .center {
+ flex: 1;
+ }
+
+ & .foot {
+ margin-top: var(--space-l);
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+
+ & .social {
+ & a {
+ position: absolute;
+ top: var(--space-l);
+ width: 32px;
+ height: 32px;
+ display: inline-block;
+ background-size: 100%;
+ background-repeat: no-repeat;
+ outline: 0;
+ color: var(--color-neutral-50);
+ & svg {
+ display: inline-block;
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ & a.twitter {
+ left: var(--space-l);
+ }
+
+ & a.mastodon {
+ left: var(--space-xxl);
+ }
+
+ & a.github {
+ right: var(--space-l);
+ }
+
+ & a.gitlab {
+ right: var(--space-xxl);
+ }
+ }
+
+ & .description {
+ & p {
+ text-align: left;
+ margin: 0;
+ }
+
+ & .more {
+ display: none;
+ }
+
+ &.expanded {
+ & .showMore {
+ display: none;
+ }
+
+ & .more {
+ display: unset;
+ }
+ }
+ }
+}
diff --git a/src/includes/mixins.pug b/src/includes/mixins.pug
index 9d4fe843cfc..4701b5562d8 100644
--- a/src/includes/mixins.pug
+++ b/src/includes/mixins.pug
@@ -23,6 +23,35 @@ mixin member(m)
if m.text
!=markdown(m.text)
+mixin spende(d, trunc = false)
+ .spende(id=d.id)&attributes(attributes)
+ .inner
+ img.avatar(src=(d.avatar || assetPath("/img/avatar.svg")) alt=d.name)
+ h3.name= d.name
+ .center
+ if d.beschreibung
+ .description
+ - const [head, tail] = trunc ? truncate(d.beschreibung, 21) : [d.beschreibung]
+ - const description = tail ? `${head} [… mehr] ${tail}` : head
+ != markdown(description)
+ if d.tags
+ .tags
+ each tag in d.tags
+ span.tag= tag
+ if d.twitter || d.github || d.gitlab
+ .social
+ if d.twitter
+ a.twitter(href=`https://twitter.com/${d.twitter}` target="_blank" rel="nofollow noopener"): +sprite('twitter', 'Twitter')
+ if d.mastodon
+ a.mastodon(href=d.mastodon target="_blank" rel="nofollow noopener"): +sprite('mastodon', 'Mastodon')
+ if d.gitlab
+ a.gitlab(href=`https://gitlab.com/${d.gitlab}` target="_blank" rel="nofollow noopener"): +sprite('gitlab', 'GitLab')
+ if d.github
+ a.github(href=`https://github.com/${d.github}` target="_blank" rel="nofollow noopener"): +sprite('github', 'GitHub')
+ .foot
+ a.button(href=d.spendenURL target="_blank" rel="nofollow noopener")
+ = `Spende an ${d.name.split(' ')[0]}`
+
mixin episodeItem(e)
article.episodeItem&attributes(attributes)
a.plain(href=`/podcast/${e.slug}/`)
diff --git a/src/includes/template.pug b/src/includes/template.pug
index 745e60773f7..2ddf109ef00 100644
--- a/src/includes/template.pug
+++ b/src/includes/template.pug
@@ -55,10 +55,11 @@ html(lang="en")
.nav
nav
a.navItem(href="/podcast/" class=(navCurrent === 'podcast' && 'current')) Podcast
+ a.navItem#navItemMeetups(href="/meetups/" class=(navCurrent === 'meetups' && 'current')) Meetups
+ a.navItem#navItemMeetups(href="/spenden/" class=(navCurrent === 'spenden' && 'current')) Spenden
+ a.navItem#navItemMedia(href="/media/" class=(navCurrent === 'media' && 'current')) Media
a.navItem(href="/soundboard/" class=(navCurrent === 'soundboard' && 'current')) Sounds
a.navItem(href="/team/" class=(navCurrent === 'team' && 'current')) Team
- a.navItem#navItemMeetups(href="/meetups/" class=(navCurrent === 'meetups' && 'current')) Meetups
- a.navItem#navItemMedia(href="/media/" class=(navCurrent === 'media' && 'current')) Media
//- a.navItem(href=site.meta.shopUrl target="_blank") Shop
button(type="button").theme
+sprite("theme")
diff --git a/src/meetups.pug b/src/meetups.pug
index 648eeea405b..c03722c6d4f 100644
--- a/src/meetups.pug
+++ b/src/meetups.pug
@@ -8,7 +8,6 @@ block main
#meetups.wrap
section
h1= title
-
p= description
:markdown-it(html linkify typographer)
diff --git a/src/spenden.pug b/src/spenden.pug
new file mode 100644
index 00000000000..8983e9c32dc
--- /dev/null
+++ b/src/spenden.pug
@@ -0,0 +1,19 @@
+extends /template.pug
+
+block vars
+ - const title = 'Spendenregister'
+ - const description = 'Diese spendenfinanzierten Projekte, Initiativen und Entwickler halten wir für unterstützenswert.'
+
+block main
+ #spenden.wrap
+ section
+ h1= title
+ p= description
+
+ :markdown-it(html linkify typographer)
+ Neue Vorschläge können gerne [per Pull Request](https://github.com/Einundzwanzig-Podcast/einundzwanzig.space/edit/master/content/spenden/) hinzugefügt werden.
+
+ h2= title
+ ul.spenden(data-shuffle)
+ each s in shuffle(spenden)
+ +spende(s)
diff --git a/static/img/avatar.svg b/static/img/avatar.svg
new file mode 100644
index 00000000000..cc4e25af472
--- /dev/null
+++ b/static/img/avatar.svg
@@ -0,0 +1 @@
+
diff --git a/static/img/spenden/bitcoinbeach.png b/static/img/spenden/bitcoinbeach.png
new file mode 100644
index 00000000000..43fbf95a2b3
Binary files /dev/null and b/static/img/spenden/bitcoinbeach.png differ
diff --git a/static/img/spenden/raspiblitz.jpg b/static/img/spenden/raspiblitz.jpg
new file mode 100644
index 00000000000..c5f198f5c91
Binary files /dev/null and b/static/img/spenden/raspiblitz.jpg differ
diff --git a/static/js/main.js b/static/js/main.js
index 92b8e8ef78d..c1a8308eaa6 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -84,4 +84,12 @@ document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll('[data-clipboard]').forEach(link => {
link.addEventListener('click', copyToClipboard)
})
+
+ // Show more
+ document.querySelectorAll('.showMore').forEach(link => {
+ link.addEventListener('click', e => {
+ e.preventDefault()
+ link.parentNode.parentNode.classList.add('expanded')
+ })
+ })
})
diff --git a/tasks/generate_pages.js b/tasks/generate_pages.js
index de2f6db28c2..dff45338dd0 100644
--- a/tasks/generate_pages.js
+++ b/tasks/generate_pages.js
@@ -5,6 +5,7 @@ const { dirname, resolve } = require('path')
const config = require('../pug.config')
const site = require('../generated/site-data.json')
const episodes = require('../generated/episodes.json')
+const spenden = require('../generated/spenden.json')
const team = require('../content/team.json')
const crew = require('../content/crew.json')
const meetups = require('../content/meetups.json')
@@ -26,6 +27,7 @@ renderPage('index', 'index', { navCurrent: 'index', currentEpisode: episodes[0]
renderPage('podcast', 'podcast', { navCurrent: 'podcast', episodes: [...episodes] })
renderPage('team', 'team', { navCurrent: 'team', team, crew })
renderPage('meetups', 'meetups', { navCurrent: 'meetups', meetups })
+renderPage('spenden', 'spenden', { navCurrent: 'spenden', spenden })
renderPage('media', 'media', { navCurrent: 'media' })
renderPage('soundboard', 'soundboard', { navCurrent: 'soundboard', soundboard })
diff --git a/tasks/generate_site_data.js b/tasks/generate_site_data.js
index 1f5e2e983cd..59d336b88ab 100644
--- a/tasks/generate_site_data.js
+++ b/tasks/generate_site_data.js
@@ -1,12 +1,12 @@
-const { writeFileSync } = require('fs')
-const { join, resolve } = require('path')
+const { readdirSync, writeFileSync } = require('fs')
+const { basename, join, resolve } = require('path')
const request = require('sync-request')
const meta = require('../content/meta.json')
const soundboard = require('../content/soundboard.json')
-const dir = resolve(__dirname, '..', 'generated')
-const dst = join(dir, 'site-data.json')
+const dir = (...path) => resolve(__dirname, '..', ...path)
+const writeJSON = (file, data) => writeFileSync(file, JSON.stringify(data, null, 2))
let recentBlocks = []
try {
@@ -18,11 +18,11 @@ try {
const block = recentBlocks.length && recentBlocks[0].height
const date = (new Date()).toJSON().split('T')[0]
-const data = { date, block, meta }
-writeFileSync(dst, JSON.stringify(data, null, 2))
+writeJSON(dir('generated', 'site-data.json'), { date, block, meta })
-const content = soundboard.map(group => {
+// Soundboard
+const sounds = soundboard.map(group => {
group.sounds = group.sounds.map(sound => {
sound.url = `https://einundzwanzig.space${sound.file}`
delete sound.file
@@ -31,5 +31,16 @@ const content = soundboard.map(group => {
return group
})
-const soundDst = resolve(__dirname, '..', 'dist', 'sounds.json')
-writeFileSync(soundDst, JSON.stringify(content, null, 2))
+writeJSON(dir('dist', 'sounds.json'), sounds)
+
+// Spenden
+const spendenDir = dir('content', 'spenden')
+const spenden = readdirSync(spendenDir).map(filename => {
+ const filePath = join(spendenDir, filename)
+ const spende = require(filePath)
+ spende.id = basename(filename, '.json')
+ return spende
+})
+
+writeJSON(dir('generated', 'spenden.json'), spenden)
+writeJSON(dir('dist', 'spenden.json'), spenden)