Skip to content

Commit

Permalink
Merge pull request #129 from project-slippi/rewrite/homepage
Browse files Browse the repository at this point in the history
Populate homepage
  • Loading branch information
NikhilNarayana authored Mar 3, 2021
2 parents 881568c + adefb43 commit 56bf334
Show file tree
Hide file tree
Showing 26 changed files with 2,270 additions and 67 deletions.
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@
"graphql": "^15.4.0",
"immer": "^8.0.0",
"lodash": "^4.17.20",
"medium-json-feed": "^0.0.3",
"moment": "^2.29.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-dropzone": "^11.2.4",
"react-markdown": "^5.0.3",
"react-query": "^3.12.0",
"react-router-dom": "^5.2.0",
"react-timeago": "^5.2.0",
"react-twitter-embed": "^3.0.3",
"react-virtualized-auto-sizer": "^1.0.3",
"react-window": "^1.8.6",
"semver": "^7.3.4",
Expand All @@ -49,12 +54,14 @@
"zustand": "^3.2.0"
},
"devDependencies": {
"@svgr/webpack": "^5.5.0",
"@types/adm-zip": "^0.4.33",
"@types/lodash": "^4.14.165",
"@types/moment": "^2.13.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-router-dom": "^5.1.6",
"@types/react-timeago": "^4.1.2",
"@types/react-virtualized-auto-sizer": "^1.0.0",
"@types/react-window": "^1.8.2",
"@types/semver": "^7.3.4",
Expand All @@ -81,6 +88,7 @@
"postinstall-postinstall": "^2.1.0",
"prettier": "^2.2.1",
"pretty-quick": "^3.1.0",
"raw-loader": "^4.0.2",
"react-hot-loader": "^4.13.0",
"sass-loader": "^10.1.0",
"threads-plugin": "^1.4.0",
Expand Down
10 changes: 10 additions & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ export interface FileLoadResult {
files: FileResult[];
fileErrorCount: number;
}

export interface NewsItem {
id: string;
title: string;
permalink: string;
publishedAt: string; // ISO string
subtitle?: string;
imageUrl?: string;
body?: string;
}
2 changes: 2 additions & 0 deletions src/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ declare module "*.gif";
declare module "*.bmp";
declare module "*.tiff";

declare module "medium-json-feed";
declare module "react-twitter-embed";
declare module "@material-ui/icons/*";
declare module "@material-ui/lab/*";

Expand Down
10 changes: 2 additions & 8 deletions src/main/downloadDolphin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import AdmZip from "adm-zip";
import { ChildProcessWithoutNullStreams, spawn, spawnSync } from "child_process";
import { DolphinLaunchType, findDolphinExecutable } from "common/dolphin";
import { fileExists } from "common/utils";
import { fetch } from "cross-fetch";
import { app, BrowserWindow } from "electron";
import { download } from "electron-dl";
import * as fs from "fs-extra";
import path from "path";
import { lt } from "semver";

import { getLatestRelease } from "./github";

function sendToRenderer(message: string, channel = "downloadDolphinLog"): void {
const window = BrowserWindow.getFocusedWindow();
if (window) {
Expand Down Expand Up @@ -84,13 +85,6 @@ async function getLatestReleaseData(type: DolphinLaunchType): Promise<any> {
return getLatestRelease(owner, repo);
}

async function getLatestRelease(owner: string, repo: string): Promise<any> {
const url = `https://api.github.com/repos/${owner}/${repo}/releases/latest`;
const res = await fetch(url);
const data = await res.json();
return data;
}

function matchesPlatform(releaseName: string): boolean {
switch (process.platform) {
case "win32":
Expand Down
59 changes: 59 additions & 0 deletions src/main/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { fetch } from "cross-fetch";
import log from "electron-log";

const SECOND = 1000;
const MINUTE = 60 * SECOND;

const EXPIRES_IN = 10 * MINUTE;

// Let's cache our Github responses so we don't exceed the 60 requests/hour rate limit
interface ResponseCacheEntry {
time: number;
response: any;
}

const githubResponseCache: Record<string, ResponseCacheEntry> = {};

export async function getLatestRelease(owner: string, repo: string): Promise<any> {
// We can re-use api calls by returning the first item in all releases
// since it already returns the newest release first.
const data = await getAllReleases(owner, repo);
if (data.length > 0) {
return data[0];
}
throw new Error(`No releases found for ${owner}/${repo}`);
}

export async function getAllReleases(owner: string, repo: string): Promise<any> {
const url = `https://api.github.com/repos/${owner}/${repo}/releases`;
const data = await cachedFetch(url);
return data;
}

async function cachedFetch(url: string): Promise<any> {
log.debug(`Checking cache for: ${url}`);
const cachedResponse = githubResponseCache[url];
if (cachedResponse) {
// Check if the cache has expired
const elapsedMs = Date.now() - cachedResponse.time;
if (elapsedMs <= EXPIRES_IN) {
log.debug(`Cache hit. Returning cached response.`);
return cachedResponse.response;
} else {
log.debug(`Cache expired. Refetching data...`);
}
}

log.debug(`Fetching: ${url}`);
// Fetch the data
const res = await fetch(url);
const data = await res.json();

// Cache the data
githubResponseCache[url] = {
response: data,
time: Date.now(),
};

return data;
}
11 changes: 8 additions & 3 deletions src/main/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { colors } from "common/colors";
import { app, BrowserWindow } from "electron";
import { app, BrowserWindow, shell } from "electron";
import contextMenu from "electron-context-menu";
import * as path from "path";
import { format as formatUrl } from "url";
Expand All @@ -16,7 +15,7 @@ function createMainWindow() {
show: false,
width: 1100,
height: 728,
backgroundColor: colors.offGray,
backgroundColor: "#23252C",
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
Expand Down Expand Up @@ -55,6 +54,12 @@ function createMainWindow() {
});
});

// Automatically open new-tab/new-window URLs in their default browser
window.webContents.on("new-window", (event: Event, url: string) => {
event.preventDefault();
shell.openExternal(url);
});

window.once("ready-to-show", () => {
window.show();
window.focus();
Expand Down
6 changes: 6 additions & 0 deletions src/main/listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path from "path";

import { DolphinManager, ReplayCommunication } from "./dolphinManager";
import { assertDolphinInstallations } from "./downloadDolphin";
import { fetchNewsFeed } from "./newsFeed";
import { worker as replayBrowserWorker } from "./replayBrowser/workerInterface";

export function setupListeners() {
Expand Down Expand Up @@ -58,4 +59,9 @@ export function setupListeners() {
const result = await w.calculateGameStats(filePath);
return result;
});

ipc.answerRenderer("fetchNewsFeed", async () => {
const result = await fetchNewsFeed();
return result;
});
}
55 changes: 55 additions & 0 deletions src/main/newsFeed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { NewsItem } from "common/types";
import mediumJSONFeed from "medium-json-feed";

import { getAllReleases } from "./github";

export async function fetchNewsFeed(): Promise<NewsItem[]> {
const mediumNews = fetchMediumNews();
const githubNews = fetchGithubReleaseNews(["Ishiiruka", "slippi-desktop-app"]);
const allNews = (await Promise.all([mediumNews, githubNews])).flat();
return allNews.sort((a, b) => {
// Sort all news item by reverse chronological order
const aDate = new Date(a.publishedAt).getTime();
const bDate = new Date(b.publishedAt).getTime();
return bDate - aDate;
});
}

async function fetchMediumNews(): Promise<NewsItem[]> {
const response = await mediumJSONFeed("project-slippi");
if (!response || response.status !== 200) {
throw new Error("Error fetching Medium feed");
}

const result = response.response;
return result.map((post: any) => {
const publishedAt = new Date(post.firstPublishedAt).toISOString();
return {
id: `medium-${post.id}`,
imageUrl: `https://cdn-images-1.medium.com/${post.virtuals.previewImage.imageId}`,
title: post.title,
subtitle: post.virtuals.subtitle,
publishedAt,
permalink: `https://medium.com/project-slippi/${post.uniqueSlug}`,
} as NewsItem;
});
}

async function fetchGithubReleaseNews(repos: string[]): Promise<NewsItem[]> {
const allReleases = await Promise.all(
repos.map(async (repo) => {
const releases = await getAllReleases("project-slippi", repo);
return releases.map((release: any) => {
return {
id: `gh-${repo}-${release.id}`,
title: `[${repo}] ${release.name}`,
body: release.body,
publishedAt: release.published_at,
permalink: release.html_url,
} as NewsItem;
});
}),
);

return allReleases.flat();
}
19 changes: 18 additions & 1 deletion src/renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import log from "electron-log";
import firebase from "firebase";
import React from "react";
import { hot } from "react-hot-loader/root";
import { QueryClient, QueryClientProvider } from "react-query";
import { HashRouter as Router, Redirect, Route, Switch } from "react-router-dom";

import { useApp } from "@/store/app";
Expand All @@ -19,6 +20,20 @@ import { LoadingView } from "./views/LoadingView";
import { NotFoundView } from "./views/NotFoundView";
import { SettingsView } from "./views/SettingsView";

const queryClient = new QueryClient({
defaultOptions: {
queries: {
// Once we've fetched a query never refetch
staleTime: Infinity,
refetchIntervalInBackground: false,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchInterval: false,
},
},
});

const App: React.FC = () => {
const initialized = useApp((state) => state.initialized);
const snackbarOpen = useApp((state) => state.snackbarOpen);
Expand Down Expand Up @@ -75,7 +90,9 @@ const AppWithProviders: React.FC = () => {
return (
<StylesProvider injectFirst>
<MuiThemeProvider theme={slippiTheme}>
<App />
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</MuiThemeProvider>
</StylesProvider>
);
Expand Down
24 changes: 14 additions & 10 deletions src/renderer/components/DualPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,7 @@ import { debounce } from "lodash";
import React from "react";
import styled from "styled-components";

interface OuterProps {
panelWidth: number;
}

const Outer = styled.div.attrs<OuterProps>((props) => ({
style: {
gridTemplateColumns: `${props.panelWidth}px auto`,
},
}))<OuterProps>`
const Outer = styled.div`
position: relative;
display: grid;
flex: 1;
Expand Down Expand Up @@ -54,6 +46,8 @@ const saveWidth = debounce((paneId: string, width: number) => {

export const DualPane: React.FC<{
id: string;
style?: React.CSSProperties;
className?: string;
resizable?: boolean;
leftSide: React.ReactNode;
rightSide: React.ReactNode;
Expand All @@ -74,6 +68,8 @@ export const DualPane: React.FC<{
maxWidth,
width = 250,
resizeHandleWidth = 8,
className,
style,
}) => {
const [panelWidth, setPanelWidth] = React.useState<number>(restoreWidth(id, width));

Expand All @@ -98,8 +94,16 @@ export const DualPane: React.FC<{
window.addEventListener("mouseup", onMouseUp);
};

const gridTemplateColumns = `${resizable ? panelWidth : width}px auto`;

return (
<Outer panelWidth={resizable ? panelWidth : width}>
<Outer
className={className}
style={{
gridTemplateColumns,
...style,
}}
>
<Column style={leftStyle}>{leftSide}</Column>
<Column style={rightStyle}>{rightSide}</Column>
{resizable && (
Expand Down
13 changes: 13 additions & 0 deletions src/renderer/components/ExternalLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/***
* This component automatically attaches target="_blank" and rel="noopener noreferrer" to links
*/

import React from "react";

export const ExternalLink: React.FC<React.HTMLProps<HTMLAnchorElement>> = (props) => {
return (
<a {...props} target="_blank" rel="noopener noreferrer">
{props.children}
</a>
);
};
2 changes: 1 addition & 1 deletion src/renderer/components/PathInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const useStyles = makeStyles((theme: Theme) =>
width: 400,
flex: 1,
marginRight: 10,
backgroundColor: theme.palette.background.default,
backgroundColor: theme.palette.background.paper,
},
input: {
marginLeft: theme.spacing(2),
Expand Down
Loading

0 comments on commit 56bf334

Please sign in to comment.