diff --git a/apps/desktop2/src/routes/trending.users.tsx b/apps/desktop2/src/routes/trending.users.tsx
index 026926ed..b2ea2692 100644
--- a/apps/desktop2/src/routes/trending.users.tsx
+++ b/apps/desktop2/src/routes/trending.users.tsx
@@ -1,5 +1,4 @@
-import { LoaderIcon } from "@lume/icons";
-import { User } from "@lume/ui";
+import { Spinner, User } from "@lume/ui";
import { Await, defer } from "@tanstack/react-router";
import { createFileRoute } from "@tanstack/react-router";
import { Suspense } from "react";
@@ -34,7 +33,7 @@ export function Screen() {
className="inline-flex items-center gap-2 text-sm font-medium"
disabled
>
-
+
Loading...
diff --git a/apps/desktop2/src/routes/users/-components/eventList.tsx b/apps/desktop2/src/routes/users/-components/eventList.tsx
index 47c8817b..3fcfb022 100644
--- a/apps/desktop2/src/routes/users/-components/eventList.tsx
+++ b/apps/desktop2/src/routes/users/-components/eventList.tsx
@@ -1,10 +1,11 @@
import { TextNote } from "@/components/text";
import { RepostNote } from "@/components/repost";
-import { ArrowRightCircleIcon, InfoIcon, LoaderIcon } from "@lume/icons";
+import { ArrowRightCircleIcon, InfoIcon } from "@lume/icons";
import { Event, Kind } from "@lume/types";
import { FETCH_LIMIT } from "@lume/utils";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useRouteContext } from "@tanstack/react-router";
+import { Spinner } from "@lume/ui";
export function EventList({ id }: { id: string }) {
const { ark } = useRouteContext({ strict: false });
@@ -39,7 +40,7 @@ export function EventList({ id }: { id: string }) {
{isLoading ? (
-
+
) : !data.length ? (
@@ -58,7 +59,7 @@ export function EventList({ id }: { id: string }) {
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
{isFetchingNextPage ? (
-
+
) : (
<>
diff --git a/packages/icons/index.ts b/packages/icons/index.ts
index c8612902..d0a5f4ea 100644
--- a/packages/icons/index.ts
+++ b/packages/icons/index.ts
@@ -33,7 +33,6 @@ export * from "./src/threads";
export * from "./src/trash";
export * from "./src/world";
export * from "./src/zap";
-export * from "./src/loader";
export * from "./src/trending";
export * from "./src/empty";
export * from "./src/cmd";
diff --git a/packages/icons/src/loader.tsx b/packages/icons/src/loader.tsx
deleted file mode 100644
index 69fbeba0..00000000
--- a/packages/icons/src/loader.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { SVGProps } from "react";
-
-export function LoaderIcon(
- props: JSX.IntrinsicAttributes & SVGProps,
-) {
- return (
-
- );
-}
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
index 566e7dcb..5eaebd7b 100644
--- a/packages/ui/src/index.ts
+++ b/packages/ui/src/index.ts
@@ -5,3 +5,4 @@ export * from "./column";
// UI
export * from "./container";
export * from "./box";
+export * from "./spinner";
diff --git a/packages/ui/src/note/buttons/downvote.tsx b/packages/ui/src/note/buttons/downvote.tsx
index 9a2b1abe..51c85b7d 100644
--- a/packages/ui/src/note/buttons/downvote.tsx
+++ b/packages/ui/src/note/buttons/downvote.tsx
@@ -1,10 +1,11 @@
-import { ArrowDownIcon, LoaderIcon } from "@lume/icons";
+import { ArrowDownIcon } from "@lume/icons";
import { useState } from "react";
import { useNoteContext } from "../provider";
import { cn } from "@lume/utils";
import * as Tooltip from "@radix-ui/react-tooltip";
import { useTranslation } from "react-i18next";
import { useRouteContext } from "@tanstack/react-router";
+import { Spinner } from "../../spinner";
export function NoteDownvote() {
const ark = useRouteContext({ strict: false });
@@ -41,7 +42,7 @@ export function NoteDownvote() {
)}
>
{loading ? (
-
+
) : (
)}
diff --git a/packages/ui/src/note/buttons/repost.tsx b/packages/ui/src/note/buttons/repost.tsx
index 53cb489a..e395f019 100644
--- a/packages/ui/src/note/buttons/repost.tsx
+++ b/packages/ui/src/note/buttons/repost.tsx
@@ -1,4 +1,4 @@
-import { LoaderIcon, QuoteIcon, RepostIcon } from "@lume/icons";
+import { QuoteIcon, RepostIcon } from "@lume/icons";
import { cn } from "@lume/utils";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import * as Tooltip from "@radix-ui/react-tooltip";
@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import { useNoteContext } from "../provider";
import { useRouteContext } from "@tanstack/react-router";
+import { Spinner } from "../../spinner";
export function NoteRepost() {
const { ark } = useRouteContext({ strict: false });
@@ -46,7 +47,7 @@ export function NoteRepost() {
className="group inline-flex size-7 items-center justify-center text-neutral-800 dark:text-neutral-200"
>
{loading ? (
-
+
) : (
{loading ? (
-
+
) : (
)}
diff --git a/packages/ui/src/spinner.tsx b/packages/ui/src/spinner.tsx
new file mode 100644
index 00000000..e6fb0a83
--- /dev/null
+++ b/packages/ui/src/spinner.tsx
@@ -0,0 +1,47 @@
+import { cn } from "@lume/utils";
+import { ReactNode } from "react";
+
+export function Spinner({
+ children,
+ className,
+}: {
+ children?: ReactNode;
+ className?: string;
+}) {
+ const spinner = (
+
+
+
+
+
+
+
+
+
+
+ );
+
+ if (children === undefined) return spinner;
+
+ return (
+
+
+ {/**
+ * `display: contents` removes the content from the accessibility tree in some browsers,
+ * so we force remove it with `aria-hidden`
+ */}
+
+ {children}
+
+