Skip to content

Commit 102b48d

Browse files
committed
New landing page design
1 parent a81265d commit 102b48d

File tree

16 files changed

+1382
-186
lines changed

16 files changed

+1382
-186
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,6 @@ src/locales/*.js
3838
/playwright-report/
3939
/blob-report/
4040
/playwright/.cache/
41+
42+
# Generated mock screenshots
43+
/mock-screenshots/

design/logo.af

825 KB
Binary file not shown.

scripts/fetch-mock-posts.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import fs from 'fs';
2+
import https from 'https';
3+
import path from 'path';
4+
import { fileURLToPath } from 'url';
5+
6+
const __filename = fileURLToPath(import.meta.url);
7+
const __dirname = path.dirname(__filename);
8+
9+
const posts = [
10+
'https://mastodon.social/api/v1/statuses/115903453951785429',
11+
'https://mastodon.social/api/v1/statuses/115887242651860407',
12+
'https://mastodon.social/api/v1/statuses/115853890477340137',
13+
'https://mastodon.social/api/v1/statuses/115854031046143927',
14+
];
15+
16+
async function fetchJSON(url) {
17+
return new Promise((resolve, reject) => {
18+
https
19+
.get(url, (res) => {
20+
let data = '';
21+
res.on('data', (chunk) => {
22+
data += chunk;
23+
});
24+
res.on('end', () => {
25+
try {
26+
resolve(JSON.parse(data));
27+
} catch (e) {
28+
reject(e);
29+
}
30+
});
31+
})
32+
.on('error', reject);
33+
});
34+
}
35+
36+
async function main() {
37+
const results = [];
38+
39+
for (const url of posts) {
40+
console.log(`Fetching ${url}...`);
41+
try {
42+
const data = await fetchJSON(url);
43+
results.push(data);
44+
} catch (error) {
45+
console.error(`Error fetching ${url}:`, error.message);
46+
}
47+
}
48+
49+
const outputPath = path.join(__dirname, '../src/data/mock-posts.json');
50+
fs.writeFileSync(outputPath, JSON.stringify(results, null, 2));
51+
console.log(`\nSaved ${results.length} posts to ${outputPath}`);
52+
}
53+
54+
main().catch(console.error);
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import path from 'path';
2+
import { fileURLToPath } from 'url';
3+
4+
import { chromium } from '@playwright/test';
5+
6+
const __filename = fileURLToPath(import.meta.url);
7+
const __dirname = path.dirname(__filename);
8+
9+
const VIEWPORTS = [
10+
{ name: 'mobile', width: 375, height: 812 },
11+
{ name: 'tablet', width: 768, height: 1024 },
12+
];
13+
14+
const COLOR_SCHEMES = ['light', 'dark'];
15+
16+
const BASE_URL = process.env.BASE_URL || 'http://localhost:5173';
17+
const OUTPUT_DIR = path.join(__dirname, '../mock-screenshots');
18+
19+
async function generateScreenshots() {
20+
const browser = await chromium.launch();
21+
22+
try {
23+
for (const colorScheme of COLOR_SCHEMES) {
24+
for (const viewport of VIEWPORTS) {
25+
console.log(
26+
`Generating ${viewport.name} ${colorScheme} screenshot (${viewport.width}x${viewport.height})...`,
27+
);
28+
29+
const context = await browser.newContext({
30+
viewport: { width: viewport.width, height: viewport.height },
31+
deviceScaleFactor: 2,
32+
colorScheme,
33+
});
34+
35+
const page = await context.newPage();
36+
37+
// Listen to console messages
38+
page.on('console', (msg) => {
39+
const type = msg.type();
40+
if (type === 'error' || type === 'warning') {
41+
console.log(` [Browser ${type}]:`, msg.text());
42+
}
43+
});
44+
45+
// Listen to page errors
46+
page.on('pageerror', (error) => {
47+
console.error(' [Browser error]:', error.message);
48+
});
49+
50+
// Navigate to the mock home page
51+
const url = `${BASE_URL}/#/_mock/home`;
52+
console.log(` Navigating to ${url}...`);
53+
await page.goto(url, { waitUntil: 'load', timeout: 60000 });
54+
55+
// Wait for React app to mount
56+
await page.waitForSelector('#app', { timeout: 10000 });
57+
console.log(' ✓ App mounted');
58+
59+
// Wait for React to hydrate and route to render
60+
await page.waitForTimeout(3000);
61+
62+
// Wait for the timeline to be rendered
63+
try {
64+
await page.waitForSelector('.timeline', { timeout: 30000 });
65+
console.log(' ✓ Timeline found');
66+
} catch (e) {
67+
console.error(' ✗ Timeline not found.');
68+
console.error(' Current URL:', page.url());
69+
const bodyHTML = await page.evaluate(() => document.body.innerHTML);
70+
console.error(' Body HTML:', bodyHTML.substring(0, 1000));
71+
const errors = await page.evaluate(() => {
72+
return window.__errors || [];
73+
});
74+
console.error(' JS Errors:', errors);
75+
throw e;
76+
}
77+
78+
// Wait for timeline items to be rendered
79+
try {
80+
await page.waitForSelector('.timeline-item', { timeout: 30000 });
81+
console.log(' ✓ Timeline items found');
82+
} catch (e) {
83+
console.error(' ✗ Timeline items not found');
84+
throw e;
85+
}
86+
87+
// Wait for images and fonts to load
88+
await page.waitForTimeout(3000);
89+
90+
const screenshotPath = path.join(
91+
OUTPUT_DIR,
92+
`mock-home-${viewport.name}-${colorScheme}@2x.png`,
93+
);
94+
await page.screenshot({
95+
path: screenshotPath,
96+
fullPage: true,
97+
});
98+
99+
console.log(` ✓ Screenshot saved: ${screenshotPath}`);
100+
await context.close();
101+
}
102+
}
103+
104+
await browser.close();
105+
console.log('\n✨ All screenshots generated successfully!');
106+
console.log(
107+
'Generated 4 screenshots: mobile & desktop in both light & dark modes',
108+
);
109+
} catch (error) {
110+
await browser.close();
111+
throw error;
112+
}
113+
}
114+
115+
generateScreenshots().catch((error) => {
116+
console.error('Error generating screenshots:', error);
117+
process.exit(1);
118+
});

src/app.jsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ const Sandbox =
8383
? lazy(() => import('./pages/sandbox'))
8484
: () => null;
8585

86+
// Lazy load MockHome component only in development (not PHANPY_DEV)
87+
const MockHome = lazy(() => import('./pages/mock-home'));
88+
8689
// Lazy load YearInPosts component
8790
const YearInPosts = lazy(() => import('./pages/year-in-posts'));
8891

@@ -621,7 +624,7 @@ function Root({ isLoggedIn }) {
621624
}
622625

623626
function isRootPath(pathname) {
624-
return /^\/(login|welcome|_sandbox|_qr-scan)/i.test(pathname);
627+
return /^\/(login|welcome|_sandbox|_qr-scan|_mock)/i.test(pathname);
625628
}
626629

627630
const PrimaryRoutes = memo(({ isLoggedIn }) => {
@@ -636,6 +639,14 @@ const PrimaryRoutes = memo(({ isLoggedIn }) => {
636639
<Route path="/" element={<Root isLoggedIn={isLoggedIn} />} />
637640
<Route path="/login" element={<Login />} />
638641
<Route path="/welcome" element={<Welcome />} />
642+
<Route
643+
path="/_mock/home"
644+
element={
645+
<Suspense>
646+
<MockHome />
647+
</Suspense>
648+
}
649+
/>
639650
{(import.meta.env.DEV || import.meta.env.PHANPY_DEV) && (
640651
<>
641652
<Route

src/assets/logo-text-2.svg

Lines changed: 3 additions & 0 deletions
Loading

src/assets/logo.svg

Lines changed: 0 additions & 1 deletion
Loading
76.1 KB
Loading
84.8 KB
Loading
135 KB
Loading

0 commit comments

Comments
 (0)