Skip to content

Commit

Permalink
add: images
Browse files Browse the repository at this point in the history
  • Loading branch information
Valexr committed Apr 14, 2024
1 parent abd6f99 commit e10fce6
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 73 deletions.
1 change: 1 addition & 0 deletions public/assets/photos.json

Large diffs are not rendered by default.

73 changes: 36 additions & 37 deletions src/App.svelte
Original file line number Diff line number Diff line change
@@ -1,38 +1,13 @@
<script lang="ts" context="module">
import Gh from "$lib/components/Gh.svelte";
import { time } from "$lib/data";
import { date, time, start, county } from "$lib/data";
</script>

<script lang="ts">
export let name: Name;
export let repository: Repository;
let start = "2013-04-09";
function county(date: string) {
const now = new Date();
const start = new Date(date);
return {
years() {
const elapsed = now.getTime() - start.getTime();
return new Date(elapsed).getFullYear() - 1970;
},
months() {
const elapsed = now.getMonth() - start.getMonth();
return (this.years() * 12 + elapsed) % 12;
},
days() {
const elapsed = now.getDate() - start.getDate();
const count = new Date(
now.getFullYear(),
now.getMonth() + 1,
0,
).getDate();
return (this.months() * count + elapsed) % count;
},
};
}
$start = "2013-04-09";
</script>

<svelte:head>
Expand All @@ -54,27 +29,51 @@
<input
type="date"
placeholder="Set start date"
bind:value={start}
bind:value={$start}
/>
</label>
</form>
<pre>
{JSON.stringify(county(start), null, 2)}
</pre>
<h3>{new Date().toLocaleDateString("ru")}</h3>
<h2>{county(start).years()} years</h2>
<h2>{county(start).months()} months</h2>
<h2>{county(start).days()} days</h2>
<h3>{$time}</h3>
<h2>{$date}</h2>
<ul>
<li id="years">{$county.years}</li>
<li id="months">{$county.months}</li>
<li id="days">{$county.days}</li>
</ul>
<h2>{$time}</h2>
</main>

<footer>
<p>Footer</p>
<p>{new Date().getFullYear()} © County</p>
</footer>

<style>
@import "app.css";
main {
padding: 1em;
}
input {
font-size: 1.5em;
}
ul {
list-style: none;
padding: 0;
margin: 0;
font-size: 25vw;
font-weight: bold;
display: flex;
justify-content: center;
gap: 5vw;
margin-bottom: 0.5em;
}
ul li {
position: relative;
}
ul li::after {
content: attr(id);
font-size: 15%;
font-weight: normal;
position: absolute;
inset: 0;
top: 90%;
}
</style>
28 changes: 28 additions & 0 deletions src/app.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,32 @@ type Name = string
type Repository = {
type: string
url: string
}

type Size = {
width: number;
height: number;
}

/**
* `https://www.picsum.photos` API response schema
*/
type ImageSchema = {
id: number;
alt: string;
src: string;
width: number;
height: number;
}

/**
* Common Image interface.
*/
type Slide = {
id?: string | number;
src?: string;
alt?: string;
width?: string | number;
height?: string | number;
[key: string]: unknown;
}
30 changes: 30 additions & 0 deletions src/lib/cacheable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { writable } from 'svelte/store'

export function cacheable<T>(key: string, initial: T, local?: boolean) {
const { subscribe, set } = writable<T>(initial)
const store = typeof window === 'object' ? local ? localStorage : sessionStorage : undefined
const item = store?.getItem(key) || ''

try {
initial = item ? JSON.parse(item) : initial
} catch {
initial = initial
}

sync(initial)

function sync(value?: T) {
store?.setItem(key, JSON.stringify(value))
initial = value as T
return set(initial)
}

return {
get: () => initial,
set: (value: T) => sync(value),
update: (fn: (value: T) => any) => sync(fn(initial)),
remove: () => store?.removeItem(key),
clear: () => store?.clear(),
subscribe
}
}
52 changes: 16 additions & 36 deletions src/lib/data.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,22 @@
import { readable, writable } from 'svelte/store';
import { derived, readable } from 'svelte/store';
import { cacheable } from './cacheable';

export const timer = createTimer()
export const start = cacheable('startDate', '', true)

function createTimer() {
const { subscribe, set, update } = writable(new Date())
set(new Date());
export const county = derived(start, ($start, set) => {
const now = new Date();
const start = new Date($start);
const elapsedYears = now.getTime() - start.getTime();
const elapsedMonth = now.getMonth() - start.getMonth();
const elapsedDays = now.getDate() - start.getDate();
const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0,).getDate();
const years = new Date(elapsedYears).getFullYear() - 1970
const months = (years * 12 + elapsedMonth) % 12
const days = (months * daysInMonth + elapsedDays) % daysInMonth

const interval = setInterval(() => {
const now = new Date();
// const elapsed = Number(now) - Number(start);
set(new Date());
}, 1000);
set({ years, months, days })

return { subscribe, set, update }

function convertMS(ms: number) {
let y, mt, w, d, h, m, s;
s = Math.floor(ms / 1000);
m = Math.floor(s / 60);
s = s % 60;
h = Math.floor(m / 60);
m = m % 60;
d = Math.floor(h / 24);
h = h % 24;
w = Math.floor(d / 7);
d = d % 7;
mt = Math.floor(w / 4);
w = w % 4;
y = Math.floor(mt / 12);
y = y % 12;
return { y, mt, w, d, h, m, s };
}
}
}, { years: 0, months: 0, days: 0 })

export const time = readable(new Date().toLocaleTimeString('ru'), (set) => {
const interval = setInterval(() => {
Expand All @@ -42,10 +27,5 @@ export const time = readable(new Date().toLocaleTimeString('ru'), (set) => {
return () => clearInterval(interval);
});

export const ticktock = readable('tick', (set, update) => {
const interval = setInterval(() => {
update((sound) => (sound === 'tick' ? 'tock' : 'tick'));
}, 1000);
export const date = readable(new Date().toLocaleDateString("ru"))

return () => clearInterval(interval);
});
52 changes: 52 additions & 0 deletions src/lib/images.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
export const getPhotos = async ({
limit = 5,
width = window.innerWidth,
height = window.innerHeight,
}) => {
try {
return await getImages(limit, { width, height });
} catch (error) {
console.error(`Could not fetch photos: ${error}`);
}
};

async function getImages(
limit: number = 9,
size = { width: window.innerWidth, height: window.innerHeight }
): Promise<Slide[]> {
const url = 'https://raw.githubusercontent.com/Valexr/county/master/assets/photos.json';
const indexes = Array.from({ length: limit }, () => Math.floor(Math.random() * 24644));
const res = await fetch(url);
const photos = await res.json();

return photos.reduce(
(acc: ImageSchema[], [src, aspectRatio, author]: [string, number, string], id: number) => {
if (indexes.includes(id)) {
const source = { width: size.height * (aspectRatio / 10), height: size.height };
const max = { width: size.width, height: size.height };
const query = `?w=${ratio(applyRatio(source, max).width)}`;

acc.push({
id,
src: `https://images.unsplash.com/photo-${src}${query}`,
alt: `Image by ${author} from Unsplash`,
...applyRatio(source, max),
});
}
return acc;
},
[]
);

function ratio(size: number) {
return size * devicePixelRatio;
}
}

const applyRatio = (src: Size, size: Size): Size => {
const ratio = Math.min(size.width / src.width, size.height, src.height);
return {
width: Math.round(src.width * ratio),
height: Math.round(src.height * ratio),
};
};

0 comments on commit e10fce6

Please sign in to comment.