Skip to content
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
2 changes: 1 addition & 1 deletion app/blog/[slug]/post.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const colourByType: Dict<string> = {

const textByType: Dict<string> = {
'announcement': 'Announcement',
'weekly': 'Weekly Update',
'weekly': 'Progress Update',
'community': 'Community Highlight',
}

Expand Down
68 changes: 68 additions & 0 deletions app/changelog/buildStatsComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import Build from '../../types/build';
import Panel from '../common/uiLibrary/panel';
import { FaArrowUp, FaWrench } from 'react-icons/fa';
import { FaCirclePlus } from "react-icons/fa6";
import Card from '../../components/mocules/Card';
import SmallNoteText from '../../components/mocules/SmallNoteText';

interface BuildStatsProps {
builds: Build[];
}

const BuildStatsComponent: React.FC<BuildStatsProps> = ({ builds }) => {
const recentBuilds = builds.slice(0, 150);

const stats = recentBuilds.reduce((acc, build) => {
build.changes.forEach(change => {
switch (change.category) {
case 'NEW':
acc.additions++;
break;
case 'IMPROVEMENT':
acc.improvements++;
break;
case 'FIX':
acc.fixes++;
break;
default:
break;
}
});
return acc;
}, { additions: 0, improvements: 0, fixes: 0 });

return (
<Panel>
<h3 className="text-xl font-bold mb-4 text-white">Recent Build Statistics</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card>
<FaCirclePlus className="text-green-500 text-2xl mr-3" />
<div>
<p className="text-gray-300 text-sm">Additions</p>
<p className="text-white text-xl font-bold">{stats.additions}</p>
</div>
</Card>

<Card>
<FaArrowUp className="text-blue-600 text-2xl mr-3" />
<div>
<p className="text-gray-300 text-sm">Improvements</p>
<p className="text-white text-xl font-bold">{stats.improvements}</p>
</div>
</Card>

<Card>
<FaWrench className="text-orange-600 text-2xl mr-3" />
<div>
<p className="text-gray-300 text-sm">Bug Fixes</p>
<p className="text-white text-xl font-bold">{stats.fixes}</p>
</div>
</Card>
</div>
<SmallNoteText text={`Stats are based on the past ${recentBuilds.length} builds that were pushed onto our staging server from upstream.`} />
</Panel>
);
};

export default BuildStatsComponent;
32 changes: 32 additions & 0 deletions app/changelog/latestReleaseComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import Panel from '../common/uiLibrary/panel';
import { FaTag } from 'react-icons/fa';
import Card from '../../components/mocules/Card';
import SmallNoteText from '../../components/mocules/SmallNoteText';

interface ReleaseInfo {
version: string;
commitSha: string;
}

interface LatestReleaseComponentProps {
releaseInfo: ReleaseInfo;
}

const LatestReleaseComponent: React.FC<LatestReleaseComponentProps> = ({ releaseInfo }) => {
return (
<Panel>
<h3 className="text-xl font-bold mb-4 text-white">Latest Code-Scan Release</h3>
<Card>
<FaTag className="text-purple-500 text-2xl mr-3" />
<div>
<p className="text-white text-xl font-bold">{releaseInfo.version}</p>
<p className="text-gray-300 text-sm">Commit: {releaseInfo.commitSha.substring(0, 7)}</p>
</div>
</Card>
<SmallNoteText text="To keep our players safe, we only allow whitelisted code to be run on clients that are scanned and opened via stationhub." />
</Panel>
);
};

export default LatestReleaseComponent;
99 changes: 91 additions & 8 deletions app/changelog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,97 @@ import Container from "../common/uiLibrary/container";
import PageHeading from "../common/uiLibrary/PageHeading";
import BuildComponent from "./buildComponent";
import LoadingBuild from "./loading";
import BuildStatsComponent from "./buildStatsComponent";
import LatestReleaseComponent from "./latestReleaseComponent";

interface ReleaseInfo {
version: string;
commitSha: string;
}

interface GitHubTag {
name: string;
commit: {
url: string;
sha: string;
};
}

const fetchChangelog = async (url: string): Promise<AllChangesResponse> => {
const response = await fetch(url);
return await response.json();
}

const fetchLatestRelease = async (): Promise<ReleaseInfo | null> => {
try {
const response = await fetch('https://api.github.com/repos/unitystation/unitystation/tags');
const data = await response.json();

if (data && data.length > 0) {
const goodFileTags = data
.filter((tag: GitHubTag) => tag.name.startsWith('good-file-'))
.sort((a: GitHubTag, b: GitHubTag) => {
const versionA = a.name.replace('good-file-', '').split('.').map(Number);
const versionB = b.name.replace('good-file-', '').split('.').map(Number);
for (let i = 0; i < Math.max(versionA.length, versionB.length); i++) {
const numA = versionA[i] || 0;
const numB = versionB[i] || 0;
if (numA !== numB) {
return numB - numA; // Higher version first
}
}
return 0;
});

if (goodFileTags.length > 0) {
const latestTag = goodFileTags[0];
const commitResponse = await fetch(latestTag.commit.url);
const commitData = await commitResponse.json();

return {
version: latestTag.name,
commitSha: latestTag.commit.sha,
};
}
}
return null;
} catch (error) {
console.error('Error fetching latest release:', error);
return null;
}
};

const ChangelogPage = () => {
const [buildsResponse, setBuildsResponse] = useState<AllChangesResponse>();
const [builds, setBuilds] = useState<Build[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(true);
const INITIAL_PAGE = "https://changelog.unitystation.org/all-changes?limit=5";
const [releaseInfo, setReleaseInfo] = useState<ReleaseInfo | null>(null);
const [isLoadingRelease, setIsLoadingRelease] = useState<boolean>(true);
const INITIAL_PAGE = "https://changelog.unitystation.org/all-changes?limit=10";

useEffect(() => {
fetchChangelog(INITIAL_PAGE).then((response) => {
const fetchData = async () => {
setIsLoading(true);
setBuildsResponse(response);
setBuilds(response.results);
}).finally(
() => setIsLoading(false)
)
setIsLoadingRelease(true);

try {
const [changelogResponse, releaseData] = await Promise.all([
fetchChangelog(INITIAL_PAGE),
fetchLatestRelease()
]);

setBuildsResponse(changelogResponse);
setBuilds(changelogResponse.results);
setReleaseInfo(releaseData);
} catch (error) {
console.error('Error fetching data:', error);
} finally {
setIsLoading(false);
setIsLoadingRelease(false);
}
};

fetchData();
}, []);

const handleScroll = useCallback(async () => {
Expand All @@ -46,8 +117,20 @@ const ChangelogPage = () => {

return (
<Container>
<PageHeading isCentered>Changelog</PageHeading>
<div className="flex flex-col gap-4">
{!!builds && builds.length > 0 && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<BuildStatsComponent builds={builds} />
{isLoadingRelease ? (
<div className="flex items-center justify-center h-32 bg-gray-800 rounded-lg">
<p className="text-gray-400">Loading latest code-scan release info...</p>
</div>
) : releaseInfo ? (
<LatestReleaseComponent releaseInfo={releaseInfo} />
) : null}
</div>
)}
<PageHeading isCentered>Changelog</PageHeading>
{!!builds && builds.map((build, index) => {
return <BuildComponent build={build} key={index}/>
})}
Expand Down
15 changes: 15 additions & 0 deletions components/mocules/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, { ReactNode } from 'react';

interface CardProps {
children: ReactNode;
}

const Card: React.FC<CardProps> = ({ children }) => {
return (
<div className="flex items-center p-4 bg-gray-800 rounded-lg">
{children}
</div>
);
};

export default Card;
13 changes: 13 additions & 0 deletions components/mocules/SmallNoteText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

interface SmallNoteTextProps {
text: string;
}

const SmallNoteText: React.FC<SmallNoteTextProps> = ({ text }) => {
return (
<p className="text-gray-400 text-sm mt-4">{text}</p>
);
};

export default SmallNoteText;
Loading