Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend rewrite #134

Draft
wants to merge 227 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
227 commits
Select commit Hold shift + click to select a range
ca09199
Start frontend rewrite
Mesoptier Aug 19, 2020
e07cc82
Working build
Mesoptier Aug 19, 2020
cd97a3b
Fetch and render samples
Mesoptier Aug 19, 2020
cb1376b
Fast UI updates on query change
Mesoptier Aug 19, 2020
c7041c6
Remove unused import
Mesoptier Aug 19, 2020
adf733a
Move useTextMeasurer
Mesoptier Aug 19, 2020
559b437
Run Prettier
Mesoptier Aug 19, 2020
4f77c74
Synchronize sample dimensions between TS/SCSS
Mesoptier Aug 19, 2020
d53129a
Rename variables -> sync-variables
Mesoptier Aug 19, 2020
ba60f61
Make sample items stylable
Mesoptier Aug 19, 2020
82db9b8
Log when sample is clicked
Mesoptier Aug 19, 2020
3ed0aa3
Add fonts and more styling
Mesoptier Aug 19, 2020
6f1bd09
Size sample container to body width
Mesoptier Aug 19, 2020
7421aaf
Add margin to sample list
Mesoptier Aug 19, 2020
ad42eb1
Sort samples
Mesoptier Aug 19, 2020
39cc4fd
Fix some responsiveness on mobile devices
Mesoptier Aug 19, 2020
3c6ecd6
Remove tabIndex on sample container
Mesoptier Aug 19, 2020
f8de8d5
Style header and add search context
Mesoptier Aug 19, 2020
b4ad030
Further styles header and search bar
Mesoptier Aug 19, 2020
81b9328
Icon button
Mesoptier Aug 19, 2020
61ba347
Resize icon button
Mesoptier Aug 20, 2020
c8e9bd1
Remove completed TODO
Mesoptier Aug 20, 2020
d35ff55
Rework header layout
Mesoptier Aug 20, 2020
c80527a
Refactor fetch/filter samples hooks to SampleList.tsx
Mesoptier Aug 20, 2020
306e09d
Refactor SearchContext
Mesoptier Aug 20, 2020
40e1422
Add Player
Mesoptier Aug 20, 2020
f0eb3ea
Move theme variables
Mesoptier Aug 20, 2020
295bd23
Add usePlayer hook and playing styles
Mesoptier Aug 20, 2020
0a4bc88
Add play/ended events
Mesoptier Aug 21, 2020
d45b022
Show progress on samples
Mesoptier Aug 21, 2020
24d896a
Remeasure texts once fonts are loaded
Mesoptier Aug 21, 2020
b680b60
Update sample widths when textmeasurer is updated
Mesoptier Aug 21, 2020
d85763d
Speed-up sample search a bit
Mesoptier Aug 21, 2020
8e15b55
Prevent selecting sample text
Mesoptier Aug 21, 2020
4fe1939
Add sample visualization
Mesoptier Aug 21, 2020
a8f1d6a
Add 'Space' shortcut for stopping all playing samples
Mesoptier Aug 22, 2020
81ab33b
Make SampleItem a button
Mesoptier Sep 2, 2020
c86ce7e
Add CSS variable for SampleItem progress color
Mesoptier Sep 2, 2020
2b16a48
Fix formatting of colors
Mesoptier Sep 2, 2020
ee62bcd
Adjust Space behavior
Mesoptier Sep 2, 2020
3ed2fd1
Use device pixel ratio for high DPI visualization
Mesoptier Sep 5, 2020
8860463
Begin adding Storybook
Mesoptier Sep 16, 2020
7c180d9
Merge branch 'master' into frontend-rewrite
Mesoptier Jun 9, 2021
b0b9947
Revert "Begin adding Storybook"
Mesoptier Jun 9, 2021
bbc55d6
Update package.json after removing Storybook
Mesoptier Jun 9, 2021
99dff94
Remove weird @types/ entry
Mesoptier Jun 9, 2021
209e526
Update yarn.lock
Mesoptier Jun 9, 2021
455b96c
Upgrade dependencies
Mesoptier Jun 9, 2021
fcc9c9b
Upgrade devDependencies
Mesoptier Jun 9, 2021
9b5ef5c
Add ARIA role for sample rows
Mesoptier Jun 11, 2021
67e1669
Speed-up sample filtering
Mesoptier Jun 11, 2021
05f5e2a
Add start:production script
Mesoptier Jun 11, 2021
970b4e1
Downgrade Parcel to v1, since v2 does not play nice with react-virtua…
Mesoptier Jun 11, 2021
0ce5f52
Extract VisualizeAnalyserNode to separate file
Mesoptier Jun 11, 2021
07df47b
Add recommended tsconfig
Mesoptier Jun 11, 2021
06f8aa0
Add script for testing typescript types
Mesoptier Jun 11, 2021
3e01912
Fix types + imports + formatting
Mesoptier Jun 11, 2021
9b6b8ee
Add spam & loop behaviour
Mesoptier Jun 11, 2021
c646dd1
Remove TODO
Mesoptier Jun 11, 2021
ddd22ad
Rename PlayOptions -> TogglePlayOptions
Mesoptier Jun 11, 2021
40a2008
Document Player methods
Mesoptier Jun 11, 2021
ff9b9fb
Show progress bar for all playing instances of a sample
Mesoptier Jun 11, 2021
02fc5a5
Add box-sizing reset
Mesoptier Jun 11, 2021
bf8d7e6
Add no-results message in SampleList
Mesoptier Jun 11, 2021
527ccfe
Use theme colors throughout the application
Mesoptier Jun 11, 2021
97116dc
Get VisualizeAnalyserNode styles from CSS custom properties
Mesoptier Jun 11, 2021
c5b28b5
Give search input `type="search"`
Mesoptier Jun 12, 2021
c6deceb
Add buttons to header
Mesoptier Jun 12, 2021
ec755f3
Add context menu
Mesoptier Jun 12, 2021
130741d
Show Play/Stop depending on playing state
Mesoptier Jun 12, 2021
bf6e69a
Add autoFocus on first context menu item
Mesoptier Jun 14, 2021
6194826
Get URLs from config
Mesoptier Jun 14, 2021
cdc10f1
Implement "Copy URL" button
Mesoptier Jun 14, 2021
1621c00
Add Download option
Mesoptier Jun 14, 2021
bb35cff
Close the context menu when the user presses the Escape key
Mesoptier Jun 14, 2021
a9dfff2
Remove extra char
Mesoptier Jun 16, 2021
c5dc0a5
Add MVP modals
Mesoptier Jun 16, 2021
405c3cf
Extract common useKeydown hook
Mesoptier Jun 16, 2021
af6013e
Extract search context hooks into a separate hook
Mesoptier Jun 16, 2021
cd065cd
Move default theme
Mesoptier Jun 16, 2021
cb63aaa
Add MVP theming
Mesoptier Jun 16, 2021
963ad7c
Add MVP theme selector
Mesoptier Jun 16, 2021
ab0b7c1
Optimize imports
Mesoptier Jun 16, 2021
7b273f7
Start adding theme thumbs
Mesoptier Jun 17, 2021
3c1c2d5
Move SampleList component
Mesoptier Jun 21, 2021
4e3d08a
Move computeLayout to separate file
Mesoptier Jun 21, 2021
95d878f
Remove unused .parcelrc
Mesoptier Jun 21, 2021
09f62b2
Add Jest for testing
Mesoptier Jun 21, 2021
c029006
Add tests for computeLayout
Mesoptier Jun 21, 2021
4d0c723
Fix computeLayout based on tests
Mesoptier Jun 21, 2021
04b9972
Add more computeLayout tests
Mesoptier Jun 21, 2021
0ece4ca
Make differently colored rows work again in Cirkeltrek theme
Mesoptier Oct 19, 2021
215e85a
Upgrade TypeScript and Popper
Mesoptier Oct 27, 2021
321f0d1
Add arrow key navigation through samples list
Mesoptier Nov 2, 2021
b0b670c
Move sample progress to separate component, in order to reduce React …
Mesoptier Nov 2, 2021
1260b3f
Use SVG for rendering sample progress, since SVG properly renders usi…
Mesoptier Nov 2, 2021
a3f3356
Keep instance objects, so additional information can be kept alongsid…
Mesoptier Nov 2, 2021
16ab539
Extrapolate audio progress in browsers with low resolution timing
Mesoptier Nov 2, 2021
e20f903
Only start extrapolating after the first real time update
Mesoptier Nov 2, 2021
28066ce
Tweak sample styles
Mesoptier Nov 13, 2021
bbae5cb
Use regular SCSS variables for border-radius
Mesoptier Nov 13, 2021
0233e41
Remove border-radius CSS variables
Mesoptier Nov 13, 2021
a91e48e
Tweak contextmenu styles
Mesoptier Nov 13, 2021
7d836ce
Upgrade to Font Awesome v6
Mesoptier Nov 13, 2021
a9e093d
Add icons in context menu
Mesoptier Nov 14, 2021
d260c79
Add contextmenu open animation
Mesoptier Nov 14, 2021
2e31a8f
Tweak header styles
Mesoptier Nov 18, 2021
c163dca
Fix search placeholder color
Mesoptier Nov 18, 2021
25c1130
Reduce overscan row count from 10 to 3
Mesoptier Nov 18, 2021
29f1c0d
Use modern SASS modules imports
Mesoptier Nov 18, 2021
b576ac2
Move component style to separate directory
Mesoptier Nov 18, 2021
cbbfc99
Tweak visualizer line width
Mesoptier Nov 27, 2021
9d1fdc1
Add 'default-classic' theme
Mesoptier Oct 18, 2022
08470fc
Add build script
Mesoptier Oct 18, 2022
84d7d57
Upgrade Yarn
Mesoptier Oct 19, 2022
f65c80e
Yarn install (+ temp hack for deasync)
Mesoptier Oct 19, 2022
f2bfa83
Upgrade Parcel + other dev dependencies
Mesoptier Oct 19, 2022
5f0f12e
Upgrade React
Mesoptier Oct 19, 2022
2136dfc
Replace react-virtualized with @tanstack/react-virtual
Mesoptier Oct 28, 2022
a36b773
Enable React strict mode
Mesoptier Oct 28, 2022
5704db6
Use useDeferredValue to defer samples filtering to user input
Mesoptier Oct 28, 2022
fc7c08d
Play samples from URI
Mesoptier Oct 28, 2022
42358bf
Show "Audio Blocked" overlay + try again when clicked
Mesoptier Oct 28, 2022
62b3698
Fix z-index for context menu overlay
Mesoptier Oct 29, 2022
a09e4e9
Disable pointer-events for presentational element overlaying sample text
Mesoptier Oct 29, 2022
321e22c
Fix z-index for modals
Mesoptier Oct 29, 2022
a25cf39
Merge remote-tracking branch 'origin/master' into frontend-rewrite
villermen Nov 8, 2022
17b13a3
Whoops I did the OGV test thing again
villermen Nov 8, 2022
15d65ae
Use Yarn in build.sh and package scripts
villermen Nov 13, 2022
be8d767
Merge branch 'frontend-rewrite' of github.com:team-thyme/soundboard i…
Mesoptier Nov 19, 2022
67a6def
Format code
Mesoptier Nov 19, 2022
4b4f20a
Fix type error
Mesoptier Nov 19, 2022
6a385d5
Type OGV library (at least the parts we use)
Mesoptier Nov 19, 2022
dcbfe80
Only load OGV library if needed
Mesoptier Nov 19, 2022
163cea9
Add GitHub workflow for frontend tests
Mesoptier Nov 19, 2022
9bf62b5
Merge branch 'master' into frontend-rewrite
Mesoptier Nov 19, 2022
e652cce
Add TODO
Mesoptier Nov 19, 2022
040d03f
Clamp currentTime (since OGVPlayer sometimes returns negative numbers)
Mesoptier Nov 19, 2022
02882f9
Fix OGV.js types (kinda)
Mesoptier Nov 19, 2022
c3ea7e2
Use OGVCompat from global namespace instead of import
Mesoptier Nov 19, 2022
853fa1b
Preload OGVPlayer if it's going to be needed
Mesoptier Nov 19, 2022
484b64e
Fix issue where measureWidth returns undefined in some cases
Mesoptier Nov 19, 2022
9f286e1
Make properties readonly, since they never need to be replaced
Mesoptier Nov 19, 2022
484fa6f
Check that context could be created
Mesoptier Nov 19, 2022
42d1463
Remove @ts-ignore for document.fonts API that has since been typed
Mesoptier Nov 19, 2022
44f6597
Add doc comments
Mesoptier Nov 19, 2022
7e24e42
Replace @ts-ignore with @ts-expect-error
Mesoptier Nov 19, 2022
ce8df86
Start using explicit SampleKey instead of string + document `playing`…
Mesoptier Nov 19, 2022
195c7ec
Update workflow
Mesoptier Nov 19, 2022
f515997
Break test on purpose to check GitHub workflow
Mesoptier Nov 19, 2022
0857f64
Revert "Break test on purpose to check GitHub workflow"
Mesoptier Nov 19, 2022
eb2f87b
Add prettier formatting checker
Mesoptier Nov 19, 2022
a347329
Run prettier
Mesoptier Nov 19, 2022
1753b55
Merge branch 'master' into frontend-rewrite
Mesoptier Nov 19, 2022
ea9f88b
Remove old .yarnrc file
Mesoptier Nov 23, 2022
30fdf3c
yarn dedupe
Mesoptier Nov 23, 2022
d537040
Enable yarn plug-and-play
Mesoptier Nov 23, 2022
c5acaa7
Revert "Enable yarn plug-and-play"
Mesoptier Nov 23, 2022
b7a8965
Fix types
Mesoptier Nov 23, 2022
7162d30
Remove -- argument from build-production script
villermen Dec 13, 2022
6ded54b
Upgrade to Yarn 4.x
Mesoptier Feb 26, 2024
5cd4fce
Upgrade build dependencies
Mesoptier Feb 26, 2024
4899219
Upgrade test dependencies
Mesoptier Feb 26, 2024
2e51024
Upgrade prettier
Mesoptier Feb 26, 2024
d54ac38
Upgrade typings
Mesoptier Feb 26, 2024
ef36b8f
Run npx update-browserslist-db@latest
Mesoptier Feb 26, 2024
a8f3031
Use Floating UI to position ContextMenu
Mesoptier Feb 26, 2024
9bacbcf
Use Floating UI to position ModalIconButton modals
Mesoptier Feb 26, 2024
e0f0756
Remove Popper
Mesoptier Feb 26, 2024
ef7e40c
yarn dedupe
Mesoptier Feb 26, 2024
d9b5c10
Update workflow
Mesoptier Feb 26, 2024
679fbd8
Upgrade @tanstack/react-virtual
Mesoptier Feb 27, 2024
a14d563
Make use of useSyncExternalStore in usePlayer
Mesoptier Feb 27, 2024
fa2a3d1
Extract getProgress function
Mesoptier Feb 27, 2024
cd66ece
Clean up usePlayerProgress
Mesoptier Feb 27, 2024
2ad2467
Extract sample filtering to separate Search class
Mesoptier Feb 27, 2024
b5ff95b
Clean up normalization in Search class
Mesoptier Feb 27, 2024
662d30f
Abort fetching samples when effect is re-run
Mesoptier Feb 27, 2024
1d74297
Fix usePlaySamplesFromURI if no sample matches
Mesoptier Feb 27, 2024
7214e5b
Scroll to first sample linked in URL
Mesoptier Feb 27, 2024
2a7f797
Refactor ContextMenu
Mesoptier Feb 28, 2024
54da9fa
Improve accessibility of ContextMenu
Mesoptier Feb 28, 2024
eb22f6a
Move sample list navigation to separate hook
Mesoptier Feb 28, 2024
6d3aa20
Fix scroll padding so focus isn't behind header
Mesoptier Feb 28, 2024
7959b7a
Move fetching of samples to app root
Mesoptier Feb 29, 2024
fcc3032
Use props/imperative handle instead of having a bunch of contexts
Mesoptier Feb 29, 2024
874d2f9
Play random sample when pressing Enter in search field
Mesoptier Feb 29, 2024
72bf281
Persist preferences
Mesoptier Mar 1, 2024
46cfc0c
Apply volume to the gain node
Mesoptier Mar 1, 2024
e22fd78
Upgrade font awesome dependencies
Mesoptier Mar 1, 2024
df889b1
Quadratic scaling to make the volume slider more usable
Mesoptier Mar 1, 2024
116b8c3
Use TypedEventTarget for Player
Mesoptier Mar 5, 2024
54b4267
Use named functions, instead of anonymous
Mesoptier Mar 5, 2024
a7181a1
Fix header responsiveness
Mesoptier Mar 5, 2024
cd4f89f
Merge branch 'master' into frontend-rewrite
Mesoptier Mar 5, 2024
dc37c05
Add samples to theme thumbs
Mesoptier Mar 6, 2024
59a16a6
Fix cirkeltrek colors showing up in other theme thumbs
Mesoptier Mar 6, 2024
25f912c
Move ThemeThumb to separate file
Mesoptier Mar 6, 2024
1500970
Remove unused useKeydown hook
Mesoptier Mar 6, 2024
4994ce6
Improve ThemeSelect styling
Mesoptier Mar 6, 2024
78d1e83
Use native focus style
Mesoptier Mar 6, 2024
bd65ba6
Calculate header height properties at compile time
Mesoptier Mar 6, 2024
80b417d
Make request sample button link to contribute URL
Mesoptier Mar 6, 2024
aa7a56c
Combine preferences into a single modal, and style it
Mesoptier Mar 6, 2024
5a07c4e
Move preference sections to separate files
Mesoptier Mar 6, 2024
b621f2a
Optimize imports
Mesoptier Mar 6, 2024
71070bc
Move ContextMenu components to floating/ContextMenu/
Mesoptier Mar 6, 2024
267b30d
Refactor Modal component
Mesoptier Mar 6, 2024
4f24d28
Adjust specificity of --playing sample in thumb
Mesoptier Mar 6, 2024
4493510
Apply simple styling for BlockedOverlay
Mesoptier Mar 6, 2024
3c4e5b3
Improve ContextMenu placement
Mesoptier Mar 6, 2024
203e1b4
Disable 'Copy URL' if clipboard API is unavailable
Mesoptier Mar 6, 2024
9bf417f
Use URL constructor instead of an anchor element
Mesoptier Mar 6, 2024
46c7203
Remove TODO
Mesoptier Mar 6, 2024
b3dee72
Play version sample
Mesoptier Mar 6, 2024
cd631c7
Simplify ContextMenu modules
Mesoptier Mar 8, 2024
803b925
Improve accessibility of sample list
Mesoptier Mar 8, 2024
2031e7d
Add roles for header components
Mesoptier Mar 8, 2024
d313823
Extract getTransformOrigin helpers to separate file
Mesoptier Mar 8, 2024
529b079
Add appear animation for modals
Mesoptier Mar 8, 2024
1f94c70
Tweak sample list layout styles
Mesoptier Mar 9, 2024
7044193
Proper OR implementation for searches
Mesoptier Mar 9, 2024
3d3051e
Remove console.log
Mesoptier Mar 9, 2024
ce31b0c
Play random sample matching the ID
Mesoptier Mar 9, 2024
018eea6
Improve playing from URL
Mesoptier Mar 9, 2024
5f60a96
Rename id -> hash
Mesoptier Mar 9, 2024
3569c18
Set media metadata when sample is playing
Mesoptier Mar 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions .eslintrc.json

