mirror of
https://github.com/lumehq/lume.git
synced 2025-03-17 13:22:05 +01:00
revamp
This commit is contained in:
parent
8398ae80d3
commit
d841163ba7
@ -37,7 +37,6 @@
|
|||||||
"@tauri-apps/plugin-upload": "^2.0.0",
|
"@tauri-apps/plugin-upload": "^2.0.0",
|
||||||
"@tauri-apps/plugin-window-state": "^2.0.0",
|
"@tauri-apps/plugin-window-state": "^2.0.0",
|
||||||
"bitcoin-units": "^1.0.0",
|
"bitcoin-units": "^1.0.0",
|
||||||
"boring-avatars": "^1.11.2",
|
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"embla-carousel-react": "^8.3.0",
|
"embla-carousel-react": "^8.3.0",
|
||||||
"i18next": "^23.15.1",
|
"i18next": "^23.15.1",
|
||||||
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -89,9 +89,6 @@ importers:
|
|||||||
bitcoin-units:
|
bitcoin-units:
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
boring-avatars:
|
|
||||||
specifier: ^1.11.2
|
|
||||||
version: 1.11.2
|
|
||||||
dayjs:
|
dayjs:
|
||||||
specifier: ^1.11.13
|
specifier: ^1.11.13
|
||||||
version: 1.11.13
|
version: 1.11.13
|
||||||
@ -1504,9 +1501,6 @@ packages:
|
|||||||
bitcoin-units@1.0.0:
|
bitcoin-units@1.0.0:
|
||||||
resolution: {integrity: sha512-brac+Ttz7ovf/8D0jQHSWHnN2hmdjxDRBStxhjO752URLJlQIFpfZxzUteSZ81UYnRNiMkvsW9WsYPDuxHfnYA==}
|
resolution: {integrity: sha512-brac+Ttz7ovf/8D0jQHSWHnN2hmdjxDRBStxhjO752URLJlQIFpfZxzUteSZ81UYnRNiMkvsW9WsYPDuxHfnYA==}
|
||||||
|
|
||||||
boring-avatars@1.11.2:
|
|
||||||
resolution: {integrity: sha512-3+wkwPeObwS4R37FGXMYViqc4iTrIRj5yzfX9Qy4mnpZ26sX41dGMhsAgmKks1r/uufY1pl4vpgzMWHYfJRb2A==}
|
|
||||||
|
|
||||||
brace-expansion@2.0.1:
|
brace-expansion@2.0.1:
|
||||||
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
|
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
|
||||||
|
|
||||||
@ -3556,8 +3550,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
big.js: 6.2.2
|
big.js: 6.2.2
|
||||||
|
|
||||||
boring-avatars@1.11.2: {}
|
|
||||||
|
|
||||||
brace-expansion@2.0.1:
|
brace-expansion@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 171 KiB |
Binary file not shown.
Before Width: | Height: | Size: 201 KiB |
Binary file not shown.
Before Width: | Height: | Size: 170 KiB |
Binary file not shown.
Before Width: | Height: | Size: 104 KiB |
@ -290,13 +290,7 @@ pub async fn get_group_events(
|
|||||||
|
|
||||||
let authors: Vec<PublicKey> = public_keys
|
let authors: Vec<PublicKey> = public_keys
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| {
|
.map(|p| PublicKey::from_str(p).map_err(|err| err.to_string()))
|
||||||
if p.starts_with("npub1") {
|
|
||||||
PublicKey::from_bech32(p).map_err(|err| err.to_string())
|
|
||||||
} else {
|
|
||||||
PublicKey::from_hex(p).map_err(|err| err.to_string())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
let filter = Filter::new()
|
let filter = Filter::new()
|
||||||
@ -340,6 +334,7 @@ pub async fn get_global_events(
|
|||||||
state: State<'_, Nostr>,
|
state: State<'_, Nostr>,
|
||||||
) -> Result<Vec<RichEvent>, String> {
|
) -> Result<Vec<RichEvent>, String> {
|
||||||
let client = &state.client;
|
let client = &state.client;
|
||||||
|
|
||||||
let as_of = match until {
|
let as_of = match until {
|
||||||
Some(until) => Timestamp::from_str(until).map_err(|err| err.to_string())?,
|
Some(until) => Timestamp::from_str(until).map_err(|err| err.to_string())?,
|
||||||
None => Timestamp::now(),
|
None => Timestamp::now(),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { commands } from "@/commands.gen";
|
import { commands } from "@/commands.gen";
|
||||||
import type { LumeColumn } from "@/types";
|
import type { LumeColumn } from "@/types";
|
||||||
import { Check, DotsThree } from "@phosphor-icons/react";
|
import { CaretDown, Check } from "@phosphor-icons/react";
|
||||||
import { useParams } from "@tanstack/react-router";
|
import { useParams } from "@tanstack/react-router";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
@ -86,10 +86,10 @@ export const Column = memo(function Column({ column }: { column: LumeColumn }) {
|
|||||||
}, [params.account]);
|
}, [params.account]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-[440px] shrink-0 p-2">
|
<div className="h-full w-[440px] shrink-0 border-r border-black/5 dark:border-white/5">
|
||||||
<div className="flex flex-col w-full h-full rounded-xl bg-black/5 dark:bg-white/20">
|
<div className="flex flex-col gap-px size-full">
|
||||||
<Header label={column.label} name={column.name} />
|
<Header label={column.label} name={column.name} />
|
||||||
<div ref={container} className="flex-1 w-full h-full">
|
<div ref={container} className="flex-1 size-full">
|
||||||
{!isCreated ? (
|
{!isCreated ? (
|
||||||
<div className="size-full flex items-center justify-center">
|
<div className="size-full flex items-center justify-center">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
@ -102,7 +102,7 @@ export const Column = memo(function Column({ column }: { column: LumeColumn }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function Header({ label, name }: { label: string; name: string }) {
|
function Header({ label, name }: { label: string; name: string }) {
|
||||||
const [title, setTitle] = useState(name);
|
const [title, setTitle] = useState("");
|
||||||
const [isChanged, setIsChanged] = useState(false);
|
const [isChanged, setIsChanged] = useState(false);
|
||||||
|
|
||||||
const saveNewTitle = async () => {
|
const saveNewTitle = async () => {
|
||||||
@ -173,19 +173,18 @@ function Header({ label, name }: { label: string; name: string }) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (title.length !== name.length) setIsChanged(true);
|
if (title.length > 0) setIsChanged(true);
|
||||||
}, [title]);
|
}, [title.length]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between w-full px-1 h-9 shrink-0">
|
<div className="group flex items-center justify-center gap-2 w-full h-9 shrink-0">
|
||||||
<div className="size-7" />
|
|
||||||
<div className="flex items-center justify-center shrink-0 h-7">
|
<div className="flex items-center justify-center shrink-0 h-7">
|
||||||
<div className="relative flex items-center gap-2">
|
<div className="relative flex items-center gap-2">
|
||||||
<div
|
<div
|
||||||
contentEditable
|
contentEditable
|
||||||
suppressContentEditableWarning={true}
|
suppressContentEditableWarning={true}
|
||||||
onBlur={(e) => setTitle(e.currentTarget.textContent)}
|
onBlur={(e) => setTitle(e.currentTarget.textContent)}
|
||||||
className="text-sm font-medium focus:outline-none"
|
className="text-[12px] font-semibold focus:outline-none"
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
</div>
|
</div>
|
||||||
@ -193,9 +192,9 @@ function Header({ label, name }: { label: string; name: string }) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => saveNewTitle()}
|
onClick={() => saveNewTitle()}
|
||||||
className="text-teal-500 hover:text-teal-600"
|
className="text-teal-500 hover:text-teal-600 inline-flex items-center justify-center size-6 border-[.5px] border-neutral-200 dark:border-neutral-800 shadow shadow-neutral-200/50 dark:shadow-none rounded-full bg-white dark:bg-black"
|
||||||
>
|
>
|
||||||
<Check className="size-4" />
|
<Check className="size-3" weight="bold" />
|
||||||
</button>
|
</button>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
@ -203,9 +202,9 @@ function Header({ label, name }: { label: string; name: string }) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={(e) => showContextMenu(e)}
|
onClick={(e) => showContextMenu(e)}
|
||||||
className="inline-flex items-center justify-center rounded-lg size-7 hover:bg-black/10 dark:hover:bg-white/10"
|
className="hidden shrink-0 group-hover:inline-flex items-center justify-center size-6 border-[.5px] border-neutral-200 dark:border-neutral-800 shadow shadow-neutral-200/50 dark:shadow-none rounded-full bg-white dark:bg-black"
|
||||||
>
|
>
|
||||||
<DotsThree className="size-5" />
|
<CaretDown className="size-3" weight="bold" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -94,7 +94,6 @@ export function NoteContent({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"select-text text-pretty content-break overflow-hidden",
|
"select-text text-pretty content-break overflow-hidden",
|
||||||
event.content.length > 500 ? "max-h-[250px] gradient-mask-b-0" : "",
|
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -13,12 +13,7 @@ export const Quote = memo(function Quote({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Note.Provider event={event}>
|
<Note.Provider event={event}>
|
||||||
<Note.Root
|
<Note.Root className={cn("", className)}>
|
||||||
className={cn(
|
|
||||||
"bg-white dark:bg-black/20 rounded-xl flex flex-col gap-3 shadow-primary dark:ring-1 ring-neutral-800/50",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<Note.Child event={event.quote} isRoot />
|
<Note.Child event={event.quote} isRoot />
|
||||||
<div className="flex items-center gap-2 px-3">
|
<div className="flex items-center gap-2 px-3">
|
||||||
|
@ -15,12 +15,7 @@ export const RepostNote = memo(function RepostNote({
|
|||||||
const { isLoading, isError, data } = useEvent(event.repostId);
|
const { isLoading, isError, data } = useEvent(event.repostId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Note.Root
|
<Note.Root className={cn("", className)}>
|
||||||
className={cn(
|
|
||||||
"bg-white dark:bg-black/20 rounded-xl shadow-primary dark:ring-1 ring-neutral-800/50",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center h-20 gap-2">
|
<div className="flex items-center justify-center h-20 gap-2">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
|
@ -12,12 +12,7 @@ export const TextNote = memo(function TextNote({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Note.Provider event={event}>
|
<Note.Provider event={event}>
|
||||||
<Note.Root
|
<Note.Root className={cn("", className)}>
|
||||||
className={cn(
|
|
||||||
"bg-white dark:bg-black/20 rounded-xl shadow-primary dark:ring-1 dark:ring-white/5",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between px-3 h-14">
|
<div className="flex items-center justify-between px-3 h-14">
|
||||||
<Note.User />
|
<Note.User />
|
||||||
<Note.Menu />
|
<Note.Menu />
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { appSettings, cn } from "@/commons";
|
import { cn } from "@/commons";
|
||||||
import { User } from "@/components/user";
|
import { User } from "@/components/user";
|
||||||
import { LumeWindow } from "@/system";
|
import { LumeWindow } from "@/system";
|
||||||
import { CaretDown, Feather, MagnifyingGlass } from "@phosphor-icons/react";
|
import { CaretDown, Feather, MagnifyingGlass } from "@phosphor-icons/react";
|
||||||
import { Outlet, createLazyFileRoute } from "@tanstack/react-router";
|
import { Outlet, createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import { useStore } from "@tanstack/react-store";
|
|
||||||
import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu";
|
import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu";
|
||||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||||
import { memo, useCallback } from "react";
|
import { memo, useCallback } from "react";
|
||||||
@ -14,7 +13,6 @@ export const Route = createLazyFileRoute("/$account/_app")({
|
|||||||
|
|
||||||
function Screen() {
|
function Screen() {
|
||||||
const context = Route.useRouteContext();
|
const context = Route.useRouteContext();
|
||||||
const transparent = useStore(appSettings, (state) => state.transparent);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-screen h-screen">
|
<div className="flex flex-col w-screen h-screen">
|
||||||
@ -54,14 +52,7 @@ function Screen() {
|
|||||||
className="relative z-[200] flex-1 flex items-center justify-end gap-1"
|
className="relative z-[200] flex-1 flex items-center justify-end gap-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="flex-1 bg-neutral-100 dark:bg-neutral-900 border-t-[.5px] border-black/20 dark:border-white/20">
|
||||||
className={cn(
|
|
||||||
"flex-1",
|
|
||||||
transparent
|
|
||||||
? ""
|
|
||||||
: "bg-white dark:bg-black border-t border-black/20 dark:border-white/20",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -179,8 +179,8 @@ function Screen() {
|
|||||||
<Column key={column.label} column={column} />
|
<Column key={column.label} column={column} />
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
<div className="shrink-0 p-2 h-full w-[450px]">
|
<div className="shrink-0 p-2 h-full w-[440px]">
|
||||||
<div className="size-full bg-black/5 dark:bg-white/15 rounded-xl flex items-center justify-center">
|
<div className="size-full flex items-center justify-center">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => LumeWindow.openColumnsGallery()}
|
onClick={() => LumeWindow.openColumnsGallery()}
|
||||||
|
@ -49,6 +49,7 @@ function Screen() {
|
|||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
queryKey: [search.label, search.account],
|
queryKey: [search.label, search.account],
|
||||||
});
|
});
|
||||||
|
// @ts-ignore, tanstack router bug.
|
||||||
navigate({ to: search.redirect, search: { ...search, name: title } });
|
navigate({ to: search.redirect, search: { ...search, name: title } });
|
||||||
} else {
|
} else {
|
||||||
await message(res.error, {
|
await message(res.error, {
|
||||||
@ -69,10 +70,10 @@ function Screen() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col w-4/5 max-w-full gap-3">
|
<div className="flex flex-col w-4/5 max-w-full gap-3">
|
||||||
<div className="flex items-center w-full rounded-lg h-9 shrink-0 bg-black/5 dark:bg-white/5">
|
<div className="flex items-center w-full rounded-lg h-9 shrink-0 bg-neutral-200 dark:bg-neutral-800">
|
||||||
<label
|
<label
|
||||||
htmlFor="name"
|
htmlFor="name"
|
||||||
className="w-16 text-sm font-semibold text-center border-r border-black/10 dark:border-white/10 shrink-0"
|
className="w-16 text-sm font-semibold text-center border-r border-neutral-300 dark:border-neutral-700 shrink-0"
|
||||||
>
|
>
|
||||||
Name
|
Name
|
||||||
</label>
|
</label>
|
||||||
@ -85,19 +86,19 @@ function Screen() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center w-full gap-3">
|
<div className="flex flex-col items-center w-full gap-3">
|
||||||
<div className="overflow-y-auto scrollbar-none p-2 w-full h-[450px] flex flex-col gap-3 bg-black/5 dark:bg-white/5 rounded-xl">
|
<div className="overflow-y-auto scrollbar-none p-2 w-full h-[450px] flex flex-col gap-3 bg-neutral-200 dark:bg-neutral-900 rounded-xl">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<input
|
<input
|
||||||
name="npub"
|
name="npub"
|
||||||
value={npub}
|
value={npub}
|
||||||
onChange={(e) => setNpub(e.target.value)}
|
onChange={(e) => setNpub(e.target.value)}
|
||||||
placeholder="npub1..."
|
placeholder="npub1..."
|
||||||
className="w-full px-3 text-sm border-none rounded-lg h-9 bg-black/10 dark:bg-white/10 placeholder:text-neutral-600 focus:border-neutral-500 focus:ring-0 dark:placeholder:text-neutral-400"
|
className="w-full px-3 text-sm border-none rounded-lg h-9 bg-neutral-300 dark:bg-neutral-700 placeholder:text-neutral-600 focus:border-neutral-500 focus:ring-0 dark:placeholder:text-neutral-400"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => addUser()}
|
onClick={() => addUser()}
|
||||||
className="inline-flex items-center justify-center text-white rounded-lg size-9 bg-black/20 dark:bg-white/20 shrink-0 hover:bg-blue-500"
|
className="inline-flex items-center justify-center text-neutral-500 rounded-lg size-9 bg-neutral-300 dark:bg-neutral-700 shrink-0 hover:bg-blue-500 hover:text-white"
|
||||||
>
|
>
|
||||||
<Plus className="size-5" />
|
<Plus className="size-5" />
|
||||||
</button>
|
</button>
|
||||||
@ -125,7 +126,7 @@ function Screen() {
|
|||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center text-sm rounded-lg bg-black/5 dark:bg-white/5 h-14">
|
<div className="flex items-center justify-center text-sm rounded-lg bg-neutral-300 dark:bg-neutral-700 h-14">
|
||||||
Empty.
|
Empty.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -29,9 +29,12 @@ function Screen() {
|
|||||||
<ScrollArea.Root
|
<ScrollArea.Root
|
||||||
type={"scroll"}
|
type={"scroll"}
|
||||||
scrollHideDelay={300}
|
scrollHideDelay={300}
|
||||||
className="overflow-hidden size-full flex-1"
|
className="overflow-hidden size-full px-3"
|
||||||
>
|
>
|
||||||
<ScrollArea.Viewport ref={ref} className="h-full pt-1 px-3 pb-3">
|
<ScrollArea.Viewport
|
||||||
|
ref={ref}
|
||||||
|
className="relative h-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
>
|
||||||
<Virtualizer scrollRef={ref}>
|
<Virtualizer scrollRef={ref}>
|
||||||
<RootEvent />
|
<RootEvent />
|
||||||
<ReplyList />
|
<ReplyList />
|
||||||
@ -54,28 +57,24 @@ function RootEvent() {
|
|||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white flex items-center justify-center h-32 dark:bg-black/10 rounded-xl shadow-primary dark:ring-1 dark:ring-white/5">
|
<div className="flex items-center gap-2 text-sm">
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<Spinner />
|
||||||
<Spinner />
|
Loading...
|
||||||
Loading...
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white flex items-center justify-center h-32 dark:bg-black/10 rounded-xl shadow-primary dark:ring-1 dark:ring-white/5">
|
<div className="flex items-center gap-2 text-sm text-red-500">
|
||||||
<div className="flex items-center gap-2 text-sm text-red-500">
|
{error.message}
|
||||||
{error.message}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Note.Provider event={event}>
|
<Note.Provider event={event}>
|
||||||
<Note.Root className="bg-white dark:bg-white/10 rounded-xl shadow-primary dark:shadow-none">
|
<Note.Root className="border-b-[.5px] border-neutral-300 dark:border-neutral-700">
|
||||||
<div className="flex items-center justify-between px-3 h-14">
|
<div className="flex items-center justify-between px-3 h-14">
|
||||||
<Note.User />
|
<Note.User />
|
||||||
<Note.Menu />
|
<Note.Menu />
|
||||||
@ -217,7 +216,7 @@ function ReplyList() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="px-3">
|
||||||
<div className="flex items-center text-sm font-semibold h-14 text-neutral-600 dark:text-white/30">
|
<div className="flex items-center text-sm font-semibold h-14 text-neutral-600 dark:text-white/30">
|
||||||
All replies
|
All replies
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,6 @@ import type { LumeColumn } from "@/types";
|
|||||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||||
import Avatar from "boring-avatars";
|
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/columns/_layout/gallery")({
|
export const Route = createLazyFileRoute("/columns/_layout/gallery")({
|
||||||
component: Screen,
|
component: Screen,
|
||||||
@ -26,31 +25,14 @@ function Screen() {
|
|||||||
{columns.map((column) => (
|
{columns.map((column) => (
|
||||||
<div
|
<div
|
||||||
key={column.label}
|
key={column.label}
|
||||||
className="mb-3 group flex px-2 items-center justify-between h-16 rounded-xl bg-white dark:bg-white/20 shadow-sm shadow-neutral-500/10"
|
className="mb-3 group flex px-4 items-center justify-between h-16 rounded-xl bg-white dark:bg-black border-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
>
|
>
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="text-sm">
|
||||||
<div className="size-11 bg-neutral-200 rounded-lg overflow-hidden">
|
<div className="mb-px leading-tight font-semibold">
|
||||||
<Avatar
|
{column.name}
|
||||||
name={column.name}
|
|
||||||
size={44}
|
|
||||||
square={true}
|
|
||||||
variant="pixel"
|
|
||||||
colors={[
|
|
||||||
"#84cc16",
|
|
||||||
"#22c55e",
|
|
||||||
"#0ea5e9",
|
|
||||||
"#3b82f6",
|
|
||||||
"#6366f1",
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm">
|
<div className="leading-tight text-neutral-500 dark:text-neutral-400">
|
||||||
<div className="mb-px leading-tight font-semibold">
|
{column.description}
|
||||||
{column.name}
|
|
||||||
</div>
|
|
||||||
<div className="leading-tight text-neutral-500 dark:text-neutral-400">
|
|
||||||
{column.description}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
@ -3,7 +3,7 @@ import { toLumeEvents } from "@/commons";
|
|||||||
import { Quote, RepostNote, Spinner, TextNote } from "@/components";
|
import { Quote, RepostNote, Spinner, TextNote } from "@/components";
|
||||||
import type { LumeEvent } from "@/system";
|
import type { LumeEvent } from "@/system";
|
||||||
import { Kind } from "@/types";
|
import { Kind } from "@/types";
|
||||||
import { ArrowCircleRight } from "@phosphor-icons/react";
|
import { ArrowDown } from "@phosphor-icons/react";
|
||||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
@ -48,12 +48,30 @@ export function Screen() {
|
|||||||
if (!event) return;
|
if (!event) return;
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case Kind.Repost:
|
case Kind.Repost:
|
||||||
return <RepostNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<RepostNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
default: {
|
default: {
|
||||||
if (event.isQuote) {
|
if (event.isQuote) {
|
||||||
return <Quote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<Quote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return <TextNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<TextNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,9 +83,12 @@ export function Screen() {
|
|||||||
<ScrollArea.Root
|
<ScrollArea.Root
|
||||||
type={"scroll"}
|
type={"scroll"}
|
||||||
scrollHideDelay={300}
|
scrollHideDelay={300}
|
||||||
className="overflow-hidden size-full"
|
className="overflow-hidden size-full px-3"
|
||||||
>
|
>
|
||||||
<ScrollArea.Viewport ref={ref} className="h-full px-3 pb-3">
|
<ScrollArea.Viewport
|
||||||
|
ref={ref}
|
||||||
|
className="relative h-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
>
|
||||||
<Virtualizer scrollRef={ref}>
|
<Virtualizer scrollRef={ref}>
|
||||||
{isFetching && !isLoading && !isFetchingNextPage ? (
|
{isFetching && !isLoading && !isFetchingNextPage ? (
|
||||||
<div className="z-50 fixed top-0 left-0 w-full h-14 flex items-center justify-center px-3">
|
<div className="z-50 fixed top-0 left-0 w-full h-14 flex items-center justify-center px-3">
|
||||||
@ -79,11 +100,11 @@ export function Screen() {
|
|||||||
) : null}
|
) : null}
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center w-full h-16 gap-2">
|
<div className="flex items-center justify-center w-full h-16 gap-2">
|
||||||
<Spinner className="size-5" />
|
<Spinner className="size-4" />
|
||||||
<span className="text-sm font-medium">Loading...</span>
|
<span className="text-sm font-medium">Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
) : !data.length ? (
|
) : !data.length ? (
|
||||||
<div className="mb-3 flex items-center justify-center h-20 text-sm rounded-xl bg-black/5 dark:bg-white/5">
|
<div className="mb-3 flex items-center justify-center h-20 text-sm">
|
||||||
🎉 Yo. You're catching up on all latest notes.
|
🎉 Yo. You're catching up on all latest notes.
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@ -95,13 +116,13 @@ export function Screen() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => fetchNextPage()}
|
onClick={() => fetchNextPage()}
|
||||||
disabled={isFetchingNextPage || isLoading}
|
disabled={isFetchingNextPage || isLoading}
|
||||||
className="inline-flex items-center justify-center w-full gap-2 px-3 font-medium h-9 rounded-xl bg-black/5 hover:bg-black/10 focus:outline-none dark:bg-white/10 dark:hover:bg-white/20"
|
className="inline-flex items-center justify-center w-full gap-2 px-3 text-sm font-medium text-blue-500 h-11 focus:outline-none"
|
||||||
>
|
>
|
||||||
{isFetchingNextPage ? (
|
{isFetchingNextPage ? (
|
||||||
<Spinner className="size-5" />
|
<Spinner className="size-4" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ArrowCircleRight className="size-5" />
|
<ArrowDown className="size-4" />
|
||||||
Load more
|
Load more
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -3,7 +3,7 @@ import { toLumeEvents } from "@/commons";
|
|||||||
import { Quote, RepostNote, Spinner, TextNote } from "@/components";
|
import { Quote, RepostNote, Spinner, TextNote } from "@/components";
|
||||||
import type { LumeEvent } from "@/system";
|
import type { LumeEvent } from "@/system";
|
||||||
import { Kind } from "@/types";
|
import { Kind } from "@/types";
|
||||||
import { ArrowCircleRight } from "@phosphor-icons/react";
|
import { ArrowDown } from "@phosphor-icons/react";
|
||||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
@ -49,12 +49,30 @@ export function Screen() {
|
|||||||
if (!event) return;
|
if (!event) return;
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case Kind.Repost:
|
case Kind.Repost:
|
||||||
return <RepostNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<RepostNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
default: {
|
default: {
|
||||||
if (event.isQuote) {
|
if (event.isQuote) {
|
||||||
return <Quote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<Quote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return <TextNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<TextNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,9 +84,12 @@ export function Screen() {
|
|||||||
<ScrollArea.Root
|
<ScrollArea.Root
|
||||||
type={"scroll"}
|
type={"scroll"}
|
||||||
scrollHideDelay={300}
|
scrollHideDelay={300}
|
||||||
className="overflow-hidden size-full"
|
className="overflow-hidden size-full px-3"
|
||||||
>
|
>
|
||||||
<ScrollArea.Viewport ref={ref} className="h-full px-3 pb-3">
|
<ScrollArea.Viewport
|
||||||
|
ref={ref}
|
||||||
|
className="relative h-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
>
|
||||||
<Virtualizer scrollRef={ref}>
|
<Virtualizer scrollRef={ref}>
|
||||||
{isFetching && !isLoading && !isFetchingNextPage ? (
|
{isFetching && !isLoading && !isFetchingNextPage ? (
|
||||||
<div className="z-50 fixed top-0 left-0 w-full h-14 flex items-center justify-center px-3">
|
<div className="z-50 fixed top-0 left-0 w-full h-14 flex items-center justify-center px-3">
|
||||||
@ -80,11 +101,11 @@ export function Screen() {
|
|||||||
) : null}
|
) : null}
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center w-full h-16 gap-2">
|
<div className="flex items-center justify-center w-full h-16 gap-2">
|
||||||
<Spinner className="size-5" />
|
<Spinner className="size-4" />
|
||||||
<span className="text-sm font-medium">Loading...</span>
|
<span className="text-sm font-medium">Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
) : !data.length ? (
|
) : !data.length ? (
|
||||||
<div className="mb-3 flex items-center justify-center h-20 text-sm rounded-xl bg-black/5 dark:bg-white/5">
|
<div className="mb-3 flex items-center justify-center h-20 text-sm">
|
||||||
🎉 Yo. You're catching up on all latest notes.
|
🎉 Yo. You're catching up on all latest notes.
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@ -96,13 +117,13 @@ export function Screen() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => fetchNextPage()}
|
onClick={() => fetchNextPage()}
|
||||||
disabled={isFetchingNextPage || isLoading}
|
disabled={isFetchingNextPage || isLoading}
|
||||||
className="inline-flex items-center justify-center w-full gap-2 px-3 font-medium h-9 rounded-xl bg-black/5 hover:bg-black/10 focus:outline-none dark:bg-white/10 dark:hover:bg-white/20"
|
className="inline-flex items-center justify-center w-full gap-2 px-3 text-sm font-medium text-blue-500 h-11 focus:outline-none"
|
||||||
>
|
>
|
||||||
{isFetchingNextPage ? (
|
{isFetchingNextPage ? (
|
||||||
<Spinner className="size-5" />
|
<Spinner className="size-4" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ArrowCircleRight className="size-5" />
|
<ArrowDown className="size-4" />
|
||||||
Load more
|
Load more
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -3,7 +3,7 @@ import { toLumeEvents } from "@/commons";
|
|||||||
import { Quote, RepostNote, Spinner, TextNote } from "@/components";
|
import { Quote, RepostNote, Spinner, TextNote } from "@/components";
|
||||||
import type { LumeEvent } from "@/system";
|
import type { LumeEvent } from "@/system";
|
||||||
import { Kind } from "@/types";
|
import { Kind } from "@/types";
|
||||||
import { ArrowCircleRight } from "@phosphor-icons/react";
|
import { ArrowDown } from "@phosphor-icons/react";
|
||||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
@ -50,12 +50,30 @@ export function Screen() {
|
|||||||
if (!event) return;
|
if (!event) return;
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case Kind.Repost:
|
case Kind.Repost:
|
||||||
return <RepostNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<RepostNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
default: {
|
default: {
|
||||||
if (event.isQuote) {
|
if (event.isQuote) {
|
||||||
return <Quote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<Quote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return <TextNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<TextNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,9 +85,12 @@ export function Screen() {
|
|||||||
<ScrollArea.Root
|
<ScrollArea.Root
|
||||||
type={"scroll"}
|
type={"scroll"}
|
||||||
scrollHideDelay={300}
|
scrollHideDelay={300}
|
||||||
className="overflow-hidden size-full"
|
className="overflow-hidden size-full px-3"
|
||||||
>
|
>
|
||||||
<ScrollArea.Viewport ref={ref} className="h-full px-3 pb-3">
|
<ScrollArea.Viewport
|
||||||
|
ref={ref}
|
||||||
|
className="relative h-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
>
|
||||||
<Virtualizer scrollRef={ref}>
|
<Virtualizer scrollRef={ref}>
|
||||||
{isFetching && !isLoading && !isFetchingNextPage ? (
|
{isFetching && !isLoading && !isFetchingNextPage ? (
|
||||||
<div className="z-50 fixed top-0 left-0 w-full h-14 flex items-center justify-center px-3">
|
<div className="z-50 fixed top-0 left-0 w-full h-14 flex items-center justify-center px-3">
|
||||||
@ -81,11 +102,11 @@ export function Screen() {
|
|||||||
) : null}
|
) : null}
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center w-full h-16 gap-2">
|
<div className="flex items-center justify-center w-full h-16 gap-2">
|
||||||
<Spinner className="size-5" />
|
<Spinner className="size-4" />
|
||||||
<span className="text-sm font-medium">Loading...</span>
|
<span className="text-sm font-medium">Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
) : !data.length ? (
|
) : !data.length ? (
|
||||||
<div className="mb-3 flex items-center justify-center h-20 text-sm rounded-xl bg-black/5 dark:bg-white/5">
|
<div className="mb-3 flex items-center justify-center h-20 text-sm">
|
||||||
🎉 Yo. You're catching up on all latest notes.
|
🎉 Yo. You're catching up on all latest notes.
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@ -97,13 +118,13 @@ export function Screen() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => fetchNextPage()}
|
onClick={() => fetchNextPage()}
|
||||||
disabled={isFetchingNextPage || isLoading}
|
disabled={isFetchingNextPage || isLoading}
|
||||||
className="inline-flex items-center justify-center w-full gap-2 px-3 font-medium h-9 rounded-xl bg-black/5 hover:bg-black/10 focus:outline-none dark:bg-white/10 dark:hover:bg-white/20"
|
className="inline-flex items-center justify-center w-full gap-2 px-3 text-sm font-medium text-blue-500 h-11 focus:outline-none"
|
||||||
>
|
>
|
||||||
{isFetchingNextPage ? (
|
{isFetchingNextPage ? (
|
||||||
<Spinner className="size-5" />
|
<Spinner className="size-4" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ArrowCircleRight className="size-5" />
|
<ArrowDown className="size-4" />
|
||||||
Load more
|
Load more
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -3,7 +3,7 @@ import { toLumeEvents } from "@/commons";
|
|||||||
import { Quote, RepostNote, Spinner, TextNote } from "@/components";
|
import { Quote, RepostNote, Spinner, TextNote } from "@/components";
|
||||||
import { LumeEvent } from "@/system";
|
import { LumeEvent } from "@/system";
|
||||||
import { Kind, type Meta } from "@/types";
|
import { Kind, type Meta } from "@/types";
|
||||||
import { ArrowCircleRight, ArrowUp } from "@phosphor-icons/react";
|
import { ArrowDown, ArrowUp } from "@phosphor-icons/react";
|
||||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||||
import { type InfiniteData, useInfiniteQuery } from "@tanstack/react-query";
|
import { type InfiniteData, useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
@ -68,12 +68,30 @@ export function Screen() {
|
|||||||
if (!event) return;
|
if (!event) return;
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case Kind.Repost:
|
case Kind.Repost:
|
||||||
return <RepostNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<RepostNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
default: {
|
default: {
|
||||||
if (event.isQuote) {
|
if (event.isQuote) {
|
||||||
return <Quote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<Quote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return <TextNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<TextNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,6 +113,7 @@ export function Screen() {
|
|||||||
return (
|
return (
|
||||||
<Navigate
|
<Navigate
|
||||||
to="/columns/create-newsfeed/users"
|
to="/columns/create-newsfeed/users"
|
||||||
|
// @ts-ignore, tanstack router bug.
|
||||||
search={{ label, account, redirect: location.href }}
|
search={{ label, account, redirect: location.href }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -104,10 +123,13 @@ export function Screen() {
|
|||||||
<ScrollArea.Root
|
<ScrollArea.Root
|
||||||
type={"scroll"}
|
type={"scroll"}
|
||||||
scrollHideDelay={300}
|
scrollHideDelay={300}
|
||||||
className="overflow-hidden size-full"
|
className="overflow-hidden size-full px-3"
|
||||||
>
|
>
|
||||||
<ScrollArea.Viewport ref={ref} className="relative h-full px-3 pb-3">
|
<ScrollArea.Viewport
|
||||||
<Listerner />
|
ref={ref}
|
||||||
|
className="relative h-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
>
|
||||||
|
<Listener />
|
||||||
<Virtualizer scrollRef={ref}>
|
<Virtualizer scrollRef={ref}>
|
||||||
{isFetching && !isLoading && !isFetchingNextPage ? (
|
{isFetching && !isLoading && !isFetchingNextPage ? (
|
||||||
<div className="z-50 fixed top-0 left-0 w-full h-14 flex items-center justify-center px-3">
|
<div className="z-50 fixed top-0 left-0 w-full h-14 flex items-center justify-center px-3">
|
||||||
@ -119,11 +141,11 @@ export function Screen() {
|
|||||||
) : null}
|
) : null}
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center w-full h-16 gap-2">
|
<div className="flex items-center justify-center w-full h-16 gap-2">
|
||||||
<Spinner className="size-5" />
|
<Spinner className="size-4" />
|
||||||
<span className="text-sm font-medium">Loading...</span>
|
<span className="text-sm font-medium">Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
) : !data.length ? (
|
) : !data.length ? (
|
||||||
<div className="mb-3 flex items-center justify-center h-20 text-sm rounded-xl bg-black/5 dark:bg-white/5">
|
<div className="mb-3 flex items-center justify-center h-20 text-sm">
|
||||||
🎉 Yo. You're catching up on all latest notes.
|
🎉 Yo. You're catching up on all latest notes.
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@ -135,13 +157,13 @@ export function Screen() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => fetchNextPage()}
|
onClick={() => fetchNextPage()}
|
||||||
disabled={isFetchingNextPage || isLoading}
|
disabled={isFetchingNextPage || isLoading}
|
||||||
className="inline-flex items-center justify-center w-full gap-2 px-3 font-medium h-9 rounded-xl bg-black/5 hover:bg-black/10 focus:outline-none dark:bg-white/10 dark:hover:bg-white/20"
|
className="inline-flex items-center justify-center w-full gap-2 px-3 text-sm font-medium text-blue-500 h-11 focus:outline-none"
|
||||||
>
|
>
|
||||||
{isFetchingNextPage ? (
|
{isFetchingNextPage ? (
|
||||||
<Spinner className="size-5" />
|
<Spinner className="size-4" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ArrowCircleRight className="size-5" />
|
<ArrowDown className="size-4" />
|
||||||
Load more
|
Load more
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -161,7 +183,7 @@ export function Screen() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Listerner = memo(function Listerner() {
|
const Listener = memo(function Listerner() {
|
||||||
const { queryClient } = Route.useRouteContext();
|
const { queryClient } = Route.useRouteContext();
|
||||||
const { label, account } = Route.useSearch();
|
const { label, account } = Route.useSearch();
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { commands } from "@/commands.gen";
|
import { commands } from "@/commands.gen";
|
||||||
import { decodeZapInvoice, formatCreatedAt } from "@/commons";
|
import { decodeZapInvoice, formatCreatedAt } from "@/commons";
|
||||||
import { Note, Spinner, User } from "@/components";
|
import { Note, RepostIcon, Spinner, User } from "@/components";
|
||||||
import { LumeEvent, LumeWindow, useEvent } from "@/system";
|
import { LumeEvent, LumeWindow, useEvent } from "@/system";
|
||||||
import { Kind, type NostrEvent } from "@/types";
|
import { Kind, type NostrEvent } from "@/types";
|
||||||
import { Info, Repeat } from "@phosphor-icons/react";
|
import { Info } from "@phosphor-icons/react";
|
||||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||||
import * as Tabs from "@radix-ui/react-tabs";
|
import * as Tabs from "@radix-ui/react-tabs";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
@ -99,110 +99,114 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs.Root defaultValue="replies" className="flex flex-col h-full">
|
<div className="px-3 h-full overflow-y-auto">
|
||||||
<Tabs.List className="h-8 shrink-0 flex items-center">
|
<Tabs.Root
|
||||||
<Tabs.Trigger
|
defaultValue="replies"
|
||||||
className="flex-1 inline-flex h-8 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-black/10 dark:border-white/10 data-[state=active]:border-black/30 dark:data-[state=active]:border-white/30 data-[state=inactive]:opacity-50"
|
className="flex flex-col h-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
value="replies"
|
|
||||||
>
|
|
||||||
Replies
|
|
||||||
</Tabs.Trigger>
|
|
||||||
<Tabs.Trigger
|
|
||||||
className="flex-1 inline-flex h-8 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-black/10 dark:border-white/10 data-[state=active]:border-black/30 dark:data-[state=active]:border-white/30 data-[state=inactive]:opacity-50"
|
|
||||||
value="reactions"
|
|
||||||
>
|
|
||||||
Reactions
|
|
||||||
</Tabs.Trigger>
|
|
||||||
<Tabs.Trigger
|
|
||||||
className="flex-1 inline-flex h-8 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-black/10 dark:border-white/10 data-[state=active]:border-black/30 dark:data-[state=active]:border-white/30 data-[state=inactive]:opacity-50"
|
|
||||||
value="zaps"
|
|
||||||
>
|
|
||||||
Zaps
|
|
||||||
</Tabs.Trigger>
|
|
||||||
</Tabs.List>
|
|
||||||
<ScrollArea.Root
|
|
||||||
type={"scroll"}
|
|
||||||
scrollHideDelay={300}
|
|
||||||
className="min-h-0 flex-1 overflow-x-hidden"
|
|
||||||
>
|
>
|
||||||
<Tab value="replies">
|
<Tabs.List className="h-11 shrink-0 flex items-center">
|
||||||
{data.texts.map((event, index) => (
|
<Tabs.Trigger
|
||||||
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
|
className="flex-1 inline-flex h-11 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-neutral-100 dark:border-neutral-900 data-[state=active]:border-neutral-200 dark:data-[state=active]:border-neutral-800 data-[state=inactive]:opacity-50"
|
||||||
<TextNote key={event.id + index} event={event} />
|
value="replies"
|
||||||
))}
|
>
|
||||||
</Tab>
|
Replies
|
||||||
<Tab value="reactions">
|
</Tabs.Trigger>
|
||||||
{[...data.reactions.entries()].map(([root, events]) => (
|
<Tabs.Trigger
|
||||||
<div
|
className="flex-1 inline-flex h-11 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-neutral-100 dark:border-neutral-900 data-[state=active]:border-neutral-200 dark:data-[state=active]:border-neutral-800 data-[state=inactive]:opacity-50"
|
||||||
key={root}
|
value="reactions"
|
||||||
className="flex flex-col gap-1 p-3 mb-3 bg-white dark:bg-black/20 rounded-xl shadow-primary dark:ring-1 dark:ring-white/5"
|
>
|
||||||
>
|
Reactions
|
||||||
<div className="flex flex-col flex-1 min-w-0 gap-2">
|
</Tabs.Trigger>
|
||||||
<div className="flex items-center gap-2 pb-2 border-b border-black/5 dark:border-white/5">
|
<Tabs.Trigger
|
||||||
<RootNote id={root} />
|
className="flex-1 inline-flex h-11 items-center justify-center gap-2 px-3 text-sm font-medium border-b border-neutral-100 dark:border-neutral-900 data-[state=active]:border-neutral-200 dark:data-[state=active]:border-neutral-800 data-[state=inactive]:opacity-50"
|
||||||
</div>
|
value="zaps"
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
>
|
||||||
{events.map((event) => (
|
Zaps
|
||||||
<User.Provider key={event.id} pubkey={event.pubkey}>
|
</Tabs.Trigger>
|
||||||
<User.Root className="shrink-0 flex rounded-full h-7 bg-black/10 dark:bg-white/10 p-[2px]">
|
</Tabs.List>
|
||||||
<User.Avatar className="flex-1 rounded-full size-6" />
|
<ScrollArea.Root
|
||||||
<div className="inline-flex items-center justify-center flex-1 text-xs truncate rounded-full size-7">
|
type={"scroll"}
|
||||||
{event.kind === Kind.Reaction ? (
|
scrollHideDelay={300}
|
||||||
event.content === "+" ? (
|
className="min-h-0 flex-1 overflow-x-hidden"
|
||||||
"👍"
|
|
||||||
) : (
|
|
||||||
event.content
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<Repeat className="text-teal-400 size-4 dark:text-teal-600" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</User.Root>
|
|
||||||
</User.Provider>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Tab>
|
|
||||||
<Tab value="zaps">
|
|
||||||
{[...data.zaps.entries()].map(([root, events]) => (
|
|
||||||
<div
|
|
||||||
key={root}
|
|
||||||
className="flex flex-col gap-1 p-3 mb-3 bg-white dark:bg-black/20 rounded-xl shadow-primary dark:ring-1 dark:ring-white/5"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col flex-1 min-w-0 gap-2">
|
|
||||||
<div className="flex items-center gap-2 pb-2 border-b border-black/5 dark:border-white/5">
|
|
||||||
<RootNote id={root} />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
|
||||||
{events.map((event) => (
|
|
||||||
<User.Provider
|
|
||||||
key={event.id}
|
|
||||||
pubkey={event.tags.find((tag) => tag[0] === "P")[1]}
|
|
||||||
>
|
|
||||||
<User.Root className="shrink-0 flex gap-1.5 rounded-full h-7 bg-black/10 dark:bg-white/10 p-[2px]">
|
|
||||||
<User.Avatar className="rounded-full size-6" />
|
|
||||||
<div className="flex-1 h-6 w-max pr-1.5 rounded-full inline-flex items-center justify-center text-xs font-semibold truncate">
|
|
||||||
₿ {decodeZapInvoice(event.tags).bitcoinFormatted}
|
|
||||||
</div>
|
|
||||||
</User.Root>
|
|
||||||
</User.Provider>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Tab>
|
|
||||||
<ScrollArea.Scrollbar
|
|
||||||
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
|
||||||
orientation="vertical"
|
|
||||||
>
|
>
|
||||||
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
|
<Tab value="replies">
|
||||||
</ScrollArea.Scrollbar>
|
{data.texts.map((event, index) => (
|
||||||
<ScrollArea.Corner className="bg-transparent" />
|
<TextNote key={event.id + index} event={event} />
|
||||||
</ScrollArea.Root>
|
))}
|
||||||
</Tabs.Root>
|
</Tab>
|
||||||
|
<Tab value="reactions">
|
||||||
|
{[...data.reactions.entries()].map(([root, events]) => (
|
||||||
|
<div
|
||||||
|
key={root}
|
||||||
|
className="p-3 w-full border-b-[.5px] border-neutral-300 dark:border-neutral-700 hover:border-blue-500"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col flex-1 min-w-0 gap-2">
|
||||||
|
<div className="flex items-center gap-2 pb-2">
|
||||||
|
<RootNote id={root} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
{events.map((event) => (
|
||||||
|
<User.Provider key={event.id} pubkey={event.pubkey}>
|
||||||
|
<User.Root className="shrink-0 flex rounded-full h-7 bg-neutral-100 dark:bg-neutral-900 p-[2px]">
|
||||||
|
<User.Avatar className="flex-1 rounded-full size-6" />
|
||||||
|
<div className="inline-flex items-center justify-center flex-1 text-xs truncate rounded-full size-6">
|
||||||
|
{event.kind === Kind.Reaction ? (
|
||||||
|
event.content === "+" ? (
|
||||||
|
"👍"
|
||||||
|
) : (
|
||||||
|
event.content
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<RepostIcon className="text-teal-400 size-4 dark:text-teal-600" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</User.Root>
|
||||||
|
</User.Provider>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Tab>
|
||||||
|
<Tab value="zaps">
|
||||||
|
{[...data.zaps.entries()].map(([root, events]) => (
|
||||||
|
<div
|
||||||
|
key={root}
|
||||||
|
className="p-3 w-full border-b-[.5px] border-neutral-300 dark:border-neutral-700 hover:border-blue-500"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col flex-1 min-w-0 gap-2">
|
||||||
|
<div className="flex items-center gap-2 pb-2">
|
||||||
|
<RootNote id={root} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
{events.map((event) => (
|
||||||
|
<User.Provider
|
||||||
|
key={event.id}
|
||||||
|
pubkey={event.tags.find((tag) => tag[0] === "P")[1]}
|
||||||
|
>
|
||||||
|
<User.Root className="shrink-0 flex gap-1.5 rounded-full h-7 bg-black/10 dark:bg-white/10 p-[2px]">
|
||||||
|
<User.Avatar className="rounded-full size-6" />
|
||||||
|
<div className="flex-1 h-6 w-max pr-1.5 rounded-full inline-flex items-center justify-center text-xs font-semibold truncate">
|
||||||
|
₿ {decodeZapInvoice(event.tags).bitcoinFormatted}
|
||||||
|
</div>
|
||||||
|
</User.Root>
|
||||||
|
</User.Provider>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Tab>
|
||||||
|
<ScrollArea.Scrollbar
|
||||||
|
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
||||||
|
orientation="vertical"
|
||||||
|
>
|
||||||
|
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
|
||||||
|
</ScrollArea.Scrollbar>
|
||||||
|
<ScrollArea.Corner className="bg-transparent" />
|
||||||
|
</ScrollArea.Root>
|
||||||
|
</Tabs.Root>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +215,7 @@ function Tab({ value, children }: { value: string; children: ReactNode[] }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs.Content value={value} className="size-full">
|
<Tabs.Content value={value} className="size-full">
|
||||||
<ScrollArea.Viewport ref={ref} className="h-full p-3">
|
<ScrollArea.Viewport ref={ref} className="h-full">
|
||||||
<Virtualizer scrollRef={ref}>{children}</Virtualizer>
|
<Virtualizer scrollRef={ref}>{children}</Virtualizer>
|
||||||
</ScrollArea.Viewport>
|
</ScrollArea.Viewport>
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
@ -267,10 +271,10 @@ function TextNote({ event }: { event: LumeEvent }) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => LumeWindow.openEvent(event)}
|
onClick={() => LumeWindow.openEvent(event)}
|
||||||
className="w-full rounded-xl hover:ring-1 ring-blue-500 mb-3"
|
className="p-3 w-full border-b-[.5px] border-neutral-300 dark:border-neutral-700 hover:border-blue-500"
|
||||||
>
|
>
|
||||||
<Note.Provider event={event}>
|
<Note.Provider event={event}>
|
||||||
<Note.Root className="flex flex-col p-3 rounded-xl bg-white dark:bg-black/20 shadow-primary dark:ring-1 dark:ring-white/5">
|
<Note.Root className="flex flex-col">
|
||||||
<User.Provider pubkey={event.pubkey}>
|
<User.Provider pubkey={event.pubkey}>
|
||||||
<User.Root className="inline-flex items-center gap-2">
|
<User.Root className="inline-flex items-center gap-2">
|
||||||
<User.Avatar className="rounded-full size-9" />
|
<User.Avatar className="rounded-full size-9" />
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/columns/_layout/onboarding")({
|
export const Route = createLazyFileRoute("/columns/_layout/onboarding")({
|
||||||
@ -6,107 +7,97 @@ export const Route = createLazyFileRoute("/columns/_layout/onboarding")({
|
|||||||
|
|
||||||
function Screen() {
|
function Screen() {
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col py-6 gap-6 overflow-y-auto scrollbar-none">
|
<ScrollArea.Root
|
||||||
<div className="text-center flex flex-col items-center justify-center">
|
type={"scroll"}
|
||||||
<h1 className="text-2xl font-serif font-medium">Welcome to Lume</h1>
|
scrollHideDelay={300}
|
||||||
<p className="leading-tight text-neutral-700 dark:text-neutral-300">
|
className="overflow-hidden size-full"
|
||||||
Here are a few suggestions to help you get started.
|
>
|
||||||
</p>
|
<ScrollArea.Viewport className="relative h-full px-3 pb-3">
|
||||||
</div>
|
<div className="my-10 text-center flex flex-col items-center justify-center">
|
||||||
<div className="px-3 flex flex-col gap-3">
|
<h1 className="text-2xl font-serif font-medium">Welcome to Lume</h1>
|
||||||
<div className="relative flex flex-col items-center justify-center rounded-xl bg-black/10 dark:bg-white/10">
|
<p className="leading-tight text-neutral-700 dark:text-neutral-300">
|
||||||
<div className="absolute top-2 left-3 text-2xl font-semibold font-serif text-neutral-600 dark:text-neutral-400">
|
Here are a few suggestions to help you get started.
|
||||||
01.
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="relative flex flex-col items-center justify-center rounded-xl bg-neutral-200 dark:bg-neutral-800">
|
||||||
|
<div className="absolute top-2 left-3 text-2xl font-semibold font-serif text-neutral-600 dark:text-neutral-400">
|
||||||
|
01.
|
||||||
|
</div>
|
||||||
|
<div className="h-16 flex items-center justify-center shrink-0 px-3 text-lg select-text">
|
||||||
|
Navigate between columns.
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 w-3/4 h-full pb-10">
|
||||||
|
<video
|
||||||
|
className="h-auto w-full rounded-lg shadow-md"
|
||||||
|
controls
|
||||||
|
muted
|
||||||
|
preload="none"
|
||||||
|
poster="https://image.nostr.build/143354665d94b20013fde14ad05e53e958e11eec568a11b273921d1808c410cc.png"
|
||||||
|
>
|
||||||
|
<source
|
||||||
|
src="https://video.nostr.build/8fc3598ef85a1be292cee4ad6ad85b2c8bbf86da0aefd693e60416b56ec96e5f.mp4"
|
||||||
|
type="video/mp4"
|
||||||
|
/>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-16 flex items-center justify-center shrink-0 px-3 text-lg select-text">
|
<div className="relative flex flex-col items-center justify-center rounded-xl bg-neutral-200 dark:bg-neutral-800">
|
||||||
Navigate between columns.
|
<div className="absolute top-2 left-3 text-2xl font-semibold font-serif text-neutral-600 dark:text-neutral-400">
|
||||||
|
02.
|
||||||
|
</div>
|
||||||
|
<div className="h-16 flex items-center justify-center shrink-0 px-3 text-lg select-text">
|
||||||
|
Add a new column.
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 w-3/4 h-full pb-10">
|
||||||
|
<video
|
||||||
|
className="h-auto w-full rounded-lg shadow-md"
|
||||||
|
controls
|
||||||
|
muted
|
||||||
|
preload="none"
|
||||||
|
poster="https://image.nostr.build/216015bc81931725c23171700b4d2d73556ecfe3efe662afd7eea6627574b506.png"
|
||||||
|
>
|
||||||
|
<source
|
||||||
|
src="https://video.nostr.build/cdb842a1ffc6864bab009a668f68acb0a672e04e9538dbe7e2ac44182722d956.mp4"
|
||||||
|
type="video/mp4"
|
||||||
|
/>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 w-3/4 h-full pb-10">
|
<div className="relative flex flex-col items-center justify-center rounded-xl bg-neutral-200 dark:bg-neutral-800">
|
||||||
<video
|
<div className="absolute top-2 left-3 text-2xl font-semibold font-serif text-neutral-600 dark:text-neutral-400">
|
||||||
className="h-auto w-full aspect-square rounded-lg shadow-md transform"
|
03.
|
||||||
controls
|
</div>
|
||||||
muted
|
<div className="h-16 flex items-center justify-center shrink-0 px-3 text-lg select-text">
|
||||||
preload="none"
|
View a thread.
|
||||||
poster="/poster_1.jpeg"
|
</div>
|
||||||
>
|
<div className="flex-1 w-3/4 h-full pb-10">
|
||||||
<source
|
<video
|
||||||
src="https://video.nostr.build/692f71e2be47ecfc29edcbdaa198cc5979bfb9c900f05d78682895dd546d8d4f.mp4"
|
className="h-auto w-full rounded-lg shadow-md"
|
||||||
type="video/mp4"
|
controls
|
||||||
/>
|
muted
|
||||||
Your browser does not support the video tag.
|
preload="none"
|
||||||
</video>
|
poster="https://image.nostr.build/3a3ead93bc64224e6397df4097998708b5aef4c6e7104f8e28c77db47ad1625a.png"
|
||||||
|
>
|
||||||
|
<source
|
||||||
|
src="https://video.nostr.build/088636b03976a4e54c5053c718300816a7c3f9f361a1fe7d8e7a0f663ab6a582.mp4"
|
||||||
|
type="video/mp4"
|
||||||
|
/>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative flex flex-col items-center justify-center rounded-xl bg-black/10 dark:bg-white/10">
|
</ScrollArea.Viewport>
|
||||||
<div className="absolute top-2 left-3 text-2xl font-semibold font-serif text-neutral-600 dark:text-neutral-400">
|
<ScrollArea.Scrollbar
|
||||||
02.
|
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
||||||
</div>
|
orientation="vertical"
|
||||||
<div className="h-16 flex items-center justify-center shrink-0 px-3 text-lg select-text">
|
>
|
||||||
Switch between accounts.
|
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
|
||||||
</div>
|
</ScrollArea.Scrollbar>
|
||||||
<div className="flex-1 w-3/4 h-full pb-10">
|
<ScrollArea.Corner className="bg-transparent" />
|
||||||
<video
|
</ScrollArea.Root>
|
||||||
className="h-auto w-full aspect-square rounded-lg shadow-md transform"
|
|
||||||
controls
|
|
||||||
muted
|
|
||||||
preload="none"
|
|
||||||
poster="/poster_2.jpeg"
|
|
||||||
>
|
|
||||||
<source
|
|
||||||
src="https://video.nostr.build/d33962520506d86acfb4b55a7b265821e10ae637f5ec830a173b7e6092b16ec8.mp4"
|
|
||||||
type="video/mp4"
|
|
||||||
/>
|
|
||||||
Your browser does not support the video tag.
|
|
||||||
</video>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="relative flex flex-col items-center justify-center rounded-xl bg-black/10 dark:bg-white/10">
|
|
||||||
<div className="absolute top-2 left-3 text-2xl font-semibold font-serif text-neutral-600 dark:text-neutral-400">
|
|
||||||
03.
|
|
||||||
</div>
|
|
||||||
<div className="h-16 flex items-center justify-center shrink-0 px-3 text-lg select-text">
|
|
||||||
Open Lume Store.
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 w-3/4 h-full pb-10">
|
|
||||||
<video
|
|
||||||
className="h-auto w-full aspect-square rounded-lg shadow-md transform"
|
|
||||||
controls
|
|
||||||
muted
|
|
||||||
preload="none"
|
|
||||||
poster="/poster_3.jpeg"
|
|
||||||
>
|
|
||||||
<source
|
|
||||||
src="https://video.nostr.build/927abbfde2097e470ac751181b1db456b7e4b9149550408efff1a966a7ffb9a8.mp4"
|
|
||||||
type="video/mp4"
|
|
||||||
/>
|
|
||||||
Your browser does not support the video tag.
|
|
||||||
</video>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="relative flex flex-col items-center justify-center rounded-xl bg-black/10 dark:bg-white/10">
|
|
||||||
<div className="absolute top-2 left-3 text-2xl font-semibold font-serif text-neutral-600 dark:text-neutral-400">
|
|
||||||
04.
|
|
||||||
</div>
|
|
||||||
<div className="h-16 flex items-center justify-center shrink-0 px-3 text-lg select-text">
|
|
||||||
Use the Tray Menu.
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 w-3/4 h-full pb-10">
|
|
||||||
<video
|
|
||||||
className="h-auto w-full rounded-lg shadow-md transform"
|
|
||||||
controls
|
|
||||||
muted
|
|
||||||
preload="none"
|
|
||||||
poster="/poster_4.jpeg"
|
|
||||||
>
|
|
||||||
<source
|
|
||||||
src="https://video.nostr.build/513de4824b6abaf7e9698c1dad2f68096574356848c0c200bc8cb8074df29410.mp4"
|
|
||||||
type="video/mp4"
|
|
||||||
/>
|
|
||||||
Your browser does not support the video tag.
|
|
||||||
</video>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -19,37 +19,41 @@ function Screen() {
|
|||||||
const { events } = useRouterState({ select: (s) => s.location.state });
|
const { events } = useRouterState({ select: (s) => s.location.state });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col">
|
<div className="px-3 h-full">
|
||||||
<div className="h-10 shrink-0 border-b border-black/5 dark:border-white/5 flex items-center justify-between px-2">
|
<div className="size-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700">
|
||||||
<button
|
<div className="h-full flex flex-col">
|
||||||
type="button"
|
<div className="h-10 shrink-0 border-b border-black/5 dark:border-white/5 flex items-center justify-between px-2">
|
||||||
onClick={() => router.history.back()}
|
<button
|
||||||
className="inline-flex items-center justify-center gap-1.5 h-7 w-max px-1 text-sm font-semibold hover:bg-black/10 dark:hover:bg-white/10 rounded-md"
|
type="button"
|
||||||
>
|
onClick={() => router.history.back()}
|
||||||
<ArrowLeft className="size-4" />
|
className="inline-flex items-center justify-center gap-1.5 h-7 w-max px-1 text-sm font-semibold hover:bg-black/10 dark:hover:bg-white/10 rounded-md"
|
||||||
Back
|
>
|
||||||
</button>
|
<ArrowLeft className="size-4" />
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ScrollArea.Root
|
||||||
|
type={"scroll"}
|
||||||
|
scrollHideDelay={300}
|
||||||
|
className="overflow-hidden size-full flex-1"
|
||||||
|
>
|
||||||
|
<ScrollArea.Viewport ref={ref} className="h-full p-3">
|
||||||
|
<Virtualizer scrollRef={ref}>
|
||||||
|
{events.map((event) => (
|
||||||
|
<ReplyNote key={event.id} event={event} />
|
||||||
|
))}
|
||||||
|
</Virtualizer>
|
||||||
|
</ScrollArea.Viewport>
|
||||||
|
<ScrollArea.Scrollbar
|
||||||
|
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
||||||
|
orientation="vertical"
|
||||||
|
>
|
||||||
|
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
|
||||||
|
</ScrollArea.Scrollbar>
|
||||||
|
<ScrollArea.Corner className="bg-transparent" />
|
||||||
|
</ScrollArea.Root>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ScrollArea.Root
|
|
||||||
type={"scroll"}
|
|
||||||
scrollHideDelay={300}
|
|
||||||
className="overflow-hidden size-full flex-1"
|
|
||||||
>
|
|
||||||
<ScrollArea.Viewport ref={ref} className="h-full p-3">
|
|
||||||
<Virtualizer scrollRef={ref}>
|
|
||||||
{events.map((event) => (
|
|
||||||
<ReplyNote key={event.id} event={event} />
|
|
||||||
))}
|
|
||||||
</Virtualizer>
|
|
||||||
</ScrollArea.Viewport>
|
|
||||||
<ScrollArea.Scrollbar
|
|
||||||
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
|
||||||
orientation="vertical"
|
|
||||||
>
|
|
||||||
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
|
|
||||||
</ScrollArea.Scrollbar>
|
|
||||||
<ScrollArea.Corner className="bg-transparent" />
|
|
||||||
</ScrollArea.Root>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -84,59 +84,61 @@ function Screen() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3 size-full overflow-hidden">
|
<div className="px-3 h-full">
|
||||||
<div className="h-9 shrink-0 px-3 flex items-center gap-2">
|
<div className="flex flex-col gap-3 size-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700">
|
||||||
<input
|
<div className="h-9 shrink-0 pt-6 px-3 flex items-center gap-2">
|
||||||
name="search"
|
<input
|
||||||
placeholder="Search nostr ..."
|
name="search"
|
||||||
value={query}
|
placeholder="Search nostr ..."
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
value={query}
|
||||||
onKeyDown={(event) => {
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
if (event.key === "Enter") search();
|
onKeyDown={(event) => {
|
||||||
}}
|
if (event.key === "Enter") search();
|
||||||
className="h-9 px-5 flex-1 rounded-full border-none bg-black/5 dark:bg-white/10 placeholder:text-neutral-500 dark:placeholder:text-neutral-400 focus:bg-black/10 dark:focus:bg-white/10 focus:outline-none focus:ring-0"
|
}}
|
||||||
/>
|
className="h-9 px-5 flex-1 rounded-full border-none bg-black/5 dark:bg-white/10 placeholder:text-neutral-500 dark:placeholder:text-neutral-400 focus:bg-black/10 dark:focus:bg-white/10 focus:outline-none focus:ring-0"
|
||||||
<button
|
/>
|
||||||
type="button"
|
<button
|
||||||
disabled={!query.length || isPending}
|
type="button"
|
||||||
className="size-9 shrink-0 inline-flex items-center justify-center rounded-full bg-black/5 dark:bg-white/10"
|
disabled={!query.length || isPending}
|
||||||
>
|
className="size-9 shrink-0 inline-flex items-center justify-center rounded-full bg-black/5 dark:bg-white/10"
|
||||||
{isPending ? (
|
>
|
||||||
<Spinner className="size-4" />
|
|
||||||
) : (
|
|
||||||
<MagnifyingGlass className="size-4" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<ScrollArea.Root
|
|
||||||
type={"scroll"}
|
|
||||||
scrollHideDelay={300}
|
|
||||||
className="overflow-hidden size-full flex-1"
|
|
||||||
>
|
|
||||||
<ScrollArea.Viewport ref={ref} className="relative h-full px-3">
|
|
||||||
<Virtualizer scrollRef={ref}>
|
|
||||||
{isPending ? (
|
{isPending ? (
|
||||||
<div className="w-full h-[200px] flex gap-2 items-center justify-center">
|
<Spinner className="size-4" />
|
||||||
<Spinner />
|
|
||||||
Searching...
|
|
||||||
</div>
|
|
||||||
) : !events.length ? (
|
|
||||||
<div className="w-full h-[200px] flex gap-2 items-center justify-center">
|
|
||||||
Type somethings to search.
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
events.map((event) => renderItem(event))
|
<MagnifyingGlass className="size-4" />
|
||||||
)}
|
)}
|
||||||
</Virtualizer>
|
</button>
|
||||||
</ScrollArea.Viewport>
|
</div>
|
||||||
<ScrollArea.Scrollbar
|
<ScrollArea.Root
|
||||||
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
type={"scroll"}
|
||||||
orientation="vertical"
|
scrollHideDelay={300}
|
||||||
|
className="overflow-hidden size-full flex-1"
|
||||||
>
|
>
|
||||||
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
|
<ScrollArea.Viewport ref={ref} className="relative h-full px-3">
|
||||||
</ScrollArea.Scrollbar>
|
<Virtualizer scrollRef={ref}>
|
||||||
<ScrollArea.Corner className="bg-transparent" />
|
{isPending ? (
|
||||||
</ScrollArea.Root>
|
<div className="w-full h-[200px] flex gap-2 items-center justify-center">
|
||||||
|
<Spinner />
|
||||||
|
Searching...
|
||||||
|
</div>
|
||||||
|
) : !events.length ? (
|
||||||
|
<div className="w-full h-[200px] flex gap-2 items-center justify-center">
|
||||||
|
Type somethings to search.
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
events.map((event) => renderItem(event))
|
||||||
|
)}
|
||||||
|
</Virtualizer>
|
||||||
|
</ScrollArea.Viewport>
|
||||||
|
<ScrollArea.Scrollbar
|
||||||
|
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
||||||
|
orientation="vertical"
|
||||||
|
>
|
||||||
|
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
|
||||||
|
</ScrollArea.Scrollbar>
|
||||||
|
<ScrollArea.Corner className="bg-transparent" />
|
||||||
|
</ScrollArea.Root>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ function StoryItem({ contact }: { contact: string }) {
|
|||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-3 flex flex-col w-full h-[300px] bg-white dark:bg-black/20 rounded-xl shadow-primary dark:ring-1 dark:ring-white/5">
|
<div className="mb-3 flex flex-col w-full h-[300px] bg-white dark:bg-black rounded-xl border-[.5px] border-neutral-300 dark:border-neutral-700">
|
||||||
<div className="h-12 shrink-0 px-2 flex items-center justify-between border-b border-neutral-100 dark:border-white/5">
|
<div className="h-12 shrink-0 px-2 flex items-center justify-between border-b border-neutral-100 dark:border-white/5">
|
||||||
<User.Provider pubkey={contact}>
|
<User.Provider pubkey={contact}>
|
||||||
<User.Root className="inline-flex items-center gap-2">
|
<User.Root className="inline-flex items-center gap-2">
|
||||||
|
@ -12,7 +12,7 @@ export const Route = createLazyFileRoute("/columns/_layout/trending")({
|
|||||||
});
|
});
|
||||||
|
|
||||||
function Screen() {
|
function Screen() {
|
||||||
const { isLoading, isError, data } = useQuery({
|
const { isLoading, data } = useQuery({
|
||||||
queryKey: ["trending-notes"],
|
queryKey: ["trending-notes"],
|
||||||
queryFn: async ({ signal }) => {
|
queryFn: async ({ signal }) => {
|
||||||
const res = await fetch("https://api.nostr.band/v0/trending/notes", {
|
const res = await fetch("https://api.nostr.band/v0/trending/notes", {
|
||||||
@ -43,12 +43,30 @@ function Screen() {
|
|||||||
if (!event) return;
|
if (!event) return;
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case Kind.Repost:
|
case Kind.Repost:
|
||||||
return <RepostNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<RepostNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
default: {
|
default: {
|
||||||
if (event.isQuote) {
|
if (event.isQuote) {
|
||||||
return <Quote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<Quote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return <TextNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<TextNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,22 +78,21 @@ function Screen() {
|
|||||||
<ScrollArea.Root
|
<ScrollArea.Root
|
||||||
type={"scroll"}
|
type={"scroll"}
|
||||||
scrollHideDelay={300}
|
scrollHideDelay={300}
|
||||||
className="overflow-hidden size-full"
|
className="overflow-hidden size-full px-3"
|
||||||
>
|
>
|
||||||
<ScrollArea.Viewport ref={ref} className="h-full px-3">
|
<ScrollArea.Viewport
|
||||||
|
ref={ref}
|
||||||
|
className="relative h-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
>
|
||||||
<Virtualizer scrollRef={ref} overscan={1}>
|
<Virtualizer scrollRef={ref} overscan={1}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex flex-col items-center justify-center w-full h-20 gap-1">
|
<div className="flex items-center justify-center w-full h-16 gap-2">
|
||||||
<div className="inline-flex items-center gap-2 text-sm font-medium">
|
<Spinner className="size-4" />
|
||||||
<Spinner className="size-5" />
|
<span className="text-sm font-medium">Loading...</span>
|
||||||
Loading...
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : isError ? (
|
) : !data.length ? (
|
||||||
<div className="flex flex-col items-center justify-center w-full h-20 gap-1">
|
<div className="mb-3 flex items-center justify-center h-20 text-sm">
|
||||||
<div className="inline-flex items-center gap-2 text-sm font-medium">
|
🎉 Yo. You're catching up on all latest notes.
|
||||||
Error.
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
data.map((item) => renderItem(item))
|
data.map((item) => renderItem(item))
|
||||||
|
@ -22,7 +22,7 @@ function Screen() {
|
|||||||
const { isLoading, data: events } = useQuery({
|
const { isLoading, data: events } = useQuery({
|
||||||
queryKey: ["stories", id],
|
queryKey: ["stories", id],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const res = await commands.getEventsBy(id, 20);
|
const res = await commands.getEventsBy(id, 100);
|
||||||
|
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
const data = toLumeEvents(res.data);
|
const data = toLumeEvents(res.data);
|
||||||
@ -41,12 +41,30 @@ function Screen() {
|
|||||||
if (!event) return;
|
if (!event) return;
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case Kind.Repost:
|
case Kind.Repost:
|
||||||
return <RepostNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<RepostNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
default: {
|
default: {
|
||||||
if (event.isQuote) {
|
if (event.isQuote) {
|
||||||
return <Quote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<Quote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return <TextNote key={event.id} event={event} className="mb-3" />;
|
return (
|
||||||
|
<TextNote
|
||||||
|
key={event.id}
|
||||||
|
event={event}
|
||||||
|
className="border-b-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -57,21 +75,24 @@ function Screen() {
|
|||||||
<ScrollArea.Root
|
<ScrollArea.Root
|
||||||
type={"scroll"}
|
type={"scroll"}
|
||||||
scrollHideDelay={300}
|
scrollHideDelay={300}
|
||||||
className="overflow-hidden size-full"
|
className="overflow-hidden size-full px-3"
|
||||||
>
|
>
|
||||||
<ScrollArea.Viewport ref={ref} className="relative h-full px-3 pb-3">
|
<ScrollArea.Viewport
|
||||||
|
ref={ref}
|
||||||
|
className="relative h-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700"
|
||||||
|
>
|
||||||
<Virtualizer scrollRef={ref} overscan={0}>
|
<Virtualizer scrollRef={ref} overscan={0}>
|
||||||
<User.Provider pubkey={id}>
|
<User.Provider pubkey={id}>
|
||||||
<User.Root className="relative">
|
<User.Root className="relative">
|
||||||
<User.Cover className="object-cover w-full h-44 rounded-t-lg gradient-mask-b-0" />
|
<User.Cover className="object-cover w-full h-44 rounded-t-lg gradient-mask-b-0" />
|
||||||
<User.Button className="z-10 absolute top-4 right-4 inline-flex items-center justify-center w-20 text-xs font-medium text-white shadow-md bg-black hover:bg-black/80 rounded-full h-7" />
|
<User.Button className="z-10 absolute top-4 right-4 inline-flex items-center justify-center w-20 text-xs font-medium text-white shadow-md bg-black hover:bg-black/80 rounded-full h-7" />
|
||||||
<div className="z-10 relative flex flex-col items-center gap-1.5 -mt-16">
|
<div className="z-10 relative flex flex-col gap-1.5 -mt-16 px-4">
|
||||||
<User.Avatar className="rounded-full size-14" />
|
<User.Avatar className="rounded-full size-14" />
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex gap-1">
|
||||||
<User.Name className="text-lg font-semibold leading-tight" />
|
<User.Name className="text-lg font-semibold leading-tight" />
|
||||||
<User.NIP05 />
|
<User.NIP05 />
|
||||||
</div>
|
</div>
|
||||||
<User.About className="text-center" />
|
<User.About className="text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</User.Root>
|
</User.Root>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user