dece
This commit is contained in:
55
src/app.tsx
55
src/app.tsx
@@ -10,7 +10,6 @@ import {
|
|||||||
import "./app.css";
|
import "./app.css";
|
||||||
import { LeftBar, RightBar } from "./components/Bars";
|
import { LeftBar, RightBar } from "./components/Bars";
|
||||||
import { TerminalSplash } from "./components/TerminalSplash";
|
import { TerminalSplash } from "./components/TerminalSplash";
|
||||||
import { SplashProvider } from "./context/splash";
|
|
||||||
import { MetaProvider } from "@solidjs/meta";
|
import { MetaProvider } from "@solidjs/meta";
|
||||||
import ErrorBoundaryFallback from "./components/ErrorBoundaryFallback";
|
import ErrorBoundaryFallback from "./components/ErrorBoundaryFallback";
|
||||||
import { BarsProvider, useBars } from "./context/bars";
|
import { BarsProvider, useBars } from "./context/bars";
|
||||||
@@ -160,33 +159,6 @@ function AppLayout(props: { children: any }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex max-w-screen flex-row">
|
<div class="flex max-w-screen flex-row">
|
||||||
{/* Hamburger menu button - only visible on non-touch devices under mobile breakpoint */}
|
|
||||||
<button
|
|
||||||
onClick={() => setLeftBarVisible(!leftBarVisible())}
|
|
||||||
class="hamburger-menu-btn fixed top-4 left-4 z-50 p-2 rounded-md bg-surface0 hover:bg-surface1 transition-colors shadow-md"
|
|
||||||
classList={{
|
|
||||||
"hidden": leftBarVisible()
|
|
||||||
}}
|
|
||||||
aria-label="Toggle navigation menu"
|
|
||||||
style={{
|
|
||||||
display: "none" // Hidden by default, shown via media query for non-touch devices
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="w-6 h-6 text-text"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M4 6h16M4 12h16M4 18h16"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<LeftBar />
|
<LeftBar />
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -194,7 +166,7 @@ function AppLayout(props: { children: any }) {
|
|||||||
"margin-left": `${leftBarSize()}px`
|
"margin-left": `${leftBarSize()}px`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Suspense>{props.children}</Suspense>
|
<Suspense fallback={<TerminalSplash />}>{props.children}</Suspense>
|
||||||
</div>
|
</div>
|
||||||
<RightBar />
|
<RightBar />
|
||||||
</div>
|
</div>
|
||||||
@@ -204,20 +176,17 @@ function AppLayout(props: { children: any }) {
|
|||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
<MetaProvider>
|
<MetaProvider>
|
||||||
<SplashProvider>
|
<ErrorBoundary
|
||||||
<ErrorBoundary
|
fallback={(error, reset) => (
|
||||||
fallback={(error, reset) => (
|
<ErrorBoundaryFallback error={error} reset={reset} />
|
||||||
<ErrorBoundaryFallback error={error} reset={reset} />
|
)}
|
||||||
)}
|
>
|
||||||
>
|
<BarsProvider>
|
||||||
<BarsProvider>
|
<Router root={(props) => <AppLayout>{props.children}</AppLayout>}>
|
||||||
<TerminalSplash />
|
<FileRoutes />
|
||||||
<Router root={(props) => <AppLayout>{props.children}</AppLayout>}>
|
</Router>
|
||||||
<FileRoutes />
|
</BarsProvider>
|
||||||
</Router>
|
</ErrorBoundary>
|
||||||
</BarsProvider>
|
|
||||||
</ErrorBoundary>
|
|
||||||
</SplashProvider>
|
|
||||||
</MetaProvider>
|
</MetaProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Typewriter } from "./Typewriter";
|
|||||||
import { useBars } from "~/context/bars";
|
import { useBars } from "~/context/bars";
|
||||||
import { onMount, createEffect, createSignal, Show, For } from "solid-js";
|
import { onMount, createEffect, createSignal, Show, For } from "solid-js";
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
|
import { TerminalSplash } from "./TerminalSplash";
|
||||||
|
|
||||||
export function LeftBar() {
|
export function LeftBar() {
|
||||||
const { setLeftBarSize, leftBarVisible, setLeftBarVisible } = useBars();
|
const { setLeftBarSize, leftBarVisible, setLeftBarVisible } = useBars();
|
||||||
@@ -136,7 +137,7 @@ export function LeftBar() {
|
|||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
ref={ref}
|
ref={ref}
|
||||||
class="border-r-overlay2 fixed z-50 h-full w-fit max-w-[25%] border-r-2 transition-transform duration-500 ease-out"
|
class="border-r-overlay2 fixed z-50 h-full w-min border-r-2 transition-transform duration-500 ease-out md:max-w-[20%]"
|
||||||
classList={{
|
classList={{
|
||||||
"-translate-x-full": !leftBarVisible(),
|
"-translate-x-full": !leftBarVisible(),
|
||||||
"translate-x-0": leftBarVisible()
|
"translate-x-0": leftBarVisible()
|
||||||
@@ -160,10 +161,7 @@ export function LeftBar() {
|
|||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<span class="text-lg font-semibold">Recent Posts</span>
|
<span class="text-lg font-semibold">Recent Posts</span>
|
||||||
<div class="flex flex-col gap-3">
|
<div class="flex flex-col gap-3">
|
||||||
<Show
|
<Show when={recentPosts()} fallback={<TerminalSplash />}>
|
||||||
when={recentPosts()}
|
|
||||||
fallback={<div class="text-sm">Loading...</div>}
|
|
||||||
>
|
|
||||||
<For each={recentPosts()}>
|
<For each={recentPosts()}>
|
||||||
{(post) => (
|
{(post) => (
|
||||||
<a
|
<a
|
||||||
@@ -172,6 +170,7 @@ export function LeftBar() {
|
|||||||
>
|
>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span>{post.title.replace(/_/g, " ")}</span>
|
<span>{post.title.replace(/_/g, " ")}</span>
|
||||||
|
|
||||||
<span class="text-subtext0 text-sm">
|
<span class="text-subtext0 text-sm">
|
||||||
{new Date(post.date).toLocaleDateString("en-US", {
|
{new Date(post.date).toLocaleDateString("en-US", {
|
||||||
month: "short",
|
month: "short",
|
||||||
@@ -258,7 +257,7 @@ export function RightBar() {
|
|||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
ref={ref}
|
ref={ref}
|
||||||
class="border-l-overlay2 fixed right-0 z-50 hidden h-full min-h-screen w-fit max-w-[25%] border-l-2 transition-transform duration-500 ease-out md:block"
|
class="border-l-overlay2 fixed right-0 z-50 hidden h-full min-h-screen w-fit border-l-2 transition-transform duration-500 ease-out md:block md:max-w-[20%]"
|
||||||
classList={{
|
classList={{
|
||||||
"translate-x-full": !rightBarVisible(),
|
"translate-x-full": !rightBarVisible(),
|
||||||
"translate-x-0": rightBarVisible()
|
"translate-x-0": rightBarVisible()
|
||||||
|
|||||||
@@ -1,57 +1,31 @@
|
|||||||
import { Show, onMount, onCleanup, createSignal } from "solid-js";
|
import { onMount, onCleanup, createSignal } from "solid-js";
|
||||||
import { useSplash } from "~/context/splash";
|
import { isServer } from "solid-js/web";
|
||||||
|
|
||||||
const spinnerChars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
const spinnerChars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
||||||
|
|
||||||
export function TerminalSplash() {
|
export function TerminalSplash() {
|
||||||
const { showSplash, setShowSplash } = useSplash();
|
|
||||||
const [showing, setShowing] = createSignal(0);
|
const [showing, setShowing] = createSignal(0);
|
||||||
const [isVisible, setIsVisible] = createSignal(true);
|
|
||||||
|
|
||||||
onMount(() => {
|
// Only run animation on client
|
||||||
const interval = setInterval(() => {
|
if (!isServer) {
|
||||||
setShowing((prev) => (prev + 1) % spinnerChars.length);
|
onMount(() => {
|
||||||
}, 50);
|
const interval = setInterval(() => {
|
||||||
|
setShowing((prev) => (prev + 1) % spinnerChars.length);
|
||||||
|
}, 50);
|
||||||
|
|
||||||
// Hide splash after 1.5 seconds
|
onCleanup(() => {
|
||||||
const timeoutId = setTimeout(() => {
|
clearInterval(interval);
|
||||||
setShowSplash(false);
|
});
|
||||||
}, 1500);
|
|
||||||
|
|
||||||
onCleanup(() => {
|
|
||||||
clearInterval(interval);
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
// Handle fade out when splash is hidden
|
|
||||||
const shouldRender = () => showSplash() || isVisible();
|
|
||||||
|
|
||||||
// Trigger fade out, then hide after transition
|
|
||||||
const opacity = () => {
|
|
||||||
if (!showSplash() && isVisible()) {
|
|
||||||
setTimeout(() => setIsVisible(false), 500);
|
|
||||||
return "0";
|
|
||||||
}
|
|
||||||
if (showSplash()) {
|
|
||||||
setIsVisible(true);
|
|
||||||
return "1";
|
|
||||||
}
|
|
||||||
return "0";
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={shouldRender()}>
|
<div class="bg-base mx-auto flex min-h-full w-full flex-col items-center justify-center overflow-hidden">
|
||||||
<div
|
<div class="text-text max-w-3xl p-8 font-mono text-4xl whitespace-pre-wrap">
|
||||||
class="bg-base fixed inset-0 z-50 mx-auto flex h-screen w-screen flex-col items-center justify-center overflow-hidden transition-opacity duration-500"
|
<div class="flex items-center justify-center">
|
||||||
style={{ opacity: opacity() }}
|
{spinnerChars[showing()]}
|
||||||
>
|
|
||||||
<div class="text-text max-w-3xl p-8 font-mono text-4xl whitespace-pre-wrap">
|
|
||||||
<div class="flex items-center justify-center">
|
|
||||||
{spinnerChars[showing()]}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { JSX, onMount, createSignal, children } from "solid-js";
|
import { JSX, onMount, createSignal, children } from "solid-js";
|
||||||
import { useSplash } from "~/context/splash";
|
|
||||||
|
|
||||||
export function Typewriter(props: {
|
export function Typewriter(props: {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
@@ -17,7 +16,6 @@ export function Typewriter(props: {
|
|||||||
typeof keepAlive === "number" ? keepAlive : -1
|
typeof keepAlive === "number" ? keepAlive : -1
|
||||||
);
|
);
|
||||||
const resolved = children(() => props.children);
|
const resolved = children(() => props.children);
|
||||||
const { showSplash } = useSplash();
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (!containerRef || !cursorRef) return;
|
if (!containerRef || !cursorRef) return;
|
||||||
@@ -69,23 +67,6 @@ export function Typewriter(props: {
|
|||||||
cursorRef.style.height = `${firstChar.offsetHeight}px`;
|
cursorRef.style.height = `${firstChar.offsetHeight}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// THEN: Wait for splash to be hidden before starting the animation
|
|
||||||
const checkSplashHidden = () => {
|
|
||||||
if (showSplash()) {
|
|
||||||
setTimeout(checkSplashHidden, 10);
|
|
||||||
} else {
|
|
||||||
// Start delay if specified
|
|
||||||
if (delay > 0) {
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsDelaying(false);
|
|
||||||
startReveal();
|
|
||||||
}, delay);
|
|
||||||
} else {
|
|
||||||
startReveal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const startReveal = () => {
|
const startReveal = () => {
|
||||||
setIsTyping(true); // Switch to typing cursor
|
setIsTyping(true); // Switch to typing cursor
|
||||||
|
|
||||||
@@ -139,7 +120,15 @@ export function Typewriter(props: {
|
|||||||
setTimeout(revealNextChar, 100);
|
setTimeout(revealNextChar, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
checkSplashHidden();
|
// Start delay if specified, otherwise start immediately
|
||||||
|
if (delay > 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsDelaying(false);
|
||||||
|
startReveal();
|
||||||
|
}, delay);
|
||||||
|
} else {
|
||||||
|
startReveal();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const getCursorClass = () => {
|
const getCursorClass = () => {
|
||||||
|
|||||||
@@ -25,47 +25,40 @@ export interface CardProps {
|
|||||||
|
|
||||||
export default function Card(props: CardProps) {
|
export default function Card(props: CardProps) {
|
||||||
return (
|
return (
|
||||||
<div class="relative z-0 mx-auto h-96 w-full overflow-hidden rounded-lg bg-white shadow-lg dark:bg-zinc-900 md:w-5/6 lg:w-3/4">
|
<div class="bg-base relative z-0 mx-auto h-96 w-full overflow-hidden rounded-lg shadow-lg md:w-5/6 lg:w-3/4 dark:bg-zinc-900">
|
||||||
<Show when={props.privilegeLevel === "admin"}>
|
<Show when={props.privilegeLevel === "admin"}>
|
||||||
<div class="absolute top-0 w-full border-b border-white border-opacity-20 bg-white bg-opacity-40 px-2 py-4 backdrop-blur-md dark:border-black dark:bg-zinc-800 dark:bg-opacity-60 md:px-6">
|
<div class="border-opacity-20 bg-opacity-40 dark:bg-opacity-60 absolute top-0 w-full border-b border-white bg-white px-2 py-4 backdrop-blur-md md:px-6 dark:border-black dark:bg-zinc-800">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<Show when={!props.post.published}>
|
<Show when={!props.post.published}>
|
||||||
<div class="whitespace-nowrap text-center text-lg text-black dark:text-white">
|
<div class="text-center text-lg whitespace-nowrap text-black dark:text-white">
|
||||||
Not Published
|
Not Published
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<DeletePostButton
|
<DeletePostButton type="Blog" postID={props.post.id} />
|
||||||
type="Blog"
|
|
||||||
postID={props.post.id}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<img
|
<img
|
||||||
src={
|
src={props.post.banner_photo ? props.post.banner_photo : "/bitcoin.jpg"}
|
||||||
props.post.banner_photo
|
|
||||||
? props.post.banner_photo
|
|
||||||
: "/bitcoin.jpg"
|
|
||||||
}
|
|
||||||
alt={props.post.title.replaceAll("_", " ") + " banner"}
|
alt={props.post.title.replaceAll("_", " ") + " banner"}
|
||||||
class="h-full w-full object-cover"
|
class="h-full w-full object-cover"
|
||||||
/>
|
/>
|
||||||
<div class="absolute bottom-0 w-full border-t border-white border-opacity-20 bg-white bg-opacity-40 px-2 py-4 backdrop-blur-md dark:border-zinc-900 dark:bg-zinc-800 dark:bg-opacity-60 md:px-6">
|
<div class="border-opacity-20 bg-opacity-40 dark:bg-opacity-60 bg-base absolute bottom-0 w-full border-t border-white px-2 py-4 backdrop-blur-md md:px-6 dark:border-zinc-900 dark:bg-zinc-800">
|
||||||
<div class="flex flex-col items-center justify-between md:flex-row">
|
<div class="flex flex-col items-center justify-between md:flex-row">
|
||||||
<div class="text-center md:text-left">
|
<div class="text-center md:text-left">
|
||||||
<div class="text-lg text-black dark:text-white md:text-xl">
|
<div class="text-lg text-black md:text-xl dark:text-white">
|
||||||
{props.post.subtitle}
|
{props.post.subtitle}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-2xl text-black dark:text-white md:text-3xl">
|
<div class="text-2xl text-black md:text-3xl dark:text-white">
|
||||||
{props.post.title.replaceAll("_", " ")}
|
{props.post.title.replaceAll("_", " ")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex w-full justify-around pt-2 md:w-1/3 md:justify-between md:pl-2 md:pt-0">
|
<div class="flex w-full justify-around pt-2 md:w-1/3 md:justify-between md:pt-0 md:pl-2">
|
||||||
<div class="my-auto md:h-full">
|
<div class="my-auto md:h-full">
|
||||||
<p class="whitespace-nowrap text-sm text-black dark:text-white">
|
<p class="text-sm whitespace-nowrap text-black dark:text-white">
|
||||||
{props.post.total_comments || 0} Comments
|
{props.post.total_comments || 0} Comments
|
||||||
</p>
|
</p>
|
||||||
<p class="whitespace-nowrap text-sm text-black dark:text-white">
|
<p class="text-sm whitespace-nowrap text-black dark:text-white">
|
||||||
{props.post.total_likes || 0} Likes
|
{props.post.total_likes || 0} Likes
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import { Accessor, createContext, useContext } from "solid-js";
|
|
||||||
import { createSignal } from "solid-js";
|
|
||||||
|
|
||||||
// Create context with initial value
|
|
||||||
const SplashContext = createContext<{
|
|
||||||
showSplash: Accessor<boolean>;
|
|
||||||
setShowSplash: (show: boolean) => void;
|
|
||||||
}>({
|
|
||||||
showSplash: () => true,
|
|
||||||
setShowSplash: () => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
export function useSplash() {
|
|
||||||
const context = useContext(SplashContext);
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SplashProvider(props: { children: any }) {
|
|
||||||
const [showSplash, setShowSplash] = createSignal(true);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SplashContext.Provider value={{ showSplash, setShowSplash }}>
|
|
||||||
{props.children}
|
|
||||||
</SplashContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,7 @@ import CommentSectionWrapper from "~/components/blog/CommentSectionWrapper";
|
|||||||
import PostBodyClient from "~/components/blog/PostBodyClient";
|
import PostBodyClient from "~/components/blog/PostBodyClient";
|
||||||
import type { Comment, CommentReaction, UserPublicData } from "~/types/comment";
|
import type { Comment, CommentReaction, UserPublicData } from "~/types/comment";
|
||||||
import { useBars } from "~/context/bars";
|
import { useBars } from "~/context/bars";
|
||||||
|
import { TerminalSplash } from "~/components/TerminalSplash";
|
||||||
|
|
||||||
// Server function to fetch post by title
|
// Server function to fetch post by title
|
||||||
const getPostByTitle = query(async (title: string) => {
|
const getPostByTitle = query(async (title: string) => {
|
||||||
@@ -141,13 +142,7 @@ export default function PostPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Suspense
|
<Suspense fallback={<TerminalSplash />}>
|
||||||
fallback={
|
|
||||||
<div class="w-full pt-[30vh] text-center">
|
|
||||||
<div class="text-xl">Loading post...</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Show when={data()} fallback={null}>
|
<Show when={data()} fallback={null}>
|
||||||
{(loadedData) => (
|
{(loadedData) => (
|
||||||
<Show when={loadedData().post} fallback={<Navigate href="/404" />}>
|
<Show when={loadedData().post} fallback={<Navigate href="/404" />}>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { api } from "~/lib/api";
|
|||||||
import PostSortingSelect from "~/components/blog/PostSortingSelect";
|
import PostSortingSelect from "~/components/blog/PostSortingSelect";
|
||||||
import TagSelector from "~/components/blog/TagSelector";
|
import TagSelector from "~/components/blog/TagSelector";
|
||||||
import PostSorting from "~/components/blog/PostSorting";
|
import PostSorting from "~/components/blog/PostSorting";
|
||||||
|
import { TerminalSplash } from "~/components/TerminalSplash";
|
||||||
|
|
||||||
export default function BlogIndex() {
|
export default function BlogIndex() {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
@@ -20,13 +21,7 @@ export default function BlogIndex() {
|
|||||||
<Title>Blog | Michael Freno</Title>
|
<Title>Blog | Michael Freno</Title>
|
||||||
|
|
||||||
<div class="relative mx-auto min-h-screen rounded-t-lg pt-8 pb-24 shadow-2xl">
|
<div class="relative mx-auto min-h-screen rounded-t-lg pt-8 pb-24 shadow-2xl">
|
||||||
<Suspense
|
<Suspense fallback={<TerminalSplash />}>
|
||||||
fallback={
|
|
||||||
<div class="mx-auto pt-48">
|
|
||||||
<div class="text-center">Loading...</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div class="flex flex-col justify-center gap-4 md:flex-row md:justify-around">
|
<div class="flex flex-col justify-center gap-4 md:flex-row md:justify-around">
|
||||||
<PostSortingSelect />
|
<PostSortingSelect />
|
||||||
|
|
||||||
@@ -47,13 +42,7 @@ export default function BlogIndex() {
|
|||||||
</div>
|
</div>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
||||||
<Suspense
|
<Suspense fallback={<TerminalSplash />}>
|
||||||
fallback={
|
|
||||||
<div class="mx-auto pt-48">
|
|
||||||
<div class="text-center">Loading posts...</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Show
|
<Show
|
||||||
when={data() && data()!.posts.length > 0}
|
when={data() && data()!.posts.length > 0}
|
||||||
fallback={<div class="pt-12 text-center">No posts yet!</div>}
|
fallback={<div class="pt-12 text-center">No posts yet!</div>}
|
||||||
|
|||||||
Reference in New Issue
Block a user