diff --git a/src/components/ui/Background.tsx b/src/components/ui/Background.tsx index a8c9dae..26c3923 100644 --- a/src/components/ui/Background.tsx +++ b/src/components/ui/Background.tsx @@ -1,54 +1,20 @@ -import { Random } from 'unsplash-js/dist/methods/photos/types'; -import { createLocalStore } from '~/lib/create-local-store'; -import { random } from '~/lib/math'; -import { getRandomBackground, unsplashApi } from '~/api/unsplash'; +import { createEffect, createSignal } from 'solid-js'; +import { useSettings } from '~/state/SettingsProvider'; import style from './Background.module.css'; -import { BACKGROUND_FETCH_MINS } from '~/config'; -import { createEffect, createMemo, createSignal } from 'solid-js'; - -type BackgroundStorage = { - fetched_at: number; - backgrounds: Random[] | null; -}; export function Background() { - const [backgrounds, setBackgrounds] = createLocalStore( - 'start-page-background', - { - fetched_at: 0, - backgrounds: null, - } - ); - - const shouldFetch = - !backgrounds?.backgrounds || - backgrounds.fetched_at < new Date().getTime() - 1000 * 60 * BACKGROUND_FETCH_MINS; - - if (shouldFetch) { - getRandomBackground().then((r) => - setBackgrounds({ - fetched_at: new Date().getTime(), - backgrounds: (r?.response as Random[]) ?? null, - }) - ); - } - - const backgroundUrls = createMemo(() => { - const backgroundLength = backgrounds?.backgrounds?.length ?? 0; - const background = random(backgroundLength - 1); - return backgrounds?.backgrounds?.[background].urls; - }); + const { currentBackground } = useSettings(); const [fullLoaded, setFullLoaded] = createSignal(false); createEffect(() => { - if (!backgroundUrls()) { + if (!currentBackground()) { return null; } var curImg = new Image(); - curImg.src = backgroundUrls()!.full; + curImg.src = currentBackground()!.urls.full; curImg.onload = () => setFullLoaded(true); }); @@ -56,7 +22,7 @@ export function Background() { <> {!fullLoaded() ?
: null} diff --git a/src/components/ui/Sidebar.module.css b/src/components/ui/Sidebar.module.css index ac05ba5..26dc27b 100644 --- a/src/components/ui/Sidebar.module.css +++ b/src/components/ui/Sidebar.module.css @@ -43,13 +43,18 @@ height: 100%; } -.Settings { - cursor: pointer; +.SettingsWrapper { + display: flex; + align-items: center; + gap: .5rem; margin-top: clamp(.5rem, 1vw, 1.5rem); margin-right: 1.5rem; align-self: flex-end; } +.SettingsWrapper > * { + cursor: pointer; +} @media (max-width: 600px) { .Sidebar { diff --git a/src/components/ui/Sidebar.tsx b/src/components/ui/Sidebar.tsx index 7c97f2f..621803f 100644 --- a/src/components/ui/Sidebar.tsx +++ b/src/components/ui/Sidebar.tsx @@ -1,6 +1,11 @@ +import { Tooltip } from '@hope-ui/solid'; +import { IoSave } from 'solid-icons/io'; import { createTagOrder } from '~/api/get-apps'; +import { useSettings } from '~/state/SettingsProvider'; import { Settings } from '../widgets/Settings'; + import style from './Sidebar.module.css'; +import { HiSolidStar } from 'solid-icons/hi'; type SidebarProps = { activeTag?: string | null; @@ -9,6 +14,7 @@ type SidebarProps = { export function Sidebar(props: SidebarProps) { const tagOrder = createTagOrder(); + const { onSaveBackground } = useSettings(); return (
@@ -23,7 +29,14 @@ export function Sidebar(props: SidebarProps) { ))} - +
+ + + + + + +
); } diff --git a/src/components/widgets/Settings.tsx b/src/components/widgets/Settings.tsx index e1c0764..4bcd93a 100644 --- a/src/components/widgets/Settings.tsx +++ b/src/components/widgets/Settings.tsx @@ -41,6 +41,17 @@ export function Settings(props: SettingsProps) { > Show locations + + setAppSettings((prev) => ({ + ...prev, + useSavedBackgrounds: (e.target as any).checked, + })) + } + > + Use saved backgrounds + { diff --git a/src/lib/array.ts b/src/lib/array.ts new file mode 100644 index 0000000..fc1d859 --- /dev/null +++ b/src/lib/array.ts @@ -0,0 +1,8 @@ +import { random } from './math'; + +export function sample(list: T[]): T { + if (list.length === 1) { + return list[0]; + } + return list[random(list.length - 1)]; +} diff --git a/src/state/SettingsProvider.tsx b/src/state/SettingsProvider.tsx index fb88e80..c63fe22 100644 --- a/src/state/SettingsProvider.tsx +++ b/src/state/SettingsProvider.tsx @@ -1,27 +1,67 @@ -import { JSX, createContext, useContext } from 'solid-js'; +import { Accessor, JSX, createContext, createMemo, useContext } from 'solid-js'; import { SetStoreFunction } from 'solid-js/store'; +import { Random } from 'unsplash-js/dist/methods/photos/types'; +import { sample } from '~/lib/array'; import { createLocalStore } from '~/lib/create-local-store'; +import { SavedBackgrounds } from '~/types'; +import { createBackgrounds } from './create-backgrounds'; +import { random } from '~/lib/math'; -type Settings = { showLocations: boolean; viewAsTable: boolean }; +type Settings = { showLocations: boolean; viewAsTable: boolean; useSavedBackgrounds: boolean }; type SettingsContextType = { appSettings: Settings; setAppSettings: SetStoreFunction; + savedBackgrounds: SavedBackgrounds; + onSaveBackground(): void; + currentBackground: Accessor; }; const SettingsContext = createContext({ - appSettings: { showLocations: false, viewAsTable: false }, + appSettings: { showLocations: false, viewAsTable: false, useSavedBackgrounds: false }, setAppSettings: () => {}, + currentBackground: () => undefined, + savedBackgrounds: [], + onSaveBackground: () => {}, }); export function SettingsProvider(props: { children: JSX.Element }) { const [appSettings, setAppSettings] = createLocalStore('start-page-app-settings', { showLocations: false, viewAsTable: false, + useSavedBackgrounds: false, }); + const [savedBackgrounds, setSavedBackgrounds] = createLocalStore( + 'start-page-saved-backgrounds', + [] + ); + + const { currentBackground: currentRandomBackground } = createBackgrounds(); + + const onSaveBackground = () => { + setSavedBackgrounds((prev) => { + if (prev.find((background) => background.id === currentRandomBackground()?.id)) { + return prev; + } + return [...prev, currentRandomBackground()!]; + }); + }; + + const currentBackground = createMemo(() => + appSettings.useSavedBackgrounds ? sample(savedBackgrounds) : currentRandomBackground() + ); + return ( - + {props.children} ); diff --git a/src/state/create-backgrounds.ts b/src/state/create-backgrounds.ts new file mode 100644 index 0000000..8f9f7df --- /dev/null +++ b/src/state/create-backgrounds.ts @@ -0,0 +1,34 @@ +import { createMemo } from 'solid-js'; +import { Random } from 'unsplash-js/dist/methods/photos/types'; +import { getRandomBackground } from '~/api/unsplash'; +import { BACKGROUND_FETCH_MINS } from '~/config'; +import { sample } from '~/lib/array'; +import { createLocalStore } from '~/lib/create-local-store'; +import { random } from '~/lib/math'; +import { BackgroundStorage } from '~/types'; + +export function createBackgrounds() { + const [backgrounds, setBackgrounds] = createLocalStore( + 'start-page-background', + { fetched_at: 0, backgrounds: null } + ); + + const shouldFetch = + !backgrounds?.backgrounds || + backgrounds.fetched_at < new Date().getTime() - 1000 * 60 * BACKGROUND_FETCH_MINS; + + if (shouldFetch) { + getRandomBackground().then((r) => + setBackgrounds({ + fetched_at: new Date().getTime(), + backgrounds: (r?.response as Random[]) ?? null, + }) + ); + } + + const currentBackground = createMemo(() => + sample(backgrounds?.backgrounds ?? []) + ); + + return { backgrounds, currentBackground }; +} diff --git a/src/types.ts b/src/types.ts index 7f18804..06748f3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,5 @@ +import { Random } from 'unsplash-js/dist/methods/photos/types'; + export type AppDefinition = { name: string; url: string; @@ -31,3 +33,10 @@ export type WeatherData = { sunrise: number; sunset: number; }; + +export type BackgroundStorage = { + fetched_at: number; + backgrounds: Random[] | null; +}; + +export type SavedBackgrounds = Random[];