This file was deleted.

21 changes: 21 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: CI
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'yarn'
- name: Install modules
run: yarn install --immutable
- name: Run Jest tests
run: yarn test
- name: Run TypeScript type checker
run: yarn test:types
- name: Run Prettier formatting checker
run: yarn test:prettier
12 changes: 11 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,19 @@
/.npmrc
/npm-debug.log
/public/
/.cache/
/.parcel-cache/

# Backend
# Yarn
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

# Composer
/vendor/
/.slim-cache/

Expand Down
4 changes: 2 additions & 2 deletions .parcelrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"extends": ["@parcel/config-default"],
"reporters": ["...", "parcel-reporter-static-files-copy"]
"extends": ["@parcel/config-default"],
"reporters": ["...", "parcel-reporter-static-files-copy"],
}
5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"singleQuote": true,
"jsxSingleQuote": false,
"tabWidth": 4
}
5 changes: 5 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
compressionLevel: mixed

enableGlobalCache: false

nodeLinker: node-modules
10 changes: 5 additions & 5 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ if [ "$APP_ENV" == 'dev' ]; then
fi

composer install
npm install
yarn install

if [ "$1" == '--serve' ]; then
composer run dev-server & \
npm run dev-server
yarn run dev-server
exit 0
fi

npm run build
yarn run build
exit 0
fi

