Skip to content

Commit 08b6536

Browse files
committed
App Store prep + iFly-parity refresh
Mirrors the iFly site work: legal pages for App Store reviewer scrutiny, device-frame screenshots, footer, and full Google Analytics removal. - Add /privacy: Bugsnag opt-out, iCloud (CloudKit, no dev access), local Wi-Fi web server, optional online play - Add /licenses: Dolphin GPL-2.0+ corresponding source at JoeMatt/dolphin, plus MoltenVK, ZIPFoundation, Zip, GCDWebServer, Bugsnag, libretro-db - Add Footer component with Support / Privacy / Licenses links (was missing entirely), copyright Joseph Mattiello, developer link, Dolphin source link - Add DeviceFrame + VideoShowcase components (CSS-only iPhone / iPad / Apple TV frames; blue accent on VideoShowcase placeholder) - Wrap all 8 homepage screenshots in DeviceFrame - Add stats row: No JIT / Dolphin Foundation / 3 Platforms / Free Open Source - Highlight iCube with blue-500 accent in hero H1 - Switch support email to provenance.emu+icube-support@gmail.com - Switch privacy contact to provenance.emu+icube-privacy@gmail.com - Strip Google Analytics: delete GoogleAnalytics.tsx and public/gtag-init.js, remove gtag Script tags + GA preconnect + google-analytics.com / googletagmanager.com from CSP, drop gtag event call from TestFlightGate - author/creator updated from "Provenance Emu" Organization to "Joseph Mattiello" Person in metadata + JSON-LD - sitemap: add /privacy, /licenses Build verified: 19 routes compile clean.
1 parent 37d03dd commit 08b6536

12 files changed

Lines changed: 474 additions & 125 deletions

File tree

public/gtag-init.js

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/app/layout.tsx

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import type { Metadata } from "next";
22
import "./globals.css";
33
import Navigation from "@/components/Navigation";
4-
import Script from "next/script";
5-
import GoogleAnalytics from "@/components/GoogleAnalytics";
6-
7-
const GA_ID = "G-R9V4MJ0BR2";
4+
import Footer from "@/components/Footer";
85

