Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 74 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Fill in your details in the JSON list - the setup is this:

## Reference: [ktyl.dev](https://ktyl.dev)

<img width="1250" height="256" alt="image" src="https://github.com/user-attachments/assets/82498c3f-3194-4422-aecb-c7ab316dd4ef" />
<img width="1250" height="256" alt="Reference visual for ktyl.dev implementation" src="https://github.com/user-attachments/assets/82498c3f-3194-4422-aecb-c7ab316dd4ef" />

<details>

Expand Down Expand Up @@ -105,3 +105,76 @@ a:visited {
The above should be enough to implement the component accurately with no dependencies, however in practice it is implemented using the [Astro](https://astro.build/) framework, which allows for scripting to run at build-time to dynamically determine the links. The Astro component can be found verbatim [here](https://sauce.wednesday.pizza/ktyl/ktyl.dev/src/components/Webring.astro), which includes some templating and JavaScript to do aforementioned generation.

</details>

## Reference: [badlydone.dev](https://badlydone.dev/)

<img width="1250" height="256" alt="Reference visual for badlydone.dev implementation" src="https://github.com/user-attachments/assets/613cfdf8-fb87-43ed-b661-c5292f6f8612" />

<details>

<summary>ReactTS implementation (using a `window.sessionStorage` cache)</summary>

```typescript
export interface RingEntry {
name: string;
title: string;
description: string;
url: string;
}

const [ringData, setRingData] = useState<RingEntry[]>([]);
const [leftRing, setLeftRing] = useState<RingEntry>();
const [rightRing, setRightRing] = useState<RingEntry>();

useEffect(() => {
// Session storage is used as a quick and easy caching mechanism
const possibleData = window.sessionStorage.getItem('ringData');
if (possibleData) {
setRingData(JSON.parse(possibleData));
return;
}

fetch('https://willdotwhite.github.io/webring/data.json')
.then(async res => setRingData(await res.json()));
}, []);

useEffect(() => {
if (!ringData) return;

window.sessionStorage.setItem('ringData', JSON.stringify(ringData));

function shuffle(array: any[], seed: number) {
// Mike Bostock's Fisher–Yates Shuffle
// https://bost.ocks.org/mike/shuffle/
}

// This implementation is run in real-time, so needs some way of staying consistent beteeen page loads
// (even without the session storage behaviour)
// This logic uses the current date, parsed as an integer, as the sorting seed
// e.g. '2025-12-25' -> '20251225' -> (int) 20251225
const rightNow = new Date();
const dayString = rightNow.toISOString().slice(0, 10).replace(/-/g, '');
const shuffledArray = shuffle(ringData.filter(el => !el.url.includes('badlydone.dev')), parseInt(dayString));
setLeftRing(shuffledArray[0]);
setRightRing(shuffledArray[shuffledArray.length - 1]);

}, [ringData]);
```

```html
<p>
{leftRing && (
<a style={{width: '50%', display: 'inline-block'}} href={leftRing.url}>
<span style={{fontWeight: 'bold'}}>&larr; {leftRing.name} </span><br/>
{leftRing.description}
</a>
)}
{rightRing && (
<a style={{width: '50%', display: 'inline-block'}} href={rightRing.url}>
<span style={{fontWeight: 'bold'}}>{rightRing.name} &rarr;</span><br/>
{rightRing.description}
</a>
)}
</p>
```
</details>