echo "Building soundboard in production mode..."
rm -rf .slim-cache
composer install --no-dev --optimize-autoloader
npm install
BASE_URL=${BASE_URL:=/} npm run build-production
yarn install
BASE_URL=${BASE_URL:=/} yarn run build-production
exit 0
103 changes: 103 additions & 0 deletions frontend/@types/ogv.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// TODO: Finish these types + create a PR on the ogv.js repository?

declare module 'ogv' {
type OGVVersion = string;

interface OGVCompat {
supported(component: 'OGVDecoder' | 'OGVPlayer'): boolean;
}

interface OGVLoaderBase {
base: string | undefined;
}

interface OGVLoaderWeb extends OGVLoaderBase {}

class OGVMediaError {
code: number;
message: string;
}

class OGVJSElement extends HTMLElement {}

interface OGVPlayerOptions {
base?: string;
worker?: unknown;
webGL?: unknown;
forceWebGL?: unknown;
stream?: unknown;

audioContext?: AudioContext;
audioDestination?: AudioNode;
audioBackendFactory?: unknown;

// Experimental pthreads multithreading mode, if built.
threading?: unknown;
// Experimental SIMD mode, if built.
simd?: unknown;

debug?: boolean;
debugFilter?: RegExp;
}

class OGVPlayer extends OGVJSElement {
constructor(options: OGVPlayerOptions);

src: string;
readonly buffered: TimeRanges;
readonly seekable: TimeRanges;
currentTime: number;
readonly duration: number;
readonly paused: boolean;
readonly ended: boolean;
readonly seeking: boolean;
muted: boolean;
poster: string;
readonly videoWidth: number;
readonly ogvjsVideoFrameRate: number;
readonly ogvjsAudioChannels: number;
readonly ogvjsAudioSampleRate: number;
width: number;
height: number;
autoplay: boolean;
controls: boolean;
loop: boolean;
crossOrigin: string | null;
readonly currentSrc: string | null;
readonly defaultMuted: boolean;
readonly defaultPlaybackRate: number;
readonly error: OGVMediaError | null;
preload: string;
readonly readyState: number;
readonly networkState: number;
playbackRate: number;
readonly played: TimeRanges;
volume: number;

load(): void;
canPlayType(type: string): '' | 'probably' | 'maybe';
play(): void;
pause(): void;
fastSeek(time: number): void;
}

export const OGVCompat: OGVCompat;
export const OGVVersion: OGVVersion;
export const OGVLoader: OGVLoaderWeb;
export { OGVPlayer };
}

