mirror of https://github.com/gabehf/Koito.git
parent
7ff317756f
commit
3250a4ec3f
@ -1,14 +1,14 @@
|
|||||||
# v0.0.2
|
# v0.0.3
|
||||||
## Features
|
## Features
|
||||||
- Configurable CORS policy via KOITO_CORS_ALLOWED_ORIGINS
|
- Delete listens from the UI
|
||||||
- A baseline mobile UI
|
|
||||||
|
|
||||||
## Enhancements
|
## Enhancements
|
||||||
- The import source is now saved as the client for the imported listen.
|
- Better behaved mobile UI
|
||||||
|
- Search now returns 8 items per category instead of 5
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
- Account update form now works on enter key
|
- Many mobile UI fixes
|
||||||
|
|
||||||
## Updates
|
## Updates
|
||||||
- Non-sensitive query parameters are logged with requests
|
- Refuses a config that changes the MusicBrainz rate limit while using the official MusicBrainz URL
|
||||||
- Koito version number is embedded through tags
|
- Warns when enabling ListenBrainz relay with missing configuration
|
||||||
@ -1,87 +0,0 @@
|
|||||||
# Welcome to React Router!
|
|
||||||
|
|
||||||
A modern, production-ready template for building full-stack React applications using React Router.
|
|
||||||
|
|
||||||
[](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- 🚀 Server-side rendering
|
|
||||||
- ⚡️ Hot Module Replacement (HMR)
|
|
||||||
- 📦 Asset bundling and optimization
|
|
||||||
- 🔄 Data loading and mutations
|
|
||||||
- 🔒 TypeScript by default
|
|
||||||
- 🎉 TailwindCSS for styling
|
|
||||||
- 📖 [React Router docs](https://reactrouter.com/)
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
Install the dependencies:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
### Development
|
|
||||||
|
|
||||||
Start the development server with HMR:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Your application will be available at `http://localhost:5173`.
|
|
||||||
|
|
||||||
## Building for Production
|
|
||||||
|
|
||||||
Create a production build:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Deployment
|
|
||||||
|
|
||||||
### Docker Deployment
|
|
||||||
|
|
||||||
To build and run using Docker:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker build -t my-app .
|
|
||||||
|
|
||||||
# Run the container
|
|
||||||
docker run -p 3000:3000 my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
The containerized application can be deployed to any platform that supports Docker, including:
|
|
||||||
|
|
||||||
- AWS ECS
|
|
||||||
- Google Cloud Run
|
|
||||||
- Azure Container Apps
|
|
||||||
- Digital Ocean App Platform
|
|
||||||
- Fly.io
|
|
||||||
- Railway
|
|
||||||
|
|
||||||
### DIY Deployment
|
|
||||||
|
|
||||||
If you're familiar with deploying Node applications, the built-in app server is production-ready.
|
|
||||||
|
|
||||||
Make sure to deploy the output of `npm run build`
|
|
||||||
|
|
||||||
```
|
|
||||||
├── package.json
|
|
||||||
├── package-lock.json (or pnpm-lock.yaml, or bun.lockb)
|
|
||||||
├── build/
|
|
||||||
│ ├── client/ # Static assets
|
|
||||||
│ └── server/ # Server-side code
|
|
||||||
```
|
|
||||||
|
|
||||||
## Styling
|
|
||||||
|
|
||||||
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Built with ❤️ using React Router.
|
|
||||||
@ -1,48 +1,64 @@
|
|||||||
import React, { type PropsWithChildren, useState } from 'react';
|
import React, { type PropsWithChildren, useEffect, useState } from 'react';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
inner: React.ReactNode
|
inner: React.ReactNode
|
||||||
position: string
|
position: string
|
||||||
space: number
|
space: number
|
||||||
extraClasses?: string
|
extraClasses?: string
|
||||||
hint?: string
|
hint?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Popup({ inner, position, space, extraClasses, children }: PropsWithChildren<Props>) {
|
export default function Popup({ inner, position, space, extraClasses, children }: PropsWithChildren<Props>) {
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
const [showPopup, setShowPopup] = useState(true);
|
||||||
|
|
||||||
let positionClasses
|
useEffect(() => {
|
||||||
let spaceCSS = {}
|
const mediaQuery = window.matchMedia('(min-width: 640px)');
|
||||||
if (position == "top") {
|
|
||||||
positionClasses = `top-${space} -bottom-2 -translate-y-1/2 -translate-x-1/2`
|
const handleChange = (e: MediaQueryListEvent) => {
|
||||||
} else if (position == "right") {
|
setShowPopup(e.matches);
|
||||||
positionClasses = `bottom-1 -translate-x-1/2`
|
};
|
||||||
spaceCSS = {left: 70 + space}
|
|
||||||
|
setShowPopup(mediaQuery.matches);
|
||||||
|
|
||||||
|
mediaQuery.addEventListener('change', handleChange);
|
||||||
|
return () => mediaQuery.removeEventListener('change', handleChange);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
let positionClasses = '';
|
||||||
|
let spaceCSS: React.CSSProperties = {};
|
||||||
|
if (position === 'top') {
|
||||||
|
positionClasses = `top-${space} -bottom-2 -translate-y-1/2 -translate-x-1/2`;
|
||||||
|
} else if (position === 'right') {
|
||||||
|
positionClasses = `bottom-1 -translate-x-1/2`;
|
||||||
|
spaceCSS = { left: 70 + space };
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="relative"
|
className="relative"
|
||||||
onMouseEnter={() => setIsVisible(true)}
|
onMouseEnter={() => setIsVisible(true)}
|
||||||
onMouseLeave={() => setIsVisible(false)}
|
onMouseLeave={() => setIsVisible(false)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<div
|
{showPopup && (
|
||||||
className={`
|
<div
|
||||||
absolute
|
className={`
|
||||||
${positionClasses}
|
absolute
|
||||||
${extraClasses ? extraClasses : ''}
|
${positionClasses}
|
||||||
bg-(--color-bg) color-fg border-1 border-(--color-bg-tertiary)
|
${extraClasses ?? ''}
|
||||||
px-3 py-2 rounded-lg
|
bg-(--color-bg) color-fg border-1 border-(--color-bg-tertiary)
|
||||||
transition-opacity duration-100
|
px-3 py-2 rounded-lg
|
||||||
${isVisible ? 'opacity-100' : 'opacity-0 pointer-events-none'}
|
transition-opacity duration-100
|
||||||
z-50 text-center
|
${isVisible ? 'opacity-100' : 'opacity-0 pointer-events-none'}
|
||||||
flex
|
z-50 text-center
|
||||||
`}
|
flex
|
||||||
style={spaceCSS}
|
`}
|
||||||
>
|
style={spaceCSS}
|
||||||
{inner}
|
>
|
||||||
</div>
|
{inner}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,43 +0,0 @@
|
|||||||
import { isRouteErrorResponse, Outlet } from "react-router";
|
|
||||||
import Footer from "~/components/Footer";
|
|
||||||
import type { Route } from "../+types/root";
|
|
||||||
|
|
||||||
export default function Root() {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col items-center mx-auto w-full">
|
|
||||||
<Outlet />
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
|
|
||||||
let message = "Oops!";
|
|
||||||
let details = "An unexpected error occurred.";
|
|
||||||
let stack: string | undefined;
|
|
||||||
|
|
||||||
if (isRouteErrorResponse(error)) {
|
|
||||||
message = error.status === 404 ? "404" : "Error";
|
|
||||||
details =
|
|
||||||
error.status === 404
|
|
||||||
? "The requested page could not be found."
|
|
||||||
: error.statusText || details;
|
|
||||||
} else if (import.meta.env.DEV && error && error instanceof Error) {
|
|
||||||
details = error.message;
|
|
||||||
stack = error.stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="pt-16 p-4 container mx-auto scroll-smooth">
|
|
||||||
<h1>{message}</h1>
|
|
||||||
<p>{details}</p>
|
|
||||||
{stack && (
|
|
||||||
<pre className="w-full p-4 overflow-x-auto">
|
|
||||||
<code>{stack}</code>
|
|
||||||
</pre>
|
|
||||||
)}
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Loading…
Reference in new issue