This commit is contained in:
Shusui MOYATANI
2023-02-17 17:15:13 +09:00
commit 2aa85b3ed9
42 changed files with 16424 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
import { createSignal, createEffect, onMount, Accessor, Setter } from 'solid-js';
type GenericStorage<T> = {
getItem(key: string): T | null;
setItem(key: string, value: T): void;
};
export const createStorageWithSerializer = <T>(
storageProvider: () => Storage,
serializer: (v: T) => string,
deserializer: (v: string) => T | null,
): GenericStorage<T> => {
const storage: Storage = storageProvider();
return {
getItem(key: string): T | null {
const data = storage.getItem(key);
return data != null ? deserializer(data) : null;
},
setItem(key: string, value: T) {
const data = serializer(value);
storage.setItem(key, data);
},
};
};
export const createSignalWithStorage = <T>(
key: string,
initialValue: T,
storage: GenericStorage<T>,
): [Accessor<T>, Setter<T>] => {
const [loaded, setLoaded] = createSignal<boolean>(false);
const [value, setValue] = createSignal<T>(initialValue);
onMount(() => {
const data = storage.getItem(key);
// If there is no data, default value is used.
if (data != null) setValue(() => data);
setLoaded(true);
});
createEffect(() => {
if (loaded()) storage.setItem(key, value());
});
return [value, setValue];
};

View File

@@ -0,0 +1,52 @@
import type { Accessor } from 'solid-js';
import { createSignal, createEffect } from 'solid-js';
export type UseResizedImageProps = {
imageUrl: Accessor<string | undefined>;
width: number;
height: number;
encoderOption?: number;
};
const useResizedImage = ({
imageUrl,
width,
height,
encoderOption,
}: UseResizedImageProps): Accessor<string | undefined> => {
const [resizedImage, setResizedImage] = createSignal<string | undefined>(undefined);
createEffect(() => {
const sourceUrl = imageUrl();
if (sourceUrl == null) return;
const img = document.createElement('img');
img.addEventListener('load', () => {
const ratio = img.naturalWidth / img.naturalHeight;
const rw = height * ratio;
const rh = width / ratio;
const dw = Math.min(rw, width);
const dh = Math.min(rh, height);
const canvas = document.createElement('canvas');
canvas.height = dh;
canvas.width = dw;
const ctx = canvas.getContext('2d');
if (ctx == null) throw new Error('failed to obtain context');
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, dw, dh);
const dataUrl = canvas.toDataURL('image/jpeg', encoderOption);
setResizedImage(dataUrl);
});
img.src = sourceUrl;
});
return resizedImage;
};
export default useResizedImage;

View File

@@ -0,0 +1,61 @@
// const commands = ['openPostForm'] as const;
// type Commands = (typeof commands)[number];
import { createMemo, createEffect } from 'solid-js';
type Shortcut = { key: string; command: string };
const defaultShortcut: Shortcut[] = [
{ key: 'n', command: 'openPostForm' },
{ key: 'h', command: 'moveToPrevColumn' },
{ key: 'j', command: 'moveToNextItem' },
{ key: 'k', command: 'moveToPrevItem' },
{ key: 'l', command: 'moveToNextColumn' },
{ key: 'ArrowLeft', command: 'moveToPrevColumn' },
{ key: 'ArrowDown', command: 'moveToNextItem' },
{ key: 'ArrowUp', command: 'moveToPrevItem' },
{ key: 'ArrowRight', command: 'moveToNextColumn' },
{ key: 'f', command: 'like' },
{ key: 't', command: 'repost' },
{ key: 'r', command: 'openReplyForm' },
{ key: '?', command: 'openHelp' },
{ key: 'Enter', command: 'openItemDetail' },
{ key: 'Backspace', command: 'closeItemDetail' },
];
type UseShortcutKeysProps = {
shortcuts?: Shortcut[];
onShortcut: (shortcut: Shortcut) => void;
};
const createShortcutsMap = (shortcuts: Shortcut[]) => {
const map = new Map<string, Shortcut>();
shortcuts.forEach((shortcut) => {
map.set(shortcut.key, shortcut);
});
return map;
};
const useShortcutKeys = ({ shortcuts = defaultShortcut, onShortcut }: UseShortcutKeysProps) => {
const shortcutsMap = createShortcutsMap(shortcuts);
createEffect(() => {
const handleKeydown: JSX.EventHandler<Window, KeyboardEvent> = (ev) => {
console.log(ev);
if (ev.type !== 'keydown') return;
if (ev.target instanceof HTMLTextAreaElement || ev.target instanceof HTMLInputElement) return;
const shortcut = shortcutsMap.get(ev.key);
if (shortcut == null) return;
onShortcut(shortcut);
};
window.addEventListener('keydown', handleKeydown);
return () => window.removeEventListener('keydown', handleKeydown);
});
};
export default useShortcutKeys;