Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Release Notes easter egg #1023

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
56 changes: 1 addition & 55 deletions packages/special-pages/pages/release-notes/app/Components.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { h } from 'preact'
import { DuckDuckGoLogo } from '../../../shared/components/DuckDuckGoLogo/DuckDuckGoLogo'

Check failure on line 2 in packages/special-pages/pages/release-notes/app/Components.js

View workflow job for this annotation

GitHub Actions / unit (ubuntu-20.04)

'DuckDuckGoLogo' is defined but never used
import {
PageTitle,

Check failure on line 4 in packages/special-pages/pages/release-notes/app/Components.js

View workflow job for this annotation

GitHub Actions / unit (ubuntu-20.04)

'PageTitle' is defined but never used
UpdateStatus,

Check failure on line 5 in packages/special-pages/pages/release-notes/app/Components.js

View workflow job for this annotation

GitHub Actions / unit (ubuntu-20.04)

'UpdateStatus' is defined but never used
ReleaseNotesHeading,

Check failure on line 6 in packages/special-pages/pages/release-notes/app/Components.js

View workflow job for this annotation

GitHub Actions / unit (ubuntu-20.04)

'ReleaseNotesHeading' is defined but never used
ReleaseNotesSubheading,

Check failure on line 7 in packages/special-pages/pages/release-notes/app/Components.js

View workflow job for this annotation

GitHub Actions / unit (ubuntu-20.04)

'ReleaseNotesSubheading' is defined but never used
ReleaseNotesList,

Check failure on line 8 in packages/special-pages/pages/release-notes/app/Components.js

View workflow job for this annotation

GitHub Actions / unit (ubuntu-20.04)

'ReleaseNotesList' is defined but never used
ReleaseNotesContent,

Check failure on line 9 in packages/special-pages/pages/release-notes/app/Components.js

View workflow job for this annotation

GitHub Actions / unit (ubuntu-20.04)

'ReleaseNotesContent' is defined but never used
ReleaseNotes
} from './components/ReleaseNotes'
import { EasterEgg } from './components/EasterEgg'

Check failure on line 12 in packages/special-pages/pages/release-notes/app/Components.js

View workflow job for this annotation

GitHub Actions / unit (ubuntu-20.04)

'EasterEgg' is defined but never used
import { Button } from '../../../shared/components/Button/Button'

Check failure on line 13 in packages/special-pages/pages/release-notes/app/Components.js

View workflow job for this annotation

GitHub Actions / unit (ubuntu-20.04)

'Button' is defined but never used
import { Card } from '../../../shared/components/Card/Card'

Check failure on line 14 in packages/special-pages/pages/release-notes/app/Components.js

View workflow job for this annotation

GitHub Actions / unit (ubuntu-20.04)

'Card' is defined but never used
import { ContentPlaceholder } from './components/ContentPlaceholder'
import { useTypedTranslation } from '../app/types'

Expand Down Expand Up @@ -43,64 +44,9 @@

