Skip to content

Commit

Permalink
Merge pull request #136 from nbouliol/feat/add-apod-background
Browse files Browse the repository at this point in the history
[Feature] Add support for NASA Astronomy Picture of the Day (APOD)
  • Loading branch information
the-wright-jamie committed Nov 13, 2023
2 parents 6c3416a + bfd72a0 commit 067d06a
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
- run: npm run build:${{ matrix.build_target }}
env:
UNSPLASH_API_KEY: ${{ secrets.UNSPLASH_API_KEY }}
NASA_API_KEY: ${{ secrets.NASA_API_KEY }}
- uses: actions/upload-artifact@v3
with:
name: tab-nine-${{ matrix.build_target }}
Expand Down
1 change: 1 addition & 0 deletions src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ declare global {
const BUILD_TARGET: "chromium" | "firefox" | "web";
const DEV: boolean;
const UNSPLASH_API_KEY: string;
const NASA_API_KEY: string;
const VERSION: string;

const browser: Browser;
Expand Down
16 changes: 16 additions & 0 deletions src/plugins/backgrounds/apod/Apod.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.Apod
.picture
background-position: 50% 50%
background-size: cover
transition: opacity 0.25s ease-out

.title
display: flex
flex-direction: column
position: absolute
bottom: 1rem
left: 1rem
right: 1rem

p
margin: 4px 0 0
50 changes: 50 additions & 0 deletions src/plugins/backgrounds/apod/Apod.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";

import Backdrop from "../../../views/shared/Backdrop";

import { defaultData, Props } from "./types";
import { getPicture } from "./api";
import ApodTitle from "./ApodTitle";
import "./Apod.sass";

const Unsplash: React.FC<Props> = ({
cache,
data = defaultData,
loader,
setCache,
}) => {
const [picture, setPicture] = React.useState(cache);
const mounted = React.useRef(false);

React.useEffect(() => {
getPicture(data, loader).then(setCache);
if (mounted.current || !picture) getPicture(data, loader).then(setPicture);
mounted.current = true;
}, [data.customDate, data.date]);

return (
<div className="Apod fullscreen">
<Backdrop
className="picture fullscreen"
ready={
!!(picture?.media_type === "image"
? picture?.hdurl || picture?.url
: picture?.thumbnail_url)
}
style={{
backgroundImage: `url(${
picture?.media_type === "image"
? picture?.hdurl || picture?.url
: picture?.thumbnail_url
})`,
}}
/>

{picture && data.showTitle && (
<ApodTitle title={picture.title} copyright={picture.copyright} />
)}
</div>
);
};

export default Unsplash;
3 changes: 3 additions & 0 deletions src/plugins/backgrounds/apod/ApodSettings.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.ApodSettings
.date
margin-left: 4px
50 changes: 50 additions & 0 deletions src/plugins/backgrounds/apod/ApodSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import "./ApodSettings.sass";
import { DebounceInput } from "../../shared";
import { ApodDate, defaultData, Props } from "./types";
import { format } from "date-fns";

const maxDate = format(new Date(), "yyyy-MM-dd");

const UnsplashSettings: React.FC<Props> = ({ data = defaultData, setData }) => (
<div className="ApodSettings">
<label>
Date of the picture
<select
value={data.date}
onChange={(event) =>
setData({ ...data, date: event.target.value as ApodDate })
}
>
<option value="today">Today</option>
<option value="custom">Custom date</option>
</select>
</label>

{data.date === "custom" && (
<label>
Date
<DebounceInput
type="date"
value={data.customDate}
min="1995-06-16"
max={maxDate}
className="date"
onChange={(value) => setData({ ...data, customDate: value })}
wait={500}
/>
</label>
)}

<label>
<input
type="checkbox"
checked={data.showTitle}
onChange={(event) => setData({ ...data, showTitle: !data.showTitle })}
/>{" "}
Show title
</label>
</div>
);

export default UnsplashSettings;
13 changes: 13 additions & 0 deletions src/plugins/backgrounds/apod/ApodTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";
import { Image } from "./types";

type Props = Pick<Image, "title" | "copyright">;

const Credit: React.FC<Props> = ({ title, copyright }) => (
<div className="title">
<p>{title}</p>
{copyright && <p>&copy; {copyright}</p>}
</div>
);

export default Credit;
32 changes: 32 additions & 0 deletions src/plugins/backgrounds/apod/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { API } from "../../types";
import { Image, Data } from "./types";
import { format } from "date-fns";

type Config = Data;

const formatDateForApi = (date: string): string => {
return format(new Date(date), "yyyy-MM-dd");
};

export async function getPicture(
data: Config,
loader: API["loader"],
): Promise<Image> {
const url = "https://api.nasa.gov/planetary/apod";
const params = new URLSearchParams();

params.set("api_key", NASA_API_KEY);
params.set("thumbs", "true");

if (data.date === "custom" && data.customDate) {
params.set("date", formatDateForApi(data.customDate));
}

loader.push();
const res = await fetch(`${url}?${params}`);
const json = await res.json();

loader.pop();

return json;
}
14 changes: 14 additions & 0 deletions src/plugins/backgrounds/apod/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Config } from "../../types";
import Apod from "./Apod";
import ApodSettings from "./ApodSettings";

const config: Config = {
key: "background/apod",
name: "Astronomy Picture of the Day",
description: "NASA's sky pictures",
dashboardComponent: Apod,
settingsComponent: ApodSettings,
supportsBackdrop: true,
};

export default config;
30 changes: 30 additions & 0 deletions src/plugins/backgrounds/apod/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { API } from "../../types";

export type ApodDate = "today" | "custom";

export interface Data {
date: ApodDate;
customDate?: string;
showTitle: boolean;
}

export interface Image {
url: string;
hdurl: string;

title: string;
date: Date;
media_type: string;
explanation: string;
thumbnail_url: string;
copyright: string;
}

type Cache = Image;

export type Props = API<Data, Cache>;

export const defaultData: Data = {
date: "today",
showTitle: true,
};
3 changes: 2 additions & 1 deletion src/plugins/backgrounds/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import colour from "./colour";
import gradient from "./gradient";
import image from "./image";
import unsplash from "./unsplash";
import apod from "./apod";

export const backgroundConfigs = [colour, gradient, image, unsplash];
export const backgroundConfigs = [colour, gradient, image, unsplash, apod];

backgroundConfigs.sort((a, b) => a.name.localeCompare(b.name));
1 change: 1 addition & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const config = {
DEV: JSON.stringify(!isProduction),
VERSION: JSON.stringify(version),
UNSPLASH_API_KEY: JSON.stringify(process.env.UNSPLASH_API_KEY),
NASA_API_KEY: JSON.stringify(process.env.NASA_API_KEY),
}),
],
devtool: isWeb || !isProduction ? "source-map" : false,
Expand Down

0 comments on commit 067d06a

Please sign in to comment.