Archived
Add ability to favorite backgrounds
This commit is contained in:
@@ -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<BackgroundStorage>(
|
||||
'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() {
|
||||
<>
|
||||
<img
|
||||
class={style.Background}
|
||||
src={fullLoaded() ? backgroundUrls()?.full : backgroundUrls()?.thumb}
|
||||
src={fullLoaded() ? currentBackground()?.urls.full : currentBackground()?.urls.thumb}
|
||||
/>
|
||||
{!fullLoaded() ? <div class={style.Overlay}></div> : null}
|
||||
</>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 (
|
||||
<div class={style.Sidebar}>
|
||||
@@ -23,7 +29,14 @@ export function Sidebar(props: SidebarProps) {
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Settings className={style.Settings} />
|
||||
<div class={style.SettingsWrapper}>
|
||||
<Tooltip withArrow placement="top" label="Save background">
|
||||
<HiSolidStar size={18} onClick={onSaveBackground} />
|
||||
</Tooltip>
|
||||
<Tooltip withArrow placement="top" label="Settings">
|
||||
<Settings />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,17 @@ export function Settings(props: SettingsProps) {
|
||||
>
|
||||
Show locations
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
checked={appSettings.useSavedBackgrounds}
|
||||
onChange={(e) =>
|
||||
setAppSettings((prev) => ({
|
||||
...prev,
|
||||
useSavedBackgrounds: (e.target as any).checked,
|
||||
}))
|
||||
}
|
||||
>
|
||||
Use saved backgrounds
|
||||
</Checkbox>
|
||||
<a
|
||||
style={{ display: 'flex', 'align-items': 'center', gap: '8px', cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { random } from './math';
|
||||
|
||||
export function sample<T>(list: T[]): T {
|
||||
if (list.length === 1) {
|
||||
return list[0];
|
||||
}
|
||||
return list[random(list.length - 1)];
|
||||
}
|
||||
@@ -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<Settings>;
|
||||
savedBackgrounds: SavedBackgrounds;
|
||||
onSaveBackground(): void;
|
||||
currentBackground: Accessor<Random | undefined>;
|
||||
};
|
||||
|
||||
const SettingsContext = createContext<SettingsContextType>({
|
||||
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<Settings>('start-page-app-settings', {
|
||||
showLocations: false,
|
||||
viewAsTable: false,
|
||||
useSavedBackgrounds: false,
|
||||
});
|
||||
|
||||
const [savedBackgrounds, setSavedBackgrounds] = createLocalStore<SavedBackgrounds>(
|
||||
'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 (
|
||||
<SettingsContext.Provider value={{ appSettings, setAppSettings }}>
|
||||
<SettingsContext.Provider
|
||||
value={{
|
||||
currentBackground,
|
||||
savedBackgrounds,
|
||||
onSaveBackground,
|
||||
appSettings,
|
||||
setAppSettings,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</SettingsContext.Provider>
|
||||
);
|
||||
|
||||
@@ -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<BackgroundStorage>(
|
||||
'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<Random | undefined>(() =>
|
||||
sample(backgrounds?.backgrounds ?? [])
|
||||
);
|
||||
|
||||
return { backgrounds, currentBackground };
|
||||
}
|
||||
@@ -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[];
|
||||
|
||||
Reference in New Issue
Block a user