return (
<main className={styles.main}>
<h1>Release Notes Components</h1>

<h2>DuckDuckGo Logo</h2>
<DuckDuckGoLogo />
<hr/>

<h2>Page Title</h2>
<PageTitle title={t('browserReleaseNotes')}/>
<hr/>

<h2>Update Status</h2>
<UpdateStatus status="loading" version="1.0.1" timestamp={yesterdayInMilliseconds}/>
<UpdateStatus status="loaded" version="1.0.1" timestamp={todayInMilliseconds}/>
<UpdateStatus status="updateReady" version="1.2.0" timestamp={todayInMilliseconds}/>
<hr/>

<h2>Restart Button</h2>
<div>
<Button>{t('restartToUpdate')}</Button>
</div>
<hr/>

<h2>Content Placeholder</h2>
<ContentPlaceholder/>
<hr/>

<h2>Release Notes Heading</h2>
<ReleaseNotesHeading title="May 10 2023" version="1.0.0" showNewTag={false}/>
<ReleaseNotesHeading title="May 10 2024" version="1.2.0" showNewTag={true}/>
<hr/>

<h2>Release Notes Subheading</h2>
<ReleaseNotesSubheading title="Release Notes Subheading without Icon"/>
<ReleaseNotesSubheading icon="PrivacyPro" title="Release Notes Subheading with Privacy Pro Icon"/>
<hr/>

<h2>Release Notes List</h2>
<ReleaseNotesList notes={sampleNotesData[0].notes} />
<hr/>

<h2>Content Placeholder Inside a Card</h2>
<Card className={styles.card}>
<ContentPlaceholder/>
</Card>
<hr/>

<h2>Release Notes Inside a Card</h2>
<Card className={styles.card}>
<ReleaseNotesContent status="updateReady" title="May 10 2024" version="1.2.0" notes={sampleNotesData} />
</Card>

<ReleaseNotes releaseData={sampleData.loading} />
<LoadingThen>
<ReleaseNotes releaseData={sampleData.loaded} />
</LoadingThen>
<LoadingThen>
<ReleaseNotes releaseData={sampleData.updateReady} />
</LoadingThen>
</main>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { h } from 'preact'
import { useState, useRef } from 'preact/hooks'
import styles from './EasterEgg.module.css'

/**
* @param {object} props
* @param {h.JSX.MouseEventHandler<HTMLDivElement>} props.onClick
*/
function Overlay({ onClick }) {
return (
<div className={styles.overlay} onClick={onClick}>
<h1>Want to help us squash bugs?</h1>
<p>Click to start</p>
</div>
)
}

export function EasterEgg() {
const [started, setStarted] = useState(false)
/** @type {import('preact/hooks').MutableRef<HTMLIFrameElement|null>} */
const ref = useRef(null)

const clickHandler = () => {
setStarted(true)
}

const loadHandler = () => {
if (ref.current) {
ref.current.contentWindow?.focus()
}
}

return (
<div className={styles.container}>
{ started
? <iframe src="game/index.html" className={styles.iframe} ref={ref} onLoad={loadHandler}></iframe>
: <Overlay onClick={clickHandler}/>}
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.container {
align-self: center;
justify-self: center;
background-color: #f0e68c; /* Soft, muted yellow for an older look */
display: flex;
color: #333; /* Darker text for contrast */
font-family: 'VT323', monospace; /* Nostalgic 8-bit/16-bit font */
justify-content: center;
padding: 20px 10px;
width: 80%;
max-width: 600px;
aspect-ratio: 1.6;
}

.overlay {
align-items: center;
cursor: pointer;
display: flex;
flex-flow: column;
gap: 16px;
height: 100%;
justify-content: center;
width: 100%;
}

.overlay h1 {
font-size: 32px;
font-weight: bold;
line-height: 1.2;
text-align: center;
}

.overlay p {
font-size: 16px;
text-align: center;
}

.iframe {
border: 0;
width: calc(100% - 20px);
height: calc(100% - 20px);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Text } from '../../../../shared/components/Text/Text'
import { Card } from '../../../../shared/components/Card/Card'
import { Button } from '../../../../shared/components/Button/Button'
import { ContentPlaceholder } from './ContentPlaceholder'
import { EasterEgg } from './EasterEgg'

import styles from './ReleaseNotes.module.css'

Expand Down Expand Up @@ -231,6 +232,7 @@ export function ReleaseNotes ({ releaseData }) {
title={releaseTitle}
version={latestVersion || currentVersion}
notes={notes}/>}
{status === 'loaded' && <EasterEgg />}
</Card>
</article>
)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
187 changes: 187 additions & 0 deletions packages/special-pages/pages/release-notes/src/game/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Want to Help Us Squash Bugs?</title>
<style>
/* Retro-inspired body styling */
body {
margin: 0;
padding: 0;
color: #333; /* Darker text for contrast */
}

/* Game Container with retro pixel frame */
.game-container {
width: calc(100vw - 20px);
height: calc(100vh - 40px);
overflow: hidden;
position: relative;
background-color: #87ceeb; /* Soft blue for a classic sky look */
margin: 10px auto;
border: 8px solid #333; /* Thick border to simulate retro display frame */
box-shadow: 4px 4px 0px #999; /* Giving the game a 'window' look */
}

/* Score counter styled to look retro */
.score-counter {
position: absolute;
top: 10px;
right: 10px;
font-size: 24px;
font-weight: bold;
color: #fff;
background-color: #333;
padding: 5px;
border: 2px solid #fff;
border-radius: 5px;
font-family: 'VT323', monospace;
}

/* Retro duck */
.duck {
position: absolute;
width: 70px;
height: 70px;
background-image: url('duck.png'); /* Replace with a pixelated image for retro feel */
background-size: cover;
background-repeat: no-repeat;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
filter: pixelate(2px); /* Add pixelation effect */
}

/* Retro bug */
.bug {
position: absolute;
width: 40px;
height: 40px;
background-image: url('tracker.png'); /* Replace with pixelated bug image */
background-size: cover;
background-repeat: no-repeat;
filter: pixelate(2px); /* Add pixelation effect */
}

/* Footprint with pixelation effect */
.footprint {
position: absolute;
width: 40px;
height: 40px;
background-image: url('footprint.png'); /* Replace with retro-style footprint */
background-size: cover;
background-repeat: no-repeat;
z-index: 5;
filter: pixelate(2px); /* Add pixelation effect */
}

</style>
</head>
<body>
<div class="game-container" id="game-container">
<div class="duck" id="duck"></div>
<!-- Score counter displayed in the top-right corner -->
<div class="score-counter" id="score-counter">Score: 0</div>
</div>

<script>
const gameContainer = document.getElementById('game-container');
const duck = document.getElementById('duck');
const scoreCounter = document.getElementById('score-counter');
let score = 0;

// Initialize duck position
let duckLeft = 40;
let duckTop = 40;
duck.style.left = duckLeft + 'vw';
duck.style.top = duckTop + 'vh';

// Get the dimensions of the game container and the duck
const containerWidth = gameContainer.offsetWidth;
const containerHeight = gameContainer.offsetHeight;
const duckWidth = duck.offsetWidth;
const duckHeight = duck.offsetHeight;

// Move the duck with arrow keys and prevent it from going outside the container
document.addEventListener('keydown', function(event) {
event.preventDefault();
let left = parseFloat(duck.style.left);
let top = parseFloat(duck.style.top);

// Check for arrow key inputs and adjust the duck's position
if (event.key === 'ArrowUp') top -= 2;
if (event.key === 'ArrowDown') top += 2;
if (event.key === 'ArrowLeft') left -= 2;
if (event.key === 'ArrowRight') left += 2;

// Convert the width and height of the duck from px to vw/vh units
const duckWidthVW = (duckWidth / containerWidth) * 100;
const duckHeightVH = (duckHeight / containerHeight) * 100;

// Boundary checks to prevent the duck from going outside the container
if (left < 0) left = 0;
if (left > 100 - duckWidthVW) left = 100 - duckWidthVW;
if (top < 0) top = 0;
if (top > 100 - duckHeightVH) top = 100 - duckHeightVH;

// Update duck position
duck.style.left = `${left}vw`;
duck.style.top = `${top}vh`;

checkForBugSquash(left, top);
});

// Spawn bugs
function spawnBug() {
const bug = document.createElement('div');
bug.classList.add('bug');
bug.style.left = `${Math.random() * 55}vw`; /* Adjusted for the game field size */
bug.style.top = `${Math.random() * 55}vh`; /* Adjusted for the game field size */
gameContainer.appendChild(bug);

// Despawn the bug after a certain amount of time
setTimeout(() => {
bug.remove();
}, 5000);
}

// Check for collisions between duck and bugs (with improved sensitivity)
function checkForBugSquash(duckLeft, duckTop) {
const bugs = document.getElementsByClassName('bug');
Array.from(bugs).forEach(bug => {
const bugLeft = parseFloat(bug.style.left);
const bugTop = parseFloat(bug.style.top);

// Adjusting sensitivity by increasing collision area
const sensitivityRange = 4; // Smaller value means more sensitive to overlap

// Check if the duck is within a smaller range of the bug for collision detection
if (
Math.abs(duckLeft - bugLeft) < sensitivityRange &&
Math.abs(duckTop - bugTop) < sensitivityRange
) {
// Bug squashed
const footprint = document.createElement('div');
footprint.classList.add('footprint');
footprint.style.left = `${bugLeft}vw`;
footprint.style.top = `${bugTop}vh`;
gameContainer.appendChild(footprint);

bug.remove(); // Remove the bug

// Update score
score++;
scoreCounter.innerText = `Score: ${score}`; // Update the score in the score counter
console.log('Bug squashed! Score: ', score);
}
});
}

// Start the game loop
setInterval(spawnBug, 2000);

</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading