Merge branch 'beta'

This commit is contained in:
tree🌴 2024-02-24 03:45:09 +01:00
commit 13c57ae8ed
12 changed files with 353 additions and 151 deletions

View file

@ -3,8 +3,8 @@ name: Deploy to IPFS
on:
# Trigger the workflow every time you push to the `main` branch
# Using a different branch name? Replace `main` with your branchs name
push:
branches: [ beta ]
#push:
# branches: [ beta ]
# Allow this job to clone the repo and create a page deployment
permissions:

View file

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

View file

@ -4,6 +4,30 @@ import * as config from '../config.yaml';
import core from '../core.json';
import contributors from '../contributors.json';
function findPerson(src) {
const p = core.people.find(p => src.refs?.twitter ? p.refs?.twitter === src.refs.twitter : (src.refs?.bsky ? p.refs.bsky === src.refs.bsky : {}))
if (p) {
p.ct = src
}
return p
}
function personLink(person) {
return person.refs?.twitter ? `https://twitter.com/${person.refs.twitter}` : (person.refs?.bsky ? `https://bsky.app/profile/${person.refs.bsky}` : '#')
}
function coreTeamGithubLink(person) {
return `https://github.com/${person.ct.refs.github}`
}
function filterCoreTeam(person) {
if (person.login) {
return !(core['core-team'].find(ctm => ctm.refs.github.toLowerCase() === person.login.toLowerCase()))
}
const res = core['core-team'].find(ctm => ctm.refs.twitter ? ctm.refs.twitter?.toLowerCase() === person.refs?.twitter?.toLowerCase() : ctm.refs?.bsky?.toLowerCase() === person.refs?.bsky?.toLowerCase())
return !res
}
---
<div class="mt-20">
@ -34,13 +58,41 @@ import contributors from '../contributors.json';
<h1><a href="https://docs.web3privacy.info/get-involved">Join the Community</a></h1>
<div>{config.landing.community}</div>
<div class="flex gap-4 flex-wrap my-14 items-center">
{contributors.items.map((contrib) => (
<div><a href={contrib.html_url} target="_blank" title={contrib.login}><img src={contrib.avatar_url} class="w-16 rounded-full aspect-square"></a></div>
<!--h2 class="my-6">Core Team</h2>
<div class="flex gap-4 flex-wrap mb-14 items-center">
{core['core-team'].map(findPerson).map((person) => (
<div>
<a href={coreTeamGithubLink(person)}>
<img src={person.imageUrl} title={person.name} class="w-14 rounded-full aspect-square" />
</a>
</div>
))}
</div-->
<h2 class="my-6">Speakers</h2>
<div class="flex gap-4 flex-wrap items-center">
{core.people.filter(filterCoreTeam).filter(p => p.imageUrl).map((person) => (
<div>
<a href={personLink(person)}>
<img src={person.imageUrl} title={person.name} class="w-14 rounded-full aspect-square" />
</a>
</div>
))}
</div>
<div class="flex gap-4 lg:gap-10 pt-4 flex-wrap">
<div class="flex gap-4 lg:gap-6 pt-4 flex-wrap mt-4 mb-14">
<a href={core.links.cfp} class="button inverted"><button>Submit your proposal (CfP)</button></a>
</div>
<h2 class="my-6">Git Contributors</h2>
<div class="flex gap-4 flex-wrap mb-4 items-center">
{contributors.items.filter(filterCoreTeam).map((contrib) => (
<div><a href={contrib.html_url} target="_blank" title={contrib.login}><img src={contrib.avatar_url} class="w-14 rounded-full aspect-square"></a></div>
))}
</div>
<div class="flex gap-4 lg:gap-6 pt-4 flex-wrap">
<a href="https://docs.web3privacy.info/get-involved" class="button inverted"><button>Get involved</button></a>
<a href="/leaderboard" class="button inverted"><button>Leaderboard</button></a>
<a href="https://docs.web3privacy.info/donate/" class="button inverted"><button>Donate</button></a>

View file

@ -1,104 +1,13 @@
---
const { item } = Astro.props;
import { format, compareAsc, addDays, isFuture } from 'date-fns';
import { marked } from 'marked';
import EventsExt from '../events-ext.json';
import core from "../core.json";
import { imageMetadata } from 'astro/assets/utils';
function findExt () {
let slug = null
if (item.links?.web && item.links.web.match(/^https:\/\/lu.ma\//)) {
slug = item.links.web.match(/^https:\/\/lu.ma\/(.+)$/)[1]
}
if (item.links?.rsvp && item.links.rsvp.match(/^https:\/\/lu.ma\//)) {
slug = item.links.rsvp.match(/^https:\/\/lu.ma\/(.+)$/)[1]
}
if (!slug) {
return null
}
return EventsExt.find(ex => ex.url === slug)
}
function getSpeaker (id) {
return core.people.find(p => p.id === id)
}
const ext = findExt()
const isDate = item.date.match(/^\d{4}-\d{2}-\d{2}$/)
const future = isDate && !isFuture(new Date(item.date));
const dateMatch = item.date.match(/^(\d{4})/)
const year = dateMatch ? dateMatch[1] : null
function dateFormat (str) {
if (str.match(/^\d{4}-\d{2}-\d{2}$/)) {
return format(new Date(str), 'MMM d, yyyy')
}
const qm = str.match(/^(\d{4})\/(\w+)$/)
if (qm) {
return `${qm[2]}, ${qm[1]}`
}
return str
}
function nameRenderer (item) {
let num;
let ccm = item.coincidence?.match(/\[(\w+)\]/)
let cc = ccm && ccm[1] ? ccm[1] : (item.coincidence ? item.coincidence : null)
switch (item.type) {
case 'summit':
num = item.id.match(/^w3ps(\d+)$/)[1]
return `W3PN Summmit #${num} ${item.city}`// + (cc ? ` @ ${cc}` : '')
break;
case 'meetup':
num = item.id.match(/(\d+)$/)
return `W3PN Meetup ${item.city} #${num ? num[1] : 'TBD'}`// + (cc ? ` @ ${cc}` : '')
break;
case 'hackathon':
num = item.id.match(/^w3ph(\d+)$/)[1]
return `W3PN Hackathon #${num} ${item.city}`// + (cc ? ` @ ${cc}` : '')
break;
case 'privacy-corner':
return `Privacy Corner at `+ (item.coincidenceFull ? item.coincidenceFull : `${item.coincidence} ${year}`)
break;
}
}
function ccRenderer (item) {
let ccm = item.coincidence?.match(/\[(\w+)\]/)
let cc = ccm && ccm[1] ? ccm[1] : (item.coincidence ? item.coincidence : null)
return cc
}
function dateEnd(str, days) {
return format(addDays(new Date(str), days-1), 'yyyy-MM-dd');
}
const statuses = {
preregistration: {
title: 'Pre-registration',
color: 'text-orange-500',
},
unconfirmed: {
title: 'Planned',
color: ''
},
confirmed: {
title: 'Confirmed',
color: 'text-green-500',
},
past: {
title: 'Already happened',
color: 'text-green-800',
}
}
const status = item.confirmed
? (future ? statuses.past : statuses.confirmed)
: (item.links?.rsvp ? statuses.preregistration : statuses.unconfirmed);
import { dateFormat, dateInfo, dateEnd, nameRenderer, ccRenderer, eventStatus, getSpeaker, findExt } from '../lib/events.js';
import SpeakerList from './SpeakerList.astro';
const ext = findExt(EventsExt, item)
const status = eventStatus(item)
---
<div class="w3pn-event-item">
@ -112,7 +21,7 @@ const status = item.confirmed
</div>
<div>
<div class="">
<span class="text-white text-lg">{nameRenderer(item)}</span>
<a href={`/event/${item.id}`} class="text-white text-lg hover:underline">{nameRenderer(item)}</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>}
@ -135,7 +44,7 @@ const status = item.confirmed
<div class="grow"></div>
{item.speakers &&
<div class="flex -space-x-3">
{item.speakers.map(spId => getSpeaker(spId)).slice(0,7).map((speaker) => (
{item.speakers.map(spId => getSpeaker(core, spId)).slice(0,7).map((speaker) => (
<div><img src={speaker.imageUrl} class="w-8 h-8 aspect-square object-fit rounded-full border-gray-800 border-2" /></div>
))}
{item.speakers.length > 7 &&
@ -169,7 +78,7 @@ const status = item.confirmed
<div>
<div>Date: <span class="text-white">{dateFormat(item.date)} {item.days ? ' - ' + dateFormat(dateEnd(item.date, item.days)) + ` (${item.days} days)` : ''}</span></div>
<div>
Place: {item.place && <span class="text-white" set:html={marked.parseInline(item.place)}></span> || "TBD"}
Venue: {item.place && <span class="text-white" set:html={marked.parseInline(item.place)}></span> || "TBD"}
{item.place && item['place-address'] &&
<span> @ </span>
<span class="text-white">{item['place-address']}</span>
@ -187,27 +96,7 @@ const status = item.confirmed
}
</div>
{item.speakers &&
<div class="mt-6 mb-6">
<h2>Speakers ({item.speakers.length})</h2>
<div class="grid grid-cols-1 gap-6 mt-4">
{item.speakers.map(spId => getSpeaker(spId)).map((speaker) => (
<div class="flex gap-4">
<div><img class="w-14 h-14 aspect-square rounded-full" src={speaker.imageUrl} /></div>
<div>
<div>
<span class="text-white">{speaker.name}</span>
{speaker.refs?.twitter &&
<span class="ml-2">(<a href={"https://twitter.com/"+speaker.refs.twitter}>@{speaker.refs.twitter}</a>)</span>
}
</div>
<div set:html={marked.parseInline(speaker.caption)} class="text-sm"></div>
</div>
</div>
))}
</div>
</div>
}
<SpeakerList {item} />
</div>
</div>
</div>

View file

@ -0,0 +1,44 @@
---
import { marked, } from 'marked';
const { item, thumbSize } = Astro.props;
import { getSpeaker } from '../lib/events.js';
import core from "../core.json";
---
{item.speakers &&
<div class="my-6">
<h2>Speakers ({item.speakers.length})</h2>
<div class="w3pn-speaker-list grid grid-cols-1 gap-6 mt-4">
{item.speakers.map(spId => getSpeaker(core, spId)).map((speaker) => (
<div class="flex gap-4">
<div><img class={`${thumbSize === 'big' ? 'w-16 h-16' : 'w-14 h-14'} aspect-square rounded-full object-contain`} src={speaker.imageUrl} /></div>
<div>
<div>
<span class="text-white">{speaker.name}</span>
{speaker.refs?.twitter &&
<span class="ml-2">(<a href={"https://twitter.com/"+speaker.refs.twitter}>@{speaker.refs.twitter}</a>)</span>
}
</div>
<div set:html={marked.parseInline(speaker.caption)} class="text-sm"></div>
</div>
</div>
))}
</div>
<div class="mt-8">
<a href={core.links.cfp} class="button inverted"><button>Submit proposal (CfP)</button></a>
</div>
</div>
}
{!item.speakers &&
<div class="my-6">
<h2>Speakers (0)</h2>
<div class="mt-4">Stay tuned. Speakers coming :-)</div>
<div class="mt-6">
<a href={core.links.cfp} class="button inverted"><button>Submit proposal (CfP)</button></a>
</div>
</div>
}

View file

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

View file

@ -15,7 +15,8 @@
"forum": "https://forum.web3privacy.info",
"explorer": "https://explorer.web3privacy.info",
"news": "https://news.web3privacy.info",
"telegram": "https://t.me/web3privacynow"
"telegram": "https://t.me/web3privacynow",
"cfp": "https://cfp.web3privacy.info"
},
"core-team": [
{
@ -539,7 +540,7 @@
],
"events": [
{
"id": "w3ps1",
"id": "s23prg",
"type": "summit",
"date": "2023-06-05",
"city": "Prague",
@ -581,7 +582,7 @@
]
},
{
"id": "w3ps2",
"id": "s23rom",
"type": "summit",
"date": "2023-10-05",
"city": "Rome",
@ -616,7 +617,7 @@
]
},
{
"id": "w3pm-prg1",
"id": "m23prg",
"type": "meetup",
"date": "2023-11-14",
"city": "Prague",
@ -638,7 +639,8 @@
]
},
{
"id": "w3pm-ath1",
"id": "m24ath",
"issue": 22,
"type": "meetup",
"date": "2024/Mar",
"city": "Athens",
@ -653,7 +655,8 @@
]
},
{
"id": "w3pm-buc1",
"id": "m24buc",
"issue": 8,
"type": "meetup",
"date": "2024-03-28",
"city": "Bucharest",
@ -675,7 +678,8 @@
]
},
{
"id": "w3pm-ams1",
"id": "m24ams",
"issue": 9,
"type": "meetup",
"date": "2024-04-11",
"city": "Amsterdam",
@ -698,7 +702,8 @@
]
},
{
"id": "w3pm-tal1",
"id": "m24tll",
"issue": 10,
"type": "meetup",
"date": "2024-04-18",
"city": "Tallinn",
@ -712,7 +717,8 @@
}
},
{
"id": "w3pm-por1",
"id": "m24opo",
"issue": 21,
"type": "meetup",
"date": "2024/May",
"city": "Porto",
@ -725,7 +731,8 @@
]
},
{
"id": "w3pm-ber1",
"id": "m24ber",
"issue": 6,
"type": "meetup",
"tags": [
"sfe"
@ -750,7 +757,8 @@
}
},
{
"id": "w3ps3",
"id": "s24prg",
"issue": 11,
"type": "summit",
"date": "2024-05-30",
"city": "Prague",
@ -773,7 +781,8 @@
]
},
{
"id": "w3ph1",
"id": "h24ble",
"issue": 7,
"type": "hackathon",
"date": "2024-06-19",
"days": 7,
@ -802,7 +811,8 @@
]
},
{
"id": "w3pm-lju1",
"id": "m24lju",
"issue": 12,
"type": "meetup",
"date": "2024-06-21",
"city": "Ljubljana",
@ -823,7 +833,8 @@
]
},
{
"id": "w3pm-bcn1",
"id": "m24bcn",
"issue": 20,
"type": "meetup",
"date": "2024/Jul",
"city": "Barcelona",
@ -833,7 +844,8 @@
"optional": true
},
{
"id": "w3pm-bru1",
"id": "m24bru",
"issue": 16,
"type": "meetup",
"date": "2024-07-12",
"city": "Brussels",
@ -852,7 +864,8 @@
]
},
{
"id": "w3pm-waw1",
"id": "m24waw",
"issue": 19,
"type": "meetup",
"date": "2024/Sep",
"city": "Warsaw",
@ -863,7 +876,8 @@
"optional": true
},
{
"id": "w3pm-cph1",
"id": "m24cph",
"issue": 18,
"type": "meetup",
"date": "2024/Sep",
"city": "Copenhagen",
@ -877,7 +891,8 @@
]
},
{
"id": "w3pm-rom2",
"id": "m24rom",
"issue": 13,
"type": "meetup",
"date": "2024-10-04",
"city": "Rome",
@ -897,7 +912,8 @@
]
},
{
"id": "pc-rome-2024",
"id": "c24rom",
"issue": 23,
"type": "privacy-corner",
"date": "2024-10-04",
"days": 3,
@ -908,7 +924,8 @@
"lead": "Tree"
},
{
"id": "w3ps4",
"id": "s24brn",
"issue": 14,
"type": "summit",
"date": "2024-10-24",
"city": "Brno",
@ -931,7 +948,8 @@
]
},
{
"id": "pc-brno-2024",
"id": "c24brn",
"issue": 24,
"type": "privacy-corner",
"date": "2024-10-25",
"days": 3,
@ -942,7 +960,8 @@
"lead": "Tree"
},
{
"id": "w3pm-dc1",
"id": "m24dc",
"issue": 15,
"type": "meetup",
"date": "2024-11-11",
"city": "Bangkok",

View file

@ -5,7 +5,7 @@ import * as config from '../config.yaml';
import * as pkg from '../../package.json';
import core from '../core.json';
import '../styles/base.css';
const {banner, title, description} = Astro.props;
const {banner, title, metaTitle, description} = Astro.props;
import cfonts from 'cfonts';
@ -21,7 +21,7 @@ function genHeading(str) {
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<meta name="description" content={description} />
<title>{title ? title + ' | ' + config.title : config.title}</title>
<title>{(metaTitle || title) ? ((metaTitle || title) + ' | ' + config.title) : config.title}</title>
<link
rel="preload"

99
src/lib/events.js Normal file
View file

@ -0,0 +1,99 @@
import { format, compareAsc, addDays, isFuture } from 'date-fns';
export function dateInfo (item) {
const isDate = item.date.match(/^\d{4}-\d{2}-\d{2}$/)
const future = isDate && !isFuture(new Date(item.date));
const dateMatch = item.date.match(/^(\d{4})/)
const year = dateMatch ? dateMatch[1] : null
return { isDate, isFuture: future, year }
}
export function dateFormat (str) {
if (str.match(/^\d{4}-\d{2}-\d{2}$/)) {
return format(new Date(str), 'MMM d, yyyy')
}
const qm = str.match(/^(\d{4})\/(\w+)$/)
if (qm) {
return `${qm[2]}, ${qm[1]}`
}
return str
}
export function dateEnd(str, days) {
return format(addDays(new Date(str), days-1), 'yyyy-MM-dd');
}
export function nameRenderer (item, full = false) {
let num;
let ccm = item.coincidence?.match(/\[(\w+)\]/)
let cc = ccm && ccm[1] ? ccm[1] : (item.coincidence ? item.coincidence : null)
const date = dateInfo(item)
switch (item.type) {
case 'summit':
//num = item.id.match(/^w3ps(\d+)$/)[1]
return `Summmit ${item.city}` + (full ? ` ${date.year}`: '')// + (cc ? ` @ ${cc}` : '')
break;
case 'meetup':
//num = item.id.match(/(\d+)$/)
return `Meetup ${item.city}` + (full ? ` ${date.year}`: '')// + (cc ? ` @ ${cc}` : '')
break;
case 'hackathon':
//num = item.id.match(/^w3ph(\d+)$/)[1]
return `Hackathon ${item.city}` + (full ? ` ${date.year}`: '')// + (cc ? ` @ ${cc}` : '')
break;
case 'privacy-corner':
return `Privacy Corner at `+ (item.coincidenceFull ? item.coincidenceFull : `${item.coincidence} ${date.year}`)
break;
}
}
export function ccRenderer (item) {
let ccm = item.coincidence?.match(/\[(\w+)\]/)
let cc = ccm && ccm[1] ? ccm[1] : (item.coincidence ? item.coincidence : null)
return cc
}
export function eventStatus (item) {
const statuses = {
preregistration: {
title: 'Pre-registration',
color: 'text-orange-500',
},
unconfirmed: {
title: 'Planned',
color: ''
},
confirmed: {
title: 'Confirmed',
color: 'text-green-500',
},
past: {
title: 'Already happened',
color: 'text-green-800',
}
}
const date = dateInfo(item)
return item.confirmed
? (date.isDate ? statuses.past : statuses.confirmed)
: (item.links?.rsvp ? statuses.preregistration : statuses.unconfirmed);
}
export function getSpeaker (core, id) {
return core.people.find(p => p.id === id)
}
export function findExt (eventsExt, item) {
let slug = null
if (item.links?.web && item.links.web.match(/^https:\/\/lu.ma\//)) {
slug = item.links.web.match(/^https:\/\/lu.ma\/(.+)$/)[1]
}
if (item.links?.rsvp && item.links.rsvp.match(/^https:\/\/lu.ma\//)) {
slug = item.links.rsvp.match(/^https:\/\/lu.ma\/(.+)$/)[1]
}
if (!slug) {
return null
}
return eventsExt.find(ex => ex.url === slug)
}

View file

@ -0,0 +1,92 @@
---
import BaseLayout from '../../layouts/base.astro';
import SpeakerList from '../../components/SpeakerList.astro';
import core from '../../core.json';
import EventsExt from '../../events-ext.json';
import { dateFormat, dateInfo, dateEnd, nameRenderer, ccRenderer, eventStatus, findExt } from '../../lib/events.js';
import { marked } from 'marked';
const { id } = Astro.params;
export async function getStaticPaths() {
return core.events.map(event => ({ params: { id: event.id }}));
}
const item = core.events.find(event => event.id === id)
const status = eventStatus(item)
const ext = findExt(EventsExt, item)
---
<BaseLayout title={id} metaTitle={nameRenderer(item, true)}>
<div class="middle-pane-medium mt-10">
<div class="lg:flex w-full ">
<div class="lg:mr-10 mb-8 lg:mb-0">
<div><img src={ext ? ext.coverUrl : '/logo.svg'} class="rounded border border-white/20 w-80 aspect-square object-contain" class:list={[!ext ? 'p-10' : '']} /></div>
</div>
<div class="grow">
<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>&nbsp;- {ccRenderer(item)}</span>
}
</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.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>Visitors:
{item.visitors &&
<span class="text-white">{item.visitors} people</span>
}
{!item.visitors && ext &&
<span><span class="text-white">{ext.guestCount > 0 ? (ext.guestCount + ' people') : 'n/a'}</span> {status.title === 'Pre-registration' ? 'pre-registered' : 'registered'}</span>
}
{!item.visitors && !ext &&
<span>n/a</span>
}
</div>
<div class="mt-4 mb-2">
{item.links?.rsvp &&
<a href={item.links.rsvp} class="button inverted"><button>{status.title === 'Pre-registration' ? 'Pre-registration' : 'Registration'}</button></a>
}
{item.links?.web &&
<a href={item.links.web} class="button inverted"><button>Website</button></a>
}
</div>
</div>
</div>
</div>
<div class="bg-[#0f0f0f] px-4 py-2 mt-6 flex gap-6 w-full">
<div>ID: <span class="py-1 px-2 rounded bg-white/70 text-black">{item.id}</span></div>
<div>Lead: <span class="">{item.lead || 'n/a'}</span></div>
<div class="grow flex items-right justify-end gap-6">
{item.links?.rsvp &&
<a href={item.links.rsvp} class="hover:text-white">Lu.ma</a>
}
{item.issue &&
<a href={`https://github.com/web3privacy/events/issues/${item.issue}`} class="hover:text-white">PM</a>
}
<a href="https://github.com/web3privacy/data/blob/main/src/events/index.yaml" class="hover:text-white">Source</a>
</div>
</div>
<SpeakerList {item} thumbSize="big"/>
</div>
</BaseLayout>

View file

@ -69,6 +69,9 @@ for (const year of pastYears.reverse()) {
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');

View file

@ -193,6 +193,10 @@
@apply mb-4;
}
.w3pn-speaker-list a {
@apply hover:text-white;
}
.icon {
@apply inline-block w-12 h-12;
}