The code and data behind xeiaso.net
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

lume: add events page fed by Events API

Signed-off-by: Xe Iaso <me@xeiaso.net>

Xe Iaso 21d75e1f d69787e5

+102 -26
+9 -13
cmd/xesite/main.go
··· 27 27 gitRepo = flag.String("git-repo", "https://github.com/Xe/site", "Git repository to clone") 28 28 githubSecret = flag.String("github-secret", "", "GitHub secret to use for webhooks") 29 29 internalAPIBind = flag.String("internal-api-bind", ":3001", "Port to listen on for the internal API") 30 - mimiAnnounceURL = flag.String("mimi-announce-url", "", "URL to use for the mimi announce service") 30 + miURL = flag.String("mimi-announce-url", "", "Mi url (named mimi-announce-url for historical reasons)") 31 31 patreonSaasProxyURL = flag.String("patreon-saasproxy-url", "http://xesite-patreon-saasproxy.flycast", "URL to use for the patreon saasproxy") 32 32 siteURL = flag.String("site-url", "https://xeiaso.net/", "URL to use for the site") 33 33 ) ··· 43 43 if err != nil { 44 44 log.Fatal(err) 45 45 } 46 - 47 - flag.VisitAll(func(f *flag.Flag) { 48 - slog.Debug("flag", "name", f.Name, "value", f.Value) 49 - }) 50 46 51 47 os.MkdirAll(*dataDir, 0700) 52 48 os.MkdirAll(filepath.Join(*dataDir, "tsnet"), 0700) ··· 57 53 } 58 54 59 55 fs, err := lume.New(ctx, &lume.Options{ 60 - Branch: *gitBranch, 61 - Repo: *gitRepo, 62 - StaticSiteDir: "lume", 63 - URL: *siteURL, 64 - Development: *devel, 65 - PatreonClient: pc, 66 - DataDir: *dataDir, 67 - MimiAnnounceURL: *mimiAnnounceURL, 56 + Branch: *gitBranch, 57 + Repo: *gitRepo, 58 + StaticSiteDir: "lume", 59 + URL: *siteURL, 60 + Development: *devel, 61 + PatreonClient: pc, 62 + DataDir: *dataDir, 63 + MiURL: *miURL, 68 64 }) 69 65 if err != nil { 70 66 log.Fatal(err)
+27 -13
internal/lume/lume.go
··· 21 21 "github.com/go-git/go-git/v5" 22 22 "github.com/go-git/go-git/v5/plumbing" 23 23 "github.com/twitchtv/twirp" 24 + "google.golang.org/protobuf/types/known/emptypb" 24 25 "google.golang.org/protobuf/types/known/timestamppb" 25 26 "gopkg.in/mxpv/patreon-go.v1" 26 27 "tailscale.com/metrics" 27 28 "xeiaso.net/v4/internal/config" 28 29 "xeiaso.net/v4/internal/jsonfeed" 30 + "xeiaso.net/v4/pb/external/mi" 29 31 "xeiaso.net/v4/pb/external/mimi/announce" 30 32 "xeiaso.net/v4/pb/external/protofeed" 31 33 ) ··· 70 72 opt *Options 71 73 conf *config.Config 72 74 73 - mimiClient announce.Announce 75 + // assumption: announce and events are on the same server 76 + mimiClient announce.Announce 77 + eventsClient mi.Events 74 78 75 79 fs fs.FS 76 80 lock sync.Mutex ··· 122 126 } 123 127 124 128 type Options struct { 125 - Development bool 126 - Branch string 127 - Repo string 128 - StaticSiteDir string 129 - URL string 130 - PatreonClient *patreon.Client 131 - DataDir string 132 - MimiAnnounceURL string 129 + Development bool 130 + Branch string 131 + Repo string 132 + StaticSiteDir string 133 + URL string 134 + PatreonClient *patreon.Client 135 + DataDir string 136 + MiURL string 133 137 } 134 138 135 139 func New(ctx context.Context, o *Options) (*FS, error) { ··· 206 210 siteCommit = ref.Hash().String() 207 211 } 208 212 209 - if o.MimiAnnounceURL != "" { 210 - mimiClient := announce.NewAnnounceProtobufClient(o.MimiAnnounceURL, &http.Client{}) 211 - fs.mimiClient = mimiClient 212 - slog.Debug("mimi integration enabled") 213 + if o.MiURL != "" { 214 + fs.mimiClient = announce.NewAnnounceProtobufClient(o.MiURL, &http.Client{}) 215 + fs.eventsClient = mi.NewEventsProtobufClient(o.MiURL, &http.Client{}) 216 + slog.Debug("mi integration enabled") 213 217 } 214 218 215 219 conf, err := config.Load(filepath.Join(fs.repoDir, "config.dhall")) ··· 379 383 } 380 384 } 381 385 386 + var events *mi.EventFeed 387 + if f.eventsClient != nil { 388 + var err error 389 + events, err = f.eventsClient.Get(context.Background(), &emptypb.Empty{}) 390 + if err != nil { 391 + slog.Error("failed to fetch events", "err", err) 392 + } 393 + } 394 + 382 395 if err := os.MkdirAll(dataDir, 0o755); err != nil { 383 396 return err 384 397 } ··· 389 402 "characters.json": f.conf.Characters, 390 403 "commit.json": map[string]any{"hash": siteCommit}, 391 404 "contactLinks.json": f.conf.ContactLinks, 405 + "events.json": events, 392 406 "jobHistory.json": f.conf.JobHistory, 393 407 "notableProjects.json": f.conf.NotableProjects, 394 408 "pronouns.json": f.conf.Pronouns,
+34
lume/src/_components/EventCard.jsx
··· 1 + const timestampPBtoDate = ({ seconds }) => { 2 + return new Date(seconds * 1000); 3 + }; 4 + 5 + const formatDate = (date) => { 6 + return date.toLocaleDateString('en-US', { 7 + month: 'long', 8 + day: 'numeric', 9 + year: 'numeric', 10 + }); 11 + }; 12 + 13 + // takes within.website.x.mi.Event 14 + export default ({ name, url, start_date, end_date, location, description }) => { 15 + const startDate = formatDate(timestampPBtoDate(start_date)); 16 + const endDate = formatDate(timestampPBtoDate(end_date)); 17 + return ( 18 + <div className="rounded-lg p-4 bg-bg-1 dark:bg-bgDark-1"> 19 + <h2 className="text-lg mb-2 text-fg-1 dark:text-fgDark-1"> 20 + <a href={url} target="_blank" rel="noopener noreferrer" className="text-blue-dark dark:text-blueDark-light"> 21 + {name} <span role="img" aria-label="link">🔗</span> 22 + </a> 23 + </h2> 24 + <div className="card-content text-fg-1 dark:text-fgDark-1"> 25 + <p> 26 + {location} - {startDate} {start_date.seconds !== end_date.seconds ? `thru ${endDate})}` : ""} 27 + </p> 28 + <p className="prose"> 29 + {description} 30 + </p> 31 + </div> 32 + </div> 33 + ); 34 + };
+1
lume/src/_data/.gitignore
··· 3 3 characters.json 4 4 commit.json 5 5 contactLinks.json 6 + events.json 6 7 jobHistory.json 7 8 notableProjects.json 8 9 pronouns.json
+31
lume/src/events.jsx
··· 1 + import EventCard from "./_components/EventCard.jsx"; 2 + 3 + export const title = "Events"; 4 + export const layout = "base.njk"; 5 + export const date = "2012-12-31"; 6 + export const desc = "A list of the upcoming events that I plan to attend and what I'll do there."; 7 + 8 + export default ({ events }) => { 9 + if (events.events === undefined) { 10 + return ( 11 + <> 12 + <h1 className="text-3xl mb-4">Events</h1> 13 + <p> 14 + I don't have any events planned right now or my events API is down. Check back later! 15 + </p> 16 + </> 17 + ); 18 + } 19 + 20 + return ( 21 + <> 22 + <h1 className="text-3xl mb-4">Events</h1> 23 + 24 + <p className="my-4">Where in the world is Xe Iaso?</p> 25 + 26 + <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> 27 + {events.events.map((event) => EventCard(event))} 28 + </div> 29 + </> 30 + ); 31 + }