mirror of
https://github.com/web3privacy/web
synced 2024-10-15 18:26:27 +02:00
Merge branch 'beta'
This commit is contained in:
commit
879ba0ad11
11 changed files with 287 additions and 102 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "w3pn-web",
|
||||
"type": "module",
|
||||
"version": "1.2.2",
|
||||
"version": "1.2.3",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
|
|
|
@ -22,22 +22,32 @@ const status = eventStatus(item)
|
|||
<div>
|
||||
<div class="">
|
||||
<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">
|
||||
{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 === "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.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 class="flex gap-2">
|
||||
<img src={`/flags/${item.country}.svg`} class="w-4" />
|
||||
<div>
|
||||
{item.city}, {item.country.toUpperCase()}
|
||||
{item.coincidence &&
|
||||
<span> - {ccRenderer(item)}</span>
|
||||
}
|
||||
</div>
|
||||
{item.type !== 'online-summit' &&
|
||||
<img src={`/flags/${item.country}.svg`} class="w-4" />
|
||||
<div>
|
||||
{item.city}, {item.country.toUpperCase()}
|
||||
{item.coincidence &&
|
||||
<span> - {ccRenderer(item)}</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{item.type === 'online-summit' &&
|
||||
<img src="/flags/other/earth.svg" class="w-4" />
|
||||
<div>Global</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="py-1 grow text-right items-center flex gap-4">
|
||||
|
@ -78,18 +88,21 @@ const status = eventStatus(item)
|
|||
<div>
|
||||
<div>Date: <span class="text-white">{dateFormat(item.date)} {item.days ? ' - ' + dateFormat(dateEnd(item.date, item.days)) + ` (${item.days} days)` : ''}</span></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'] &&
|
||||
<span> @ </span>
|
||||
<span class="text-white">{item['place-address']}</span>
|
||||
}
|
||||
</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 &&
|
||||
<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 &&
|
||||
<a href={item.links.web} class="button inverted"><button>Website</button></a>
|
||||
|
|
147
src/components/EventsPage.astro
Normal file
147
src/components/EventsPage.astro
Normal 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>
|
|
@ -59,7 +59,7 @@
|
|||
"received_events_url": "https://api.github.com/users/burningtree/received_events",
|
||||
"type": "User",
|
||||
"site_admin": false,
|
||||
"contributions": 765
|
||||
"contributions": 766
|
||||
},
|
||||
{
|
||||
"login": "EclecticSamurai",
|
||||
|
|
|
@ -870,7 +870,10 @@
|
|||
"name-extension": "Q2",
|
||||
"date": "2024-06-23",
|
||||
"lead": "Tree",
|
||||
"slots": 8
|
||||
"slots": 8,
|
||||
"links": {
|
||||
"rsvp": "https://lu.ma/w3pn-os24q2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m24bcn",
|
||||
|
|
|
@ -1,5 +1,30 @@
|
|||
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) {
|
||||
const isDate = item.date.match(/^\d{4}-\d{2}-\d{2}$/)
|
||||
const future = isDate && !isFuture(new Date(item.date));
|
||||
|
@ -45,6 +70,9 @@ export function nameRenderer (item, full = false) {
|
|||
case 'privacy-corner':
|
||||
return `Privacy Corner at `+ (item.coincidenceFull ? item.coincidenceFull : `${item.coincidence} ${date.year}`)
|
||||
break;
|
||||
case 'online-summit':
|
||||
return "ONLINE Summit" + (item['name-extension'] ? ' ' + item['name-extension'] : '') + (full ? ` ${date.year}` : '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,19 +30,27 @@ const ext = findExt(EventsExt, item)
|
|||
<h1 id="upcoming">W3PN {nameRenderer(item, true)}</h1>
|
||||
|
||||
<div class="flex gap-2 mb-4 text-lg">
|
||||
<img src={`/flags/${item.country}.svg`} class="w-4" />
|
||||
<div>
|
||||
{item.city}, {item.country.toUpperCase()}
|
||||
{item.coincidence &&
|
||||
<span> - {ccRenderer(item)}</span>
|
||||
}
|
||||
</div>
|
||||
{item.type !== 'online-summit' &&
|
||||
<img src={`/flags/${item.country}.svg`} class="w-4" />
|
||||
<div>
|
||||
{item.city}, {item.country.toUpperCase()}
|
||||
{item.coincidence &&
|
||||
<span> - {ccRenderer(item)}</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{item.type === 'online-summit' &&
|
||||
<img src="/flags/other/earth.svg" class="w-4" />
|
||||
<div>Online</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>
|
||||
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'] &&
|
||||
<span> @ </span>
|
||||
<span class="text-white">{item['place-address']}</span>
|
||||
|
|
|
@ -1,82 +1,7 @@
|
|||
---
|
||||
|
||||
import BaseLayout from '../layouts/base.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()
|
||||
}
|
||||
import EventsPage from '../components/EventsPage.astro';
|
||||
|
||||
---
|
||||
|
||||
<BaseLayout title="Events" image="og_events">
|
||||
|
||||
<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>
|
||||
<EventsPage />
|
14
src/pages/events/[type].astro
Normal file
14
src/pages/events/[type].astro
Normal 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} />
|
24
src/pages/events/country/[country].astro
Normal file
24
src/pages/events/country/[country].astro
Normal 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} />
|
|
@ -8,9 +8,11 @@ import talks from '../talks.json';
|
|||
import explorer from '../explorer.json';
|
||||
import dbRepo from '../db-repo.json';
|
||||
import { isPast, format } from 'date-fns';
|
||||
import EventItem from '../components/EventItem.astro';
|
||||
|
||||
const events = core.events;
|
||||
|
||||
const upcomingEvents = []
|
||||
let eventsPast = 0
|
||||
let eventsUpcoming = 0
|
||||
for (const ev of events) {
|
||||
|
@ -19,12 +21,23 @@ for (const ev of events) {
|
|||
future = false
|
||||
}
|
||||
if (future) {
|
||||
upcomingEvents.push(ev)
|
||||
eventsUpcoming++
|
||||
} else {
|
||||
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="mt-10">
|
||||
<h1>Our Projects</h1>
|
||||
</div>
|
||||
</div-->
|
||||
|
||||
<div class="mt-16">
|
||||
<h1>Featured Events</h1>
|
||||
</div-->
|
||||
<h1>Featured Upcoming Events</h1>
|
||||
</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">
|
||||
<h1>Latest Articles</h1>
|
||||
|
|
Loading…
Reference in a new issue