declare module 'ogv/dist/ogv-support' {
// TODO: This file should just export OGVCompat and OGVVersion → file issue at ogv.js?
declare global {
const OGVCompat: OGVCompat;
const OGVVersion: OGVVersion;
}
}

declare module 'ogv/dist/ogv-version' {
// TODO: This file should just export OGVVersion → file issue at ogv.js?
declare global {
const OGVVersion: OGVVersion;
}
}
35 changes: 35 additions & 0 deletions frontend/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { config } from './config';

export type SampleKey = string;

export interface Sample {
// from server
path: string;
name: string;
hash: string;
mtime: number;
categories: string[];

// added on client
key: SampleKey;
url: string;
}

export async function fetchSamples(signal?: AbortSignal): Promise<Sample[]> {
const url = `${config.apiBaseUrl}/samples`;

// TODO: Remove 'force-cache'
const res = await fetch(url, { cache: 'force-cache', signal });
if (res.status !== 200) {
throw new Error(`Server replied with ${res.status} ${res.statusText}`);
}

const data = await res.json();
return data.samples.map((sampleData: any) => ({
...sampleData,
hash: sampleData.id,
mtime: sampleData.mtime * 1000,
key: sampleData.path,
url: `${url}/${sampleData.path}`,
}));
}
74 changes: 74 additions & 0 deletions frontend/components/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useEffect, useMemo, useRef, useState } from 'react';

