theme
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { createClient, type BufferLike } from "webdav";
|
import { createClient } from "webdav";
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,9 @@ import type { Request, Response, NextFunction } from "express";
|
|||||||
export function authMiddleware(req: Request, res: Response, next: NextFunction) {
|
export function authMiddleware(req: Request, res: Response, next: NextFunction) {
|
||||||
try {
|
try {
|
||||||
const token = req.cookies.token;
|
const token = req.cookies.token;
|
||||||
|
|
||||||
if (!token) { return res.sendStatus(401); }
|
if (!token) { return res.sendStatus(401); }
|
||||||
|
|
||||||
console.log("Token: ", token)
|
|
||||||
console.log("Secret: ", process.env.JWT_SECRET)
|
|
||||||
jwt.verify(token, process.env.JWT_SECRET!)
|
jwt.verify(token, process.env.JWT_SECRET!)
|
||||||
|
|
||||||
next();
|
next();
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ router.get("/video", async (req, res) => {
|
|||||||
|
|
||||||
router.post("/createclip", async (req, res) => {
|
router.post("/createclip", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
encodeFrames({
|
await encodeFrames({
|
||||||
inputPattern: `${LOCAL_DIR}/*.jpg`,
|
inputPattern: `${LOCAL_DIR}/*.jpg`,
|
||||||
output: path.resolve("./out.mp4"),
|
output: path.resolve("./out.mp4"),
|
||||||
fps: 12,
|
fps: 8,
|
||||||
});
|
});
|
||||||
res.status(200).end();
|
res.status(200).end();
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
BIN
front/public/kapitan_papryk_dark.png
Normal file
BIN
front/public/kapitan_papryk_dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 MiB |
BIN
front/public/kapitan_papryk_light.png
Normal file
BIN
front/public/kapitan_papryk_light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 MiB |
26
front/src/components/ThemeToggle.tsx
Normal file
26
front/src/components/ThemeToggle.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Moon, Sun } from "lucide-react"
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { useTheme } from "./theme-provider"
|
||||||
|
|
||||||
|
export function ThemeToggle() {
|
||||||
|
const { theme, setTheme } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
onClick={() =>
|
||||||
|
setTheme(theme === "dark" ? "light" : "dark")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||||
|
|
||||||
|
<Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||||
|
|
||||||
|
<span className="sr-only">
|
||||||
|
Toggle theme
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
125
front/src/components/ui/bubble.tsx
Normal file
125
front/src/components/ui/bubble.tsx
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
import { Slot } from "radix-ui"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function BubbleGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="bubble-group"
|
||||||
|
className={cn("flex min-w-0 flex-col gap-2", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const bubbleVariants = cva(
|
||||||
|
"group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-1 group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"*:data-[slot=bubble-content]:bg-primary *:data-[slot=bubble-content]:text-primary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/80",
|
||||||
|
secondary:
|
||||||
|
"*:data-[slot=bubble-content]:bg-secondary *:data-[slot=bubble-content]:text-secondary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]",
|
||||||
|
muted:
|
||||||
|
"*:data-[slot=bubble-content]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--muted),var(--foreground)_5%)]",
|
||||||
|
tinted:
|
||||||
|
"*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.93_calc(c*0.4)_h)] *:data-[slot=bubble-content]:text-foreground dark:*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.3_calc(c*0.4)_h)] [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.88_calc(c*0.5)_h)] dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.35_calc(c*0.5)_h)]",
|
||||||
|
outline:
|
||||||
|
"*:data-[slot=bubble-content]:border-border *:data-[slot=bubble-content]:bg-background [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-input/30",
|
||||||
|
ghost:
|
||||||
|
"border-none *:data-[slot=bubble-content]:rounded-none *:data-[slot=bubble-content]:bg-transparent *:data-[slot=bubble-content]:p-0 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted/50",
|
||||||
|
destructive:
|
||||||
|
"*:data-[slot=bubble-content]:bg-destructive/10 *:data-[slot=bubble-content]:text-destructive dark:*:data-[slot=bubble-content]:bg-destructive/20 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/20 dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/30",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function Bubble({
|
||||||
|
variant = "default",
|
||||||
|
align = "start",
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> &
|
||||||
|
VariantProps<typeof bubbleVariants> & {
|
||||||
|
align?: "start" | "end"
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="bubble"
|
||||||
|
data-variant={variant}
|
||||||
|
data-align={align}
|
||||||
|
className={cn(bubbleVariants({ variant }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function BubbleContent({
|
||||||
|
asChild = false,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & {
|
||||||
|
asChild?: boolean
|
||||||
|
}) {
|
||||||
|
const Comp = asChild ? Slot.Root : "div"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="bubble-content"
|
||||||
|
className={cn(
|
||||||
|
"w-fit max-w-full min-w-0 overflow-hidden rounded-lg border border-transparent px-2.5 py-1.5 text-xs/relaxed wrap-break-word group-data-[align=end]/bubble:self-end [button]:text-left [button,a]:transition-colors [button,a]:outline-none [button,a]:focus-visible:border-ring [button,a]:focus-visible:ring-2 [button,a]:focus-visible:ring-ring/30",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const bubbleReactionsVariants = cva(
|
||||||
|
"absolute z-10 flex w-fit shrink-0 items-center justify-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-xs ring-2 ring-card has-[button]:p-0",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
side: {
|
||||||
|
top: "top-0 -translate-y-3/4",
|
||||||
|
bottom: "bottom-0 translate-y-3/4",
|
||||||
|
},
|
||||||
|
align: {
|
||||||
|
start: "left-3",
|
||||||
|
end: "right-3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
side: "bottom",
|
||||||
|
align: "end",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function BubbleReactions({
|
||||||
|
side = "bottom",
|
||||||
|
align = "end",
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & {
|
||||||
|
align?: "start" | "end"
|
||||||
|
side?: "top" | "bottom"
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="bubble-reactions"
|
||||||
|
data-align={align}
|
||||||
|
data-side={side}
|
||||||
|
className={cn(bubbleReactionsVariants({ side, align }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { BubbleGroup, Bubble, BubbleContent, BubbleReactions }
|
||||||
@@ -3,7 +3,10 @@ import { Outlet } from "react-router";
|
|||||||
|
|
||||||
export default function AuthLayout() {
|
export default function AuthLayout() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center bg-muted/40 p-4">
|
<div
|
||||||
|
className="min-h-screen flex items-center justify-center bg-muted/40 p-4"
|
||||||
|
style={{ backgroundImage: "url(/paprika_bg.webp)" }}
|
||||||
|
>
|
||||||
<Card className="w-full max-w-md shadow-xl rounded-2xl">
|
<Card className="w-full max-w-md shadow-xl rounded-2xl">
|
||||||
<CardHeader className="text-center space-y-2">
|
<CardHeader className="text-center space-y-2">
|
||||||
<CardTitle className="text-2xl font-semibold">PAPRYK.COM</CardTitle>
|
<CardTitle className="text-2xl font-semibold">PAPRYK.COM</CardTitle>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { useTheme } from "@/components/theme-provider";
|
||||||
|
import { ThemeToggle } from "@/components/ThemeToggle";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { useLogout } from "@/hooks/useLogout";
|
import { useLogout } from "@/hooks/useLogout";
|
||||||
@@ -22,13 +24,19 @@ export function MainLayout() {
|
|||||||
const { data: user } = useMe();
|
const { data: user } = useMe();
|
||||||
const isAuthenticated = !!user;
|
const isAuthenticated = !!user;
|
||||||
const logout = useLogout();
|
const logout = useLogout();
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col">
|
<div
|
||||||
|
className="min-h-screen flex flex-col"
|
||||||
|
style={{ backgroundImage: theme === "light" ? "url(/kapitan_papryk_light.png)" : "url(/kapitan_papryk_dark.png)" }}
|
||||||
|
>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="border-b bg-background">
|
<header className="border-b bg-background/40 backdrop-blur">
|
||||||
<div className="container mx-auto flex h-16 items-center gap-4 px-4">
|
<div
|
||||||
|
className="container mx-auto flex h-16 items-center gap-4 px-4"
|
||||||
|
>
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link to="/" className="flex items-center gap-2 shrink-0">
|
<Link to="/" className="flex items-center gap-2 shrink-0">
|
||||||
<img
|
<img
|
||||||
@@ -74,6 +82,7 @@ export function MainLayout() {
|
|||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -83,7 +92,7 @@ export function MainLayout() {
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<footer className="border-t">
|
<footer className="border-t bg-background/40">
|
||||||
<div className="container mx-auto px-4 py-4 text-center text-sm text-muted-foreground">
|
<div className="container mx-auto px-4 py-4 text-center text-sm text-muted-foreground">
|
||||||
{ /* © {new Date().getFullYear()} papryk */}
|
{ /* © {new Date().getFullYear()} papryk */}
|
||||||
papryk
|
papryk
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
import { Bubble, BubbleContent, BubbleGroup, BubbleReactions } from "@/components/ui/bubble";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
export function Home() {
|
export function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-1 flex-col gap-4 py-16">
|
<div className="flex flex-1 flex-col gap-4 py-16">
|
||||||
<YTVideo url="https://youtube.com/embed/ioiu6L3SCq8" title="EEEE" description="EEEEE... Papryka" />
|
<YTVideo url="https://youtube.com/embed/ioiu6L3SCq8" title="EEEE" description="EEEEE... Papryka" />
|
||||||
<YTVideo url="https://youtube.com/embed/o2tUOQrkjhg" title="RHCP - WAP" description="..." />
|
{ /* <YTVideo url="https://youtube.com/embed/o2tUOQrkjhg" title="RHCP - WAP" description="..." /> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -39,6 +40,21 @@ function YTVideo({ url, title, description }: {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex w-full max-w-sm flex-col gap-8 py-12">
|
||||||
|
<BubbleGroup>
|
||||||
|
<Bubble variant="muted">
|
||||||
|
<BubbleContent>
|
||||||
|
Wiecie co robią lekarze w kuchni? Leczo XD
|
||||||
|
</BubbleContent>
|
||||||
|
<BubbleReactions role="img" aria-label="Reaction: fire">
|
||||||
|
<span>🔥</span>
|
||||||
|
</BubbleReactions>
|
||||||
|
</Bubble>
|
||||||
|
</BubbleGroup>
|
||||||
|
<Bubble align="end">
|
||||||
|
<BubbleContent>XD</BubbleContent>
|
||||||
|
</Bubble>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user