96
export const metadata: Metadata = {
107
metadataBase: new URL("https://icube-emu.com"),
@@ -25,8 +22,8 @@ export const metadata: Metadata = {
2522
"Apple TV emulator",
2623
"iPhone emulator",
2724
],
28-
authors: [{ name: "Provenance Emu" }],
29-
creator: "Provenance Emu",
25+
authors: [{ name: "Joseph Mattiello" }],
26+
creator: "Joseph Mattiello",
3027
openGraph: {
3128
type: "website",
3229
siteName: "iCube",
@@ -86,11 +83,11 @@ export const metadata: Metadata = {
8683
// the CSP blocks hydration and the page renders a blank background.
8784
const CSP = [
8885
"default-src 'self'",
89-
"script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com",
86+
"script-src 'self' 'unsafe-inline'",
9087
"style-src 'self' 'unsafe-inline'",
9188
"img-src 'self' data: https: blob:",
9289
"frame-src https://itch.io https://html.itch.zone https://v6p9d9t4.ssl.hwcdn.net",
93-
"connect-src 'self' https://www.google-analytics.com https://analytics.google.com https://region1.google-analytics.com https://region1.analytics.google.com",
90+
"connect-src 'self'",
9491
"font-src 'self'",
9592
"object-src 'none'",
9693
"worker-src 'none'",
@@ -111,9 +108,9 @@ const jsonLd = {
111108
url: "https://icube-emu.com",
112109
image: "https://icube-emu.com/icon-1024.webp",
113110
author: {
114-
"@type": "Organization",
115-
name: "Provenance Emu",
116-
url: "https://provenance-emu.com",
111+
"@type": "Person",
112+
name: "Joseph Mattiello",
113+
url: "https://joemattiello.dev",
117114
},
118115
offers: {
119116
"@type": "Offer",
@@ -133,26 +130,17 @@ export default function RootLayout({
133130
<head>
134131
<meta httpEquiv="Content-Security-Policy" content={CSP} />
135132
<meta name="referrer" content="strict-origin-when-cross-origin" />
136-
<link rel="preconnect" href="https://www.googletagmanager.com" />
137-
<link rel="preconnect" href="https://www.google-analytics.com" />
138-
<link rel="dns-prefetch" href="https://www.googletagmanager.com" />
139-
<link rel="dns-prefetch" href="https://www.google-analytics.com" />
140133
<script
141134
type="application/ld+json"
142135
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
143136
/>
144137
</head>
145138
<body suppressHydrationWarning className="antialiased">
146-
<Script
147-
src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`}
148-
strategy="afterInteractive"
149-
/>
150-
<Script src="/gtag-init.js" strategy="afterInteractive" />
151139
<Navigation />
152-
<GoogleAnalytics />
153140
<main className="min-h-screen">
154141
{children}
155142
</main>
143+
<Footer />
156144
</body>
157145
</html>
158146
);

src/app/licenses/page.tsx

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import type { Metadata } from 'next';
2+
3+
export const metadata: Metadata = {
4+
title: 'Licenses & Acknowledgements',
5+
description:
6+
'Open-source components used in iCube and corresponding source code, per GPL-2.0 and other applicable licenses.',
7+
alternates: { canonical: 'https://icube-emu.com/licenses/' },
8+
};
9+
10+
type Component = {
11+
name: string;
12+
license: string;
13+
url: string;
14+
note?: string;
15+
};
16+
17+
const components: Component[] = [
18+
{
19+
name: 'Dolphin',
20+
license: 'GPL-2.0+',
21+
url: 'https://github.com/JoeMatt/dolphin',
22+
note: 'Custom modifications by Joseph Mattiello. Corresponding source available at the link above.',
23+
},
24+
{
25+
name: 'MoltenVK',
26+
license: 'Apache-2.0',
27+
url: 'https://github.com/KhronosGroup/MoltenVK',
28+
},
29+
{
30+
name: 'ZIPFoundation',
31+
license: 'MIT',
32+
url: 'https://github.com/weichsel/ZIPFoundation',
33+
},
34+
{
35+
name: 'Zip',
36+
license: 'MIT',
37+
url: 'https://github.com/marmelroy/Zip',
38+
},
39+
{
40+
name: 'GCDWebServer',
41+
license: 'BSD-3-Clause',
42+
url: 'https://github.com/swisspol/GCDWebServer',
43+
},
44+
{
45+
name: 'Bugsnag',
46+
license: 'MIT',
47+
url: 'https://github.com/bugsnag/bugsnag-cocoa',
48+
},
49+
{
50+
name: 'libretro database',
51+
license: 'See repository',
52+
url: 'https://github.com/libretro/libretro-database',
53+
},
54+
];
55+
56+
export default function Licenses() {
57+
return (
58+
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-gray-900 dark:to-blue-900">
59+
<div className="container mx-auto px-4 py-16">
60+
<div className="max-w-3xl mx-auto">
61+
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white mb-3 text-center">
62+
Licenses &amp; Acknowledgements
63+
</h1>
64+
<p className="text-center text-gray-600 dark:text-gray-400 mb-10">
65+
Open-source components used in iCube.
66+
</p>
67+
68+
<div className="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl p-8 space-y-8 shadow-lg">
69+
<section>
70+
<p className="text-gray-700 dark:text-gray-300 leading-relaxed">
71+
iCube is built on Dolphin and a number of open-source libraries. Each project
72+
is credited below alongside its license. Where required by license, links point
73+
to corresponding source.
74+
</p>
75+
</section>
76+
77+
<section>
78+
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">Components</h2>
79+
<ul className="space-y-4">
80+
{components.map(c => (
81+
<li key={c.name} className="border-l-2 border-blue-500/40 pl-4">
82+
<div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
83+
<a
84+
href={c.url}
85+
target="_blank"
86+
rel="noopener noreferrer"
87+
className="text-base font-semibold text-gray-900 dark:text-white hover:text-blue-600 dark:hover:text-blue-300 hover:underline transition-colors"
88+
>
89+
{c.name}
90+
</a>
91+
<span className="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-500">
92+
{c.license}
93+
</span>
94+
</div>
95+
{c.note && (
96+
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">{c.note}</p>
97+
)}
98+
<p className="text-xs text-gray-500 dark:text-gray-600 mt-1 break-all">{c.url}</p>
99+
</li>
100+
))}
101+
</ul>
102+
<p className="text-xs text-gray-500 dark:text-gray-500 mt-6">
103+
This list may not be exhaustive. Additional in-tree licenses ship with the app
104+
bundle and the Dolphin source repository.
105+
</p>
106+
</section>
107+
108+
<section>
109+
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">Copyright</h2>
110+
<p className="text-gray-700 dark:text-gray-300 leading-relaxed">
111+
© Joseph Mattiello. iCube and its custom Dolphin modifications
112+
developed by Joseph Mattiello.
113+
</p>
114+
</section>
115+
116+
<section>
117+
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">GPL-2.0 corresponding source</h2>
118+
<p className="text-gray-700 dark:text-gray-300 leading-relaxed">
119+
Per the GPL-2.0+ license under which Dolphin is distributed, the corresponding
120+
source for the version of Dolphin used in iCube is available at{" "}
121+
<a
122+
href="https://github.com/JoeMatt/dolphin"
123+
target="_blank"
124+
rel="noopener noreferrer"
125+
className="text-blue-600 dark:text-blue-400 hover:underline transition-colors"
126+
>
127+
github.com/JoeMatt/dolphin
128+
</a>.
129+
</p>
130+
</section>
131+
</div>
132+
</div>
133+
</div>
134+
</div>
135+
);
136+
}

src/app/page.tsx

Lines changed: 25 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { Metadata } from 'next';
2-
import Image from 'next/image';
32
import iphone1 from '@/images/screenshots/ios/iphone1-library.jpg';
43
import iphone2 from '@/images/screenshots/ios/iphone2-search.jpg';
54
import iphone3 from '@/images/screenshots/ios/iphone3-emu.webp';
@@ -14,6 +13,8 @@ export const metadata: Metadata = {
1413
import DownloadSection from '@/components/DownloadSection';
1514
import SocialButton, { DiscordIcon, XIcon, BmcIcon, PatreonIcon } from '@/components/SocialButton';
1615
import Features from '@/components/Features';
16+
import DeviceFrame from '@/components/DeviceFrame';
17+
import VideoShowcase from '@/components/VideoShowcase';
1718
import tvos1 from '@/images/screenshots/tvos/tvos-pause.webp';
1819
import tvos2 from '@/images/screenshots/tvos/tvos-settings.webp';
1920
import tvos3 from '@/images/screenshots/tvos/tvos-sources.webp';
@@ -25,36 +26,32 @@ export default function Home() {
2526
<section className="container mx-auto px-4 py-16 text-center">
2627
<div className="max-w-4xl mx-auto">
2728
<h1 className="text-5xl md:text-6xl font-bold text-gray-900 dark:text-white mb-6">
28-
iCube
29+
i<span className="text-blue-500">Cube</span>
2930
</h1>
3031
<p className="text-xl md:text-2xl text-gray-600 dark:text-gray-300 mb-4">
31-
GameCube & Wii Emulator
32+
GameCube &amp; Wii Emulator
3233
</p>
3334
<p className="text-lg text-gray-600 dark:text-gray-400 mb-8 max-w-2xl mx-auto">
3435
Experience classic Nintendo GameCube and Wii games on your iOS devices and Apple TV.
3536
Built on the proven Dolphin emulator foundation.
3637
</p>
38+
</div>
39+
</section>
3740

38-
{/* App Store Button Placeholder */}
39-
{/*
40-
Temporarily disabled until App Store availability.
41-
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center mb-16">
42-
<div className="bg-black text-white px-6 py-3 rounded-lg hover:bg-gray-800 transition-colors cursor-pointer">
43-
<div className="flex items-center gap-2">
44-
<span className="text-xs">Download on the</span>
45-
</div>
46-
<div className="text-lg font-semibold">App Store</div>
47-
</div>
48-
<div className="bg-black text-white px-6 py-3 rounded-lg hover:bg-gray-800 transition-colors cursor-pointer">
49-
<div className="flex items-center gap-2">
50-
<span className="text-xs">Available on</span>
51-
</div>
52-
<div className="text-lg font-semibold">Apple TV</div>
53-
</div>
41+
{/* Stats row */}
42+
<section className="container mx-auto px-4 pb-12">
43+
<div className="max-w-2xl mx-auto grid grid-cols-2 sm:grid-cols-4 gap-3">
44+
{([
45+
['No JIT', 'Required'],
46+
['Dolphin', 'Foundation'],
47+
['3', 'Platforms'],
48+
['Free', 'Open Source'],
49+
] as const).map(([value, label]) => (
50+
<div key={label} className="text-center py-4 px-2 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-xl shadow-sm">
51+
<div className="text-xl font-black text-blue-600 dark:text-blue-400">{value}</div>
52+
<div className="text-xs text-gray-600 dark:text-gray-500 mt-1 uppercase tracking-wide">{label}</div>
5453
</div>
55-
*/}
56-
57-
{/* Sideloading links moved to Download section below */}
54+
))}
5855
</div>
5956
</section>
6057

@@ -87,6 +84,9 @@ export default function Home() {
8784
</div>
8885
</section>
8986

87+
{/* Video Showcase — drop MP4 at public/video/gameplay.mp4 to activate */}
88+
<VideoShowcase />
89+
9090
{/* Screenshots Section */}
9191
<section className="container mx-auto px-4 py-16">
9292
<h2 className="text-3xl font-bold text-center text-gray-900 dark:text-white mb-12">
@@ -104,17 +104,7 @@ export default function Home() {
104104
[iphone2, 'iCube game search on iPhone'],
105105
[iphone3, 'iCube running a GameCube game on iPhone'],
106106
] as const).map(([img, alt], idx) => (
107-
<div key={`iphone-${idx}`} className="bg-gray-200 dark:bg-gray-700 rounded-lg p-4 shadow-lg">
108-
<div className="rounded-lg overflow-hidden w-64 h-96">
109-
<Image
110-
src={img}
111-
alt={alt}
112-
className="h-full w-full object-cover"
113-
sizes="(max-width: 768px) 256px, 256px"
114-
priority={idx === 0}
115-
/>
116-
</div>
117-
</div>
107+
<DeviceFrame key={`iphone-${idx}`} type="iphone" src={img} alt={alt} priority={idx === 0} />
118108
))}
119109
</div>
120110
</div>
@@ -129,16 +119,7 @@ export default function Home() {
129119
[ipad1, 'iCube game library on iPad'],
130120
[ipad2, 'iCube game search on iPad'],
131121
] as const).map(([img, alt], idx) => (
132-
<div key={`ipad-${idx}`} className="bg-gray-200 dark:bg-gray-700 rounded-lg p-4 shadow-lg">
133-
<div className="rounded-lg overflow-hidden w-80 h-60">
134-
<Image
135-
src={img}
136-
alt={alt}
137-
className="h-full w-full object-cover"
138-
sizes="(max-width: 768px) 320px, 320px"
139-
/>
140-
</div>
141-
</div>
122+
<DeviceFrame key={`ipad-${idx}`} type="ipad" src={img} alt={alt} />
142123
))}
143124
</div>
144125
</div>
@@ -154,16 +135,7 @@ export default function Home() {
154135
[tvos2, 'iCube settings on Apple TV'],
155136
[tvos3, 'iCube game sources on Apple TV'],
156137
] as const).map(([img, alt], idx) => (
157-
<div key={`appletv-${idx}`} className="bg-gray-200 dark:bg-gray-700 rounded-lg p-4 shadow-lg">
158-
<div className="rounded-lg overflow-hidden w-[384px] aspect-video">
159-
<Image
160-
src={img}
161-
alt={alt}
162-
className="h-full w-full object-cover"
163-
sizes="(max-width: 768px) 384px, 384px"
164-
/>
165-
</div>
166-
</div>
138+
<DeviceFrame key={`appletv-${idx}`} type="appletv" src={img} alt={alt} />
167139
))}
168140
</div>
169141
</div>

0 commit comments

Comments
 (0)