import { fetchSamples, type Sample } from '../api';
import { usePreference } from '../helpers/preferences';
import { Search } from '../helpers/Search';
import { sortSamples } from '../helpers/sortSamples';
import { BlockedOverlay } from './BlockedOverlay';

import { Header } from './Header';
import { PlayerContextProvider } from './PlayerContext';
import {
SampleList,
type SampleListImperativeHandle,
} from './SampleList/SampleList';

function useSamples(): Sample[] {
const [samples, setSamples] = useState<Sample[]>([]);

useEffect(() => {
async function handleFetchSamples(signal: AbortSignal) {
try {
const samples = await fetchSamples(signal);
sortSamples(samples);
setSamples(samples);
} catch (error) {
if (
error instanceof DOMException &&
error.name === 'AbortError'
) {
// Fetch was aborted
return;
}
throw error;
}
}

const controller = new AbortController();
void handleFetchSamples(controller.signal);
return () => controller.abort();
}, []);

return samples;
}

export default function App() {
const [theme] = usePreference('theme');
useEffect(() => {
document.body.setAttribute('data-theme', theme);
}, [theme]);

const [query, setQuery] = useState('');
const samples = useSamples();

const sampleListRef = useRef<SampleListImperativeHandle>(null);

const search = useMemo(() => new Search(samples), [samples]);
const filteredSamples = useMemo(
() => search.filter(query),
[search, query],
);

return (
<PlayerContextProvider
samples={samples}
filteredSamples={filteredSamples}
sampleListRef={sampleListRef}
search={search}
>
<BlockedOverlay />
<Header query={query} onQueryChange={setQuery} />
<SampleList ref={sampleListRef} samples={filteredSamples} />
</PlayerContextProvider>
);
}
49 changes: 49 additions & 0 deletions frontend/components/BlockedOverlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useEffect, useState } from 'react';

import { player } from '../helpers/Player';

export function BlockedOverlay(): JSX.Element | null {
const [isBlocked, setBlocked] = useState(false);

useEffect(() => {
function handleBlocked() {
setBlocked(true);
}

player.addEventListener('blocked', handleBlocked);
return () => {
player.removeEventListener('blocked', handleBlocked);
};
}, []);

useEffect(() => {
if (!isBlocked) {
return;
}

function handleInteraction() {
setBlocked(false);
player.playBlockedSamples();
}

window.addEventListener('click', handleInteraction);
window.addEventListener('keypress', handleInteraction);
return () => {
window.removeEventListener('click', handleInteraction);
window.removeEventListener('keypress', handleInteraction);
};
}, [isBlocked]);

if (!isBlocked) {
return null;
}

return (
<div className="BlockedOverlay">
<div className="BlockedOverlay__content">
<h1>Autoplay Blocked</h1>
<p>Click or press any key to continue</p>
</div>
</div>
);
}
Loading
Loading