mirror of
https://github.com/gabehf/Koito.git
synced 2026-03-14 01:46:09 -07:00
* feat: single SOT for themes + basic custom support * fix: adjust colors for yuu theme * feat: Allow loading of environment variables from file (#20) * feat: allow loading of environment variables from file * Panic if a file for an environment variable cannot be read * Use log.Fatalf + os.Exit instead of panic * fix: remove supurfluous call to os.Exit() --------- Co-authored-by: adaexec <nixos-git.s1pht@simplelogin.com> Co-authored-by: Gabe Farrell <90876006+gabehf@users.noreply.github.com> * chore: add pr test workflow * chore: changelog * feat: make all activity grids configurable * fix: adjust activity grid style * fix: make background gradient consistent size * revert: remove year from activity grid opts * style: adjust top item list min size to 200px * feat: add support for custom themes * fix: stabilized the order of top items * chore: update changelog * feat: native import & export * fix: use correct request body for alias requests * fix: clear input when closing edit modal * chore: changelog * docs: make endpoint clearer for some apps * feat: add ui and handler for export * fix: fix pr test workflow --------- Co-authored-by: adaexec <78047743+adaexec@users.noreply.github.com> Co-authored-by: adaexec <nixos-git.s1pht@simplelogin.com>
98 lines
4.8 KiB
TypeScript
98 lines
4.8 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
import { average } from "color.js";
|
|
import { imageUrl, type SearchResponse } from "api/api";
|
|
import ImageDropHandler from "~/components/ImageDropHandler";
|
|
import { Edit, ImageIcon, Merge, Trash } from "lucide-react";
|
|
import { useAppContext } from "~/providers/AppProvider";
|
|
import MergeModal from "~/components/modals/MergeModal";
|
|
import ImageReplaceModal from "~/components/modals/ImageReplaceModal";
|
|
import DeleteModal from "~/components/modals/DeleteModal";
|
|
import RenameModal from "~/components/modals/EditModal/EditModal";
|
|
import EditModal from "~/components/modals/EditModal/EditModal";
|
|
|
|
export type MergeFunc = (from: number, to: number, replaceImage: boolean) => Promise<Response>
|
|
export type MergeSearchCleanerFunc = (r: SearchResponse, id: number) => SearchResponse
|
|
|
|
interface Props {
|
|
type: "Track" | "Album" | "Artist"
|
|
title: string
|
|
img: string
|
|
id: number
|
|
musicbrainzId: string
|
|
imgItemId: number
|
|
mergeFunc: MergeFunc
|
|
mergeCleanerFunc: MergeSearchCleanerFunc
|
|
children: React.ReactNode
|
|
subContent: React.ReactNode
|
|
}
|
|
|
|
export default function MediaLayout(props: Props) {
|
|
const [bgColor, setBgColor] = useState<string>("(--color-bg)");
|
|
const [mergeModalOpen, setMergeModalOpen] = useState(false);
|
|
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
|
const [imageModalOpen, setImageModalOpen] = useState(false);
|
|
const [renameModalOpen, setRenameModalOpen] = useState(false);
|
|
const { user } = useAppContext();
|
|
|
|
useEffect(() => {
|
|
average(imageUrl(props.img, 'small'), { amount: 1 }).then((color) => {
|
|
setBgColor(`rgba(${color[0]},${color[1]},${color[2]},0.4)`);
|
|
});
|
|
}, [props.img]);
|
|
|
|
const replaceImageCallback = () => {
|
|
window.location.reload()
|
|
}
|
|
|
|
const title = `${props.title} - Koito`
|
|
|
|
const mobileIconSize = 22
|
|
const normalIconSize = 30
|
|
|
|
let vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
|
|
|
|
let iconSize = vw > 768 ? normalIconSize : mobileIconSize
|
|
|
|
return (
|
|
<main
|
|
className="w-full flex flex-col flex-grow"
|
|
style={{
|
|
background: `linear-gradient(to bottom, ${bgColor}, var(--color-bg) 700px)`,
|
|
transition: '1000',
|
|
}}
|
|
>
|
|
<ImageDropHandler itemType={props.type.toLowerCase() === 'artist' ? 'artist' : 'album'} id={props.imgItemId} onComplete={replaceImageCallback} />
|
|
<title>{title}</title>
|
|
<meta property="og:title" content={title} />
|
|
<meta
|
|
name="description"
|
|
content={title}
|
|
/>
|
|
<div className="w-19/20 mx-auto pt-12">
|
|
<div className="flex gap-8 flex-wrap md:flex-nowrap relative">
|
|
<div className="flex flex-col justify-around">
|
|
<img style={{zIndex: 5}} src={imageUrl(props.img, "large")} alt={props.title} className="md:min-w-[385px] w-[220px] h-auto shadow-(--color-shadow) shadow-lg" />
|
|
</div>
|
|
<div className="flex flex-col items-start">
|
|
<h3>{props.type}</h3>
|
|
<h1>{props.title}</h1>
|
|
{props.subContent}
|
|
</div>
|
|
{ user &&
|
|
<div className="absolute left-1 sm:right-1 sm:left-auto -top-9 sm:top-1 flex gap-3 items-center">
|
|
<button title="Edit Item" className="hover:cursor-pointer" onClick={() => setRenameModalOpen(true)}><Edit size={iconSize} /></button>
|
|
<button title="Replace Image" className="hover:cursor-pointer" onClick={() => setImageModalOpen(true)}><ImageIcon size={iconSize} /></button>
|
|
<button title="Merge Items" className="hover:cursor-pointer" onClick={() => setMergeModalOpen(true)}><Merge size={iconSize} /></button>
|
|
<button title="Delete Item" className="hover:cursor-pointer" onClick={() => setDeleteModalOpen(true)}><Trash size={iconSize} /></button>
|
|
<EditModal open={renameModalOpen} setOpen={setRenameModalOpen} type={props.type.toLowerCase()} id={props.id}/>
|
|
<ImageReplaceModal open={imageModalOpen} setOpen={setImageModalOpen} id={props.imgItemId} musicbrainzId={props.musicbrainzId} type={props.type === "Track" ? "Album" : props.type} />
|
|
<MergeModal currentTitle={props.title} mergeFunc={props.mergeFunc} mergeCleanerFunc={props.mergeCleanerFunc} type={props.type} currentId={props.id} open={mergeModalOpen} setOpen={setMergeModalOpen} />
|
|
<DeleteModal open={deleteModalOpen} setOpen={setDeleteModalOpen} title={props.title} id={props.id} type={props.type} />
|
|
</div>
|
|
}
|
|
</div>
|
|
{props.children}
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|