Merge branch 'beta'

This commit is contained in:
tree🌴 2024-03-02 19:44:41 +01:00
commit 879ba0ad11
11 changed files with 287 additions and 102 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "w3pn-web", "name": "w3pn-web",
"type": "module", "type": "module",
"version": "1.2.2", "version": "1.2.3",
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"start": "astro dev", "start": "astro dev",

View file

@ -22,15 +22,20 @@ const status = eventStatus(item)
<div> <div>
<div class=""> <div class="">
<a href={`/event/${item.id}`} class="text-white text-lg hover:underline">{nameRenderer(item)}</a> <a href={`/event/${item.id}`} class="text-white text-lg hover:underline">{nameRenderer(item)}</a>
{item.issue &&
<a href={`https://github.com/web3privacy/events/issues/${item.issue}`} class="ml-0.5 text-white/30 hover:underline">#{item.issue}</a>
}
<div class="inline-block ml-2"> <div class="inline-block ml-2">
{item.type === "hackathon" && <span class="text-xs ml-2 text-black bg-white px-1 py-0.5">HACKATHON</span>} {item.type === "hackathon" && <span class="text-xs ml-2 text-black bg-white px-1 py-0.5">HACKATHON</span>}
{item.type === "summit" && <span class="text-xs ml-2 text-black bg-white px-1 py-0.5">SUMMIT</span>} {item.type === "summit" && <span class="text-xs ml-2 text-black bg-white px-1 py-0.5">SUMMIT</span>}
{item.type === "online-summit" && <span class="text-xs ml-2 text-black bg-white px-1 py-0.5">ONLINE</span>}
{item.type === "privacy-corner" && <span class="text-xs ml-2 text-black bg-gray-500 px-1 py-0.5">↴ PC</span>} {item.type === "privacy-corner" && <span class="text-xs ml-2 text-black bg-gray-500 px-1 py-0.5">↴ PC</span>}
{item.tags && item.tags.includes("sfe") && <span class="text-xs ml-2 text-black bg-gray-500 px-1 py-0.5" title="Sponsorship Free Edition (SFE)">SFE</span>} {item.tags && item.tags.includes("sfe") && <span class="text-xs ml-2 text-black bg-gray-500 px-1 py-0.5" title="Sponsorship Free Edition (SFE)">SFE</span>}
</div> </div>
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
{item.type !== 'online-summit' &&
<img src={`/flags/${item.country}.svg`} class="w-4" /> <img src={`/flags/${item.country}.svg`} class="w-4" />
<div> <div>
{item.city}, {item.country.toUpperCase()} {item.city}, {item.country.toUpperCase()}
@ -38,6 +43,11 @@ const status = eventStatus(item)
<span>&nbsp;- {ccRenderer(item)}</span> <span>&nbsp;- {ccRenderer(item)}</span>
} }
</div> </div>
}
{item.type === 'online-summit' &&
<img src="/flags/other/earth.svg" class="w-4" />
<div>Global</div>
}
</div> </div>
</div> </div>
<div class="py-1 grow text-right items-center flex gap-4"> <div class="py-1 grow text-right items-center flex gap-4">
@ -78,18 +88,21 @@ const status = eventStatus(item)
<div> <div>
<div>Date: <span class="text-white">{dateFormat(item.date)} {item.days ? ' - ' + dateFormat(dateEnd(item.date, item.days)) + ` (${item.days} days)` : ''}</span></div> <div>Date: <span class="text-white">{dateFormat(item.date)} {item.days ? ' - ' + dateFormat(dateEnd(item.date, item.days)) + ` (${item.days} days)` : ''}</span></div>
<div> <div>
Venue: {item.place && <span class="text-white" set:html={marked.parseInline(item.place)}></span> || "TBD"} {item.type !== 'online-summit' &&
<div>Venue: {item.place && <span class="text-white" set:html={marked.parseInline(item.place)}></span> || "TBD"}</div>
}
{item.place && item['place-address'] && {item.place && item['place-address'] &&
<span> @ </span> <span> @ </span>
<span class="text-white">{item['place-address']}</span> <span class="text-white">{item['place-address']}</span>
} }
</div> </div>
<div>Status: <span class:list={[status.color]} class="mr-1.5 text-xs">●</span> {status.title}</div> <div>Status: <span class:list={[status.color]} class="mr-1.5 text-xs">●</span> {status.title}</div>
<div>Lead: <span class="text-white">{item.lead || 'n/a'}</span></div> <!--div>Lead: <span class="text-white">{item.lead || 'n/a'}</span></div-->
<div class="mt-4 mb-2"> <div class="mt-4 mb-2 flex flex-wrap gap-4">
<a href={`/event/${item.id}`} class="button inverted"><button>More details</button></a>
{item.links?.rsvp && {item.links?.rsvp &&
<a href={item.links.rsvp} class="button inverted"><button>More details & RSVP</button></a> <a href={item.links.rsvp} class="button inverted"><button>RSVP</button></a>
} }
{item.links?.web && {item.links?.web &&
<a href={item.links.web} class="button inverted"><button>Website</button></a> <a href={item.links.web} class="button inverted"><button>Website</button></a>

View file

@ -0,0 +1,147 @@
---
import BaseLayout from '../layouts/base.astro';
import core from '../core.json';
import EventItem from '../components/EventItem.astro';
import { isFuture } from 'date-fns';
import { types, countryNames } from '../lib/events.js';
const { type: selectedType } = Astro.props;
const { country: selectedCountry } = Astro.props;
const typeObj = selectedType ? types.find(t => t.id === selectedType) : null
const countryObj = selectedCountry ? { name: countryNames[selectedCountry] } : null
let title = 'Events';
if (selectedType) {
title = typeObj.plural
}
if (selectedCountry) {
title = 'Events in ' + countryObj.name
}
let events = core.events;
if (selectedType) {
events = core.events.filter(ev => ev.type === selectedType)
}
if (selectedCountry) {
events = core.events.filter(ev => ev.country === selectedCountry)
}
function eventsFilter (year, future=true) {
return function (x) {
if (!x.date.match(new RegExp(`^${year}`))) {
return false
}
const isDate = x.date.match(/^\d{4}-\d{2}-\d{2}$/)
if (!isDate) {
return false
}
return future ? isFuture(new Date(x.date)) : !isFuture(new Date(x.date))
}
}
const pastYears = [...new Set(events.map(e => e.date.match(/^(\d{4})/)[1]))];
const upcoming = events.filter(x => x.date.match(/^2024/))
const past = {}
let pastTotal = 0
for (const year of pastYears.reverse()) {
past[year] = events.filter(eventsFilter(year, false)).reverse()
pastTotal += past[year].length
}
let places = [{ id: '', country: 'All countries', num: core.events.length }];
for (const ev of core.events) {
const found = places.find(p => p.country === ev.country)
if (found) {
found.num++;
continue;
}
if (!ev.country) {
continue;
}
places.push({
id: ev.country?.toLowerCase(),
country: ev.country,
num: 1,
})
}
places = places.sort((x, y) => x.num < y.num ? 1 : -1)
---
<BaseLayout title="Events" metaTitle={title} image="og_events">
<div class="middle-pane-medium mt-10">
<div class="mb-8 sm:mb-14">
<div class="flex flex-wrap gap-3 uppercase mb-4">
{types.map((type) => (
<a href={`/events/${type.id}`} class="px-2 py-0 border border-white/15 rounded-xl" class:list={[ (type.id === selectedType) || (!type.id && !selectedType)? 'text-white border-white/60' : 'hover:bg-white/20']}>{type.plural} ({type.id ? core.events.filter(obj => obj.type === type.id).length : core.events.length})</a>
))}
</div>
<div class="flex flex-wrap gap-2 uppercase text-sm">
{places.map((place) => (
<a href={place.id ? `/events/country/${place.id}` : `/events`} class="px-2 py-0.5 border border-white/15 rounded-xl flex gap-2" class:list={[ (place.id === selectedCountry) || (!place.id && !selectedCountry)? 'text-white border-white/60' : 'hover:bg-white/20']}>
{place.id &&
<img src={`/flags/${place.country}.svg`} class="w-4 inline-block" />
}
{countryNames[place.country] || place.country} ({place.num})
</a>
))}
</div>
</div>
<!--div class="mb-10">
<img src="/events-map.svg" class="w-full" />
</div-->
{upcoming.length > 0 &&
<div>
<h1 id="upcoming">Upcoming {title} ({upcoming.length})</h1>
<div class="mb-10">
{upcoming.map((event) => (
<EventItem item={event} />
))}
</div>
</div>
}
{pastTotal > 0 &&
<div>
<h1 id="past">Past {title} ({events.length-upcoming.length})</h1>
{pastYears.map((year) => (
past[year].length > 0 &&
<h2>{year} ({past[year].length})</h2>
<div class="mt-4 mb-10">
{past[year]?.map((event) => (
<EventItem item={event} />
))}
</div>
))}
</div>
}
<p>
<a href="https://github.com/web3privacy/data/tree/main/src/events" class="hover:underline">Source repository</a>
</p>
</div>
<script is:inline>
document.querySelectorAll('.event-header .header-base').forEach((el) => {
el.addEventListener('click', (ev) => {
if (ev.target.tagName === "BUTTON") {
return false;
}
if (ev.target.tagName === "A") {
return false;
}
const detail = el.parentElement.parentElement.querySelector('.detail')
document.querySelectorAll('.detail:not(.hidden)').forEach(e => (detail !== e ? e.classList.add('hidden') : null));
detail.classList.toggle('hidden');
});
});
</script>
</BaseLayout>

View file

@ -59,7 +59,7 @@
"received_events_url": "https://api.github.com/users/burningtree/received_events", "received_events_url": "https://api.github.com/users/burningtree/received_events",
"type": "User", "type": "User",
"site_admin": false, "site_admin": false,
"contributions": 765 "contributions": 766
}, },
{ {
"login": "EclecticSamurai", "login": "EclecticSamurai",

View file

@ -870,7 +870,10 @@
"name-extension": "Q2", "name-extension": "Q2",
"date": "2024-06-23", "date": "2024-06-23",
"lead": "Tree", "lead": "Tree",
"slots": 8 "slots": 8,
"links": {
"rsvp": "https://lu.ma/w3pn-os24q2"
}
}, },
{ {
"id": "m24bcn", "id": "m24bcn",

View file

@ -1,5 +1,30 @@
import { format, compareAsc, addDays, isFuture } from 'date-fns'; import { format, compareAsc, addDays, isFuture } from 'date-fns';
export const types = [
{ id: "", plural: 'All events'},
{ id: "meetup", name: 'Meetup', plural: 'Meetups' },
{ id: "summit", name: 'Summit', plural: 'Summits' },
{ id: "privacy-corner", name: 'Privacy Corner', plural: 'Privacy Corners' },
{ id: "online-summit", name: 'Online Summit', plural: 'Online Summits' },
]
export const countryNames = {
cz: 'Czechia',
it: 'Italy',
de: 'Germany',
es: 'Spain',
si: 'Slovenia',
dk: 'Denmark',
pl: 'Poland',
be: 'Belgium',
pt: 'Portugal',
ee: 'Estonia',
nl: 'Netherlands',
ro: 'Romania',
gr: 'Greece',
th: 'Thailand',
}
export function dateInfo (item) { export function dateInfo (item) {
const isDate = item.date.match(/^\d{4}-\d{2}-\d{2}$/) const isDate = item.date.match(/^\d{4}-\d{2}-\d{2}$/)
const future = isDate && !isFuture(new Date(item.date)); const future = isDate && !isFuture(new Date(item.date));
@ -45,6 +70,9 @@ export function nameRenderer (item, full = false) {
case 'privacy-corner': case 'privacy-corner':
return `Privacy Corner at `+ (item.coincidenceFull ? item.coincidenceFull : `${item.coincidence} ${date.year}`) return `Privacy Corner at `+ (item.coincidenceFull ? item.coincidenceFull : `${item.coincidence} ${date.year}`)
break; break;
case 'online-summit':
return "ONLINE Summit" + (item['name-extension'] ? ' ' + item['name-extension'] : '') + (full ? ` ${date.year}` : '');
break;
} }
} }

View file

@ -30,6 +30,7 @@ const ext = findExt(EventsExt, item)
<h1 id="upcoming">W3PN {nameRenderer(item, true)}</h1> <h1 id="upcoming">W3PN {nameRenderer(item, true)}</h1>
<div class="flex gap-2 mb-4 text-lg"> <div class="flex gap-2 mb-4 text-lg">
{item.type !== 'online-summit' &&
<img src={`/flags/${item.country}.svg`} class="w-4" /> <img src={`/flags/${item.country}.svg`} class="w-4" />
<div> <div>
{item.city}, {item.country.toUpperCase()} {item.city}, {item.country.toUpperCase()}
@ -37,12 +38,19 @@ const ext = findExt(EventsExt, item)
<span>&nbsp;- {ccRenderer(item)}</span> <span>&nbsp;- {ccRenderer(item)}</span>
} }
</div> </div>
}
{item.type === 'online-summit' &&
<img src="/flags/other/earth.svg" class="w-4" />
<div>Online</div>
}
</div> </div>
<div> <div>
<div>Date: <span class="text-white">{dateFormat(item.date)} {item.days ? ' - ' + dateFormat(dateEnd(item.date, item.days)) + ` (${item.days} days)` : ''}</span></div> <div>Date: <span class="text-white">{dateFormat(item.date)} {item.days ? ' - ' + dateFormat(dateEnd(item.date, item.days)) + ` (${item.days} days)` : ''}</span></div>
<div> <div>
Venue: {item.place && <span class="text-white" set:html={marked.parseInline(item.place)}></span> || "TBD"} {item.type !== 'online-summit' &&
<div>Venue: {item.place && <span class="text-white" set:html={marked.parseInline(item.place)}></span> || "TBD"}</div>
}
{item.place && item['place-address'] && {item.place && item['place-address'] &&
<span> @ </span> <span> @ </span>
<span class="text-white">{item['place-address']}</span> <span class="text-white">{item['place-address']}</span>

View file

@ -1,82 +1,7 @@
--- ---
import BaseLayout from '../layouts/base.astro'; import EventsPage from '../components/EventsPage.astro';
import core from '../core.json';
import EventItem from '../components/EventItem.astro';
import { isFuture } from 'date-fns';
const events = core.events;
function eventsFilter (year, future=true) {
return function (x) {
if (!x.date.match(new RegExp(`^${year}`))) {
return false
}
const isDate = x.date.match(/^\d{4}-\d{2}-\d{2}$/)
if (!isDate) {
return false
}
return future ? isFuture(new Date(x.date)) : !isFuture(new Date(x.date))
}
}
const currentYear = "2024";
const pastYears = [ 2023, 2024 ];
const upcoming = events.filter(x => x.date.match(/^2024/))
const past = {}
for (const year of pastYears.reverse()) {
past[year] = events.filter(eventsFilter(year, false)).reverse()
}
--- ---
<BaseLayout title="Events" image="og_events"> <EventsPage />
<div class="middle-pane-medium mt-10">
<!--div class="mb-10">
<img src="/events-map.svg" class="w-full" />
</div-->
<h1 id="upcoming">Upcoming ({upcoming.length})</h1>
<div class="mb-10">
{upcoming.map((event) => (
<EventItem item={event} />
))}
</div>
<h1 id="past">Past events ({events.length-upcoming.length})</h1>
{pastYears.map((year) => (
past[year].length > 0 &&
<h2>{year} ({past[year].length})</h2>
<div class="mt-4 mb-10">
{past[year]?.map((event) => (
<EventItem item={event} />
))}
</div>
))}
<p>
<a href="https://github.com/web3privacy/data/tree/main/src/events" class="hover:underline">Source repository</a>
</p>
</div>
<script is:inline>
document.querySelectorAll('.event-header .header-base').forEach((el) => {
el.addEventListener('click', (ev) => {
if (ev.target.tagName === "BUTTON") {
return false;
}
if (ev.target.tagName === "A") {
return false;
}
const detail = el.parentElement.parentElement.querySelector('.detail')
document.querySelectorAll('.detail:not(.hidden)').forEach(e => (detail !== e ? e.classList.add('hidden') : null));
detail.classList.toggle('hidden');
});
});
</script>
</BaseLayout>

View file

@ -0,0 +1,14 @@
---
import EventsPage from '../../components/EventsPage.astro';
import { types } from '../../lib/events.js';
const { type } = Astro.params;
export async function getStaticPaths() {
return types.filter(obj => obj.id).map(obj => ({ params: { type: obj.id }}));
}
---
<EventsPage {type} />

View file

@ -0,0 +1,24 @@
---
import EventsPage from '../../../components/EventsPage.astro';
import { types } from '../../../lib/events.js';
import core from '../../../core.json';
const { country } = Astro.params;
export async function getStaticPaths() {
const countries = []
for (const ev of core.events) {
if (!ev.country) {
continue
}
if (!countries.includes(ev.country)) {
countries.push(ev.country)
}
}
return countries.map(country => ({ params: { country }}));
}
---
<EventsPage {country} />

View file

@ -8,9 +8,11 @@ import talks from '../talks.json';
import explorer from '../explorer.json'; import explorer from '../explorer.json';
import dbRepo from '../db-repo.json'; import dbRepo from '../db-repo.json';
import { isPast, format } from 'date-fns'; import { isPast, format } from 'date-fns';
import EventItem from '../components/EventItem.astro';
const events = core.events; const events = core.events;
const upcomingEvents = []
let eventsPast = 0 let eventsPast = 0
let eventsUpcoming = 0 let eventsUpcoming = 0
for (const ev of events) { for (const ev of events) {
@ -19,12 +21,23 @@ for (const ev of events) {
future = false future = false
} }
if (future) { if (future) {
upcomingEvents.push(ev)
eventsUpcoming++ eventsUpcoming++
} else { } else {
eventsPast++ eventsPast++
} }
} }
const featuredEvents = []
for (const e of upcomingEvents) {
if (featuredEvents.length > 2) {
break;
}
if (['summit', 'meetup', 'online-summit'].includes(e.type) &&
e.links?.rsvp && !featuredEvents.find(ex => ex.type === e.type)) {
featuredEvents.push(e)
}
}
--- ---
@ -80,11 +93,21 @@ for (const ev of events) {
<div class="middle-pane-medium mt-10"> <div class="middle-pane-medium mt-10">
<!--div class="mt-10"> <!--div class="mt-10">
<h1>Our Projects</h1> <h1>Our Projects</h1>
</div> </div-->
<div class="mt-16"> <div class="mt-16">
<h1>Featured Events</h1> <h1>Featured Upcoming Events</h1>
</div--> </div>
<div class="mb-6">
{featuredEvents.map((event) => (
<EventItem item={event} />
))}
</div>
<div class="mt-6">
<a href="/events" class="button inverted"><button>Show all events</button></a>
</div>
<div class="mt-16"> <div class="mt-16">
<h1>Latest Articles</h1> <h1>Latest Articles</h1>