mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-29 16:30:55 +00:00
feat: the last commit of year
This commit is contained in:
parent
b1d2496f8e
commit
56fab1dda6
BIN
apps/desktop/public/heading.png
Normal file
BIN
apps/desktop/public/heading.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
apps/desktop/public/heading@2x.png
Normal file
BIN
apps/desktop/public/heading@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
@ -1,24 +1,50 @@
|
||||
import { SettingsIcon } from "@lume/icons";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export function WelcomeScreen() {
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-10">
|
||||
<div className="mx-auto max-w-md flex w-full text-center flex-col items-center">
|
||||
<h1 className="mb-2 text-4xl font-semibold">Welcome to Nostr!</h1>
|
||||
<div className="flex flex-col items-center justify-between w-full h-full">
|
||||
<div />
|
||||
<div className="flex flex-col items-center w-full max-w-4xl gap-10 mx-auto">
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<img
|
||||
src="/heading.png"
|
||||
srcSet="/heading@2x.png 2x"
|
||||
alt="lume"
|
||||
className="w-2/3"
|
||||
/>
|
||||
<p className="mt-5 text-lg font-medium leading-snug text-neutral-600 dark:text-neutral-500">
|
||||
Lume is your safe Nostr client to meet, explore and
|
||||
<br />
|
||||
freely sharing your though to everyone in nostrverse
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col w-full max-w-xs gap-2 mx-auto">
|
||||
<Link
|
||||
to="/auth/create"
|
||||
className="inline-flex items-center justify-center w-full h-12 font-medium text-white bg-blue-500 rounded-xl hover:bg-blue-600"
|
||||
>
|
||||
Create New Account
|
||||
</Link>
|
||||
<Link
|
||||
to="/auth/import"
|
||||
className="inline-flex items-center justify-center w-full h-12 font-medium text-neutral-50 rounded-xl bg-neutral-950 hover:bg-neutral-900"
|
||||
>
|
||||
Login
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 px-8 mx-auto max-w-sm">
|
||||
<Link
|
||||
to="/auth/create"
|
||||
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-blue-500 font-medium text-white hover:bg-blue-600"
|
||||
>
|
||||
Create new account
|
||||
</Link>
|
||||
<Link
|
||||
to="/auth/import"
|
||||
className="inline-flex h-11 w-full items-center justify-center rounded-lg font-medium text-neutral-900 hover:bg-neutral-100 dark:text-neutral-100 dark:hover:bg-neutral-900"
|
||||
>
|
||||
Log in
|
||||
</Link>
|
||||
<div className="flex items-center justify-center h-11">
|
||||
<p className="text-neutral-800">
|
||||
Before joining Nostr, you can take time to learn more about Nostr{" "}
|
||||
<Link
|
||||
to="https://nostr.com"
|
||||
target="_blank"
|
||||
className="text-blue-500"
|
||||
>
|
||||
here
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -16,7 +16,7 @@ export function HomeScreen() {
|
||||
const renderItem = (column: IColumn) => {
|
||||
switch (column.kind) {
|
||||
case COL_TYPES.newsfeed:
|
||||
return <Timeline key={column.id} />;
|
||||
return <Timeline key={column.id} column={column} />;
|
||||
case COL_TYPES.thread:
|
||||
return <Thread key={column.id} column={column} />;
|
||||
case COL_TYPES.user:
|
||||
@ -24,12 +24,12 @@ export function HomeScreen() {
|
||||
case COL_TYPES.hashtag:
|
||||
return <Hashtag key={column.id} column={column} />;
|
||||
default:
|
||||
return <Timeline key={column.id} />;
|
||||
return <Timeline key={column.id} column={column} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<div className="w-full h-full">
|
||||
<VList
|
||||
ref={ref}
|
||||
className="h-full w-full flex-nowrap overflow-x-auto !overflow-y-hidden scrollbar-none focus:outline-none"
|
||||
|
@ -18,6 +18,7 @@
|
||||
"devDependencies": {
|
||||
"@lume/tailwindcss": "workspace:^",
|
||||
"@lume/tsconfig": "workspace:^",
|
||||
"@lume/types": "workspace:^",
|
||||
"@types/react": "^18.2.46",
|
||||
"tailwind": "^4.0.0",
|
||||
"typescript": "^5.3.3"
|
||||
|
@ -3,11 +3,12 @@ import { TimelineIcon } from "@lume/icons";
|
||||
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useRef } from "react";
|
||||
import { IColumn } from "../../../types";
|
||||
import { EventRoute } from "./event";
|
||||
import { HomeRoute } from "./home";
|
||||
import { UserRoute } from "./user";
|
||||
|
||||
export function Timeline() {
|
||||
export function Timeline({ column }: { column: IColumn }) {
|
||||
const colKey = "newsfeed";
|
||||
const storage = useStorage();
|
||||
const queryClient = useQueryClient();
|
||||
@ -26,7 +27,7 @@ export function Timeline() {
|
||||
return (
|
||||
<Column.Root>
|
||||
<Column.Header
|
||||
id="9999"
|
||||
id={column.id}
|
||||
queryKey={[colKey]}
|
||||
title="Timeline"
|
||||
icon={<TimelineIcon className="size-4" />}
|
||||
|
@ -17,35 +17,35 @@ export function ColumnHeader({
|
||||
queryKey,
|
||||
icon,
|
||||
}: {
|
||||
id: string;
|
||||
id: number;
|
||||
title: string;
|
||||
queryKey?: string[];
|
||||
icon?: ReactNode;
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
const { removeColumn } = useColumnContext();
|
||||
const { moveColumn, removeColumn } = useColumnContext();
|
||||
|
||||
const refresh = async () => {
|
||||
if (queryKey) await queryClient.refetchQueries({ queryKey });
|
||||
};
|
||||
|
||||
const moveLeft = async () => {
|
||||
removeColumn(id);
|
||||
moveColumn(id, "left");
|
||||
};
|
||||
|
||||
const moveRight = async () => {
|
||||
removeColumn(id);
|
||||
moveColumn(id, "right");
|
||||
};
|
||||
|
||||
const deleteWidget = async () => {
|
||||
removeColumn(id);
|
||||
await removeColumn(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-11 w-full shrink-0 items-center justify-between border-b border-neutral-100 px-3 dark:border-neutral-900">
|
||||
<div className="flex items-center justify-between w-full px-3 border-b h-11 shrink-0 border-neutral-100 dark:border-neutral-900">
|
||||
<div className="inline-flex items-center gap-4">
|
||||
<div className="h-5 w-1 shrink-0 rounded-full bg-blue-500" />
|
||||
<div className="text-neutral-800 dark:text-neutral-200 inline-flex items-center gap-2 flex-1">
|
||||
<div className="w-1 h-5 bg-blue-500 rounded-full shrink-0" />
|
||||
<div className="inline-flex items-center flex-1 gap-2 text-neutral-800 dark:text-neutral-200">
|
||||
{icon ? icon : <ThreadIcon className="size-4" />}
|
||||
<div className="text-sm font-medium">{title}</div>
|
||||
</div>
|
||||
@ -55,7 +55,7 @@ export function ColumnHeader({
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-6 w-6 items-center justify-center"
|
||||
className="inline-flex items-center justify-center w-6 h-6"
|
||||
>
|
||||
<HorizontalDotsIcon className="size-4" />
|
||||
</button>
|
||||
@ -66,7 +66,7 @@ export function ColumnHeader({
|
||||
<button
|
||||
type="button"
|
||||
onClick={refresh}
|
||||
className="inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-medium text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50"
|
||||
className="inline-flex items-center gap-2 px-3 text-sm font-medium rounded-lg h-9 text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50"
|
||||
>
|
||||
<RefreshIcon className="size-5" />
|
||||
Refresh
|
||||
@ -76,7 +76,7 @@ export function ColumnHeader({
|
||||
<button
|
||||
type="button"
|
||||
onClick={moveLeft}
|
||||
className="inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-medium text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50"
|
||||
className="inline-flex items-center gap-2 px-3 text-sm font-medium rounded-lg h-9 text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50"
|
||||
>
|
||||
<ArrowLeftIcon className="size-5" />
|
||||
Move left
|
||||
@ -86,18 +86,18 @@ export function ColumnHeader({
|
||||
<button
|
||||
type="button"
|
||||
onClick={moveRight}
|
||||
className="inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-medium text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50"
|
||||
className="inline-flex items-center gap-2 px-3 text-sm font-medium rounded-lg h-9 text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50"
|
||||
>
|
||||
<ArrowRightIcon className="size-5" />
|
||||
Move right
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator className="my-1 h-px bg-neutral-100 dark:bg-neutral-900" />
|
||||
<DropdownMenu.Separator className="h-px my-1 bg-neutral-100 dark:bg-neutral-900" />
|
||||
<DropdownMenu.Item asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={deleteWidget}
|
||||
className="inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-medium text-red-500 hover:bg-red-500 hover:text-red-50 focus:outline-none"
|
||||
className="inline-flex items-center gap-2 px-3 text-sm font-medium text-red-500 rounded-lg h-9 hover:bg-red-500 hover:text-red-50 focus:outline-none"
|
||||
>
|
||||
<TrashIcon className="size-5" />
|
||||
Delete
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { IColumn } from "@lume/types";
|
||||
import { COL_TYPES } from "@lume/utils";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
import {
|
||||
ReactNode,
|
||||
createContext,
|
||||
@ -13,9 +12,10 @@ import { useStorage } from "../../provider";
|
||||
|
||||
type ColumnContext = {
|
||||
columns: IColumn[];
|
||||
addColumn: (column: IColumn) => void;
|
||||
removeColumn: (id: string) => void;
|
||||
loadAllColumns: () => void;
|
||||
addColumn: (column: IColumn) => Promise<void>;
|
||||
removeColumn: (id: number) => Promise<void>;
|
||||
moveColumn: (id: number, position: "left" | "right") => void;
|
||||
loadAllColumns: () => Promise<IColumn[]>;
|
||||
};
|
||||
|
||||
const ColumnContext = createContext<ColumnContext>(null);
|
||||
@ -24,7 +24,7 @@ export function ColumnProvider({ children }: { children: ReactNode }) {
|
||||
const storage = useStorage();
|
||||
const [columns, setColumns] = useState<IColumn[]>([
|
||||
{
|
||||
id: "9999",
|
||||
id: 9999,
|
||||
title: "Newsfeed",
|
||||
content: "",
|
||||
kind: COL_TYPES.newsfeed,
|
||||
@ -44,11 +44,28 @@ export function ColumnProvider({ children }: { children: ReactNode }) {
|
||||
if (result) setColumns((prev) => [...prev, column]);
|
||||
}, []);
|
||||
|
||||
const removeColumn = useCallback(async (id: string) => {
|
||||
const removeColumn = useCallback(async (id: number) => {
|
||||
await storage.removeWidget(id);
|
||||
setColumns((prev) => prev.filter((t) => t.id !== id));
|
||||
}, []);
|
||||
|
||||
const moveColumn = useCallback(
|
||||
(id: number, position: "left" | "right") => {
|
||||
const newCols = [...columns];
|
||||
|
||||
const col = newCols.find((el) => el.id === id);
|
||||
const colIndex = newCols.findIndex((el) => el.id === id);
|
||||
|
||||
newCols.splice(colIndex, 1);
|
||||
|
||||
if (position === "left") newCols.splice(colIndex - 1, 0, col);
|
||||
if (position === "right") newCols.splice(colIndex + 1, 0, col);
|
||||
|
||||
setColumns(newCols);
|
||||
},
|
||||
[columns],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
@ -63,7 +80,7 @@ export function ColumnProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
return (
|
||||
<ColumnContext.Provider
|
||||
value={{ columns, addColumn, removeColumn, loadAllColumns }}
|
||||
value={{ columns, addColumn, removeColumn, moveColumn, loadAllColumns }}
|
||||
>
|
||||
{children}
|
||||
</ColumnContext.Provider>
|
||||
|
@ -14,23 +14,23 @@ export function NotePin() {
|
||||
<Tooltip.Trigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
addColumn({
|
||||
onClick={async () =>
|
||||
await addColumn({
|
||||
kind: COL_TYPES.thread,
|
||||
title: "Thread",
|
||||
content: event.id,
|
||||
})
|
||||
}
|
||||
className="inline-flex h-7 w-max items-center justify-center gap-2 rounded-full bg-neutral-100 hover:bg-neutral-200 dark:hover:bg-neutral-800 px-2 text-sm font-medium dark:bg-neutral-900"
|
||||
className="inline-flex items-center justify-center gap-2 pl-2 pr-3 text-sm font-medium rounded-full h-7 w-max bg-neutral-100 hover:bg-neutral-200 dark:hover:bg-neutral-800 dark:bg-neutral-900"
|
||||
>
|
||||
<PinIcon className="size-4" />
|
||||
Pin
|
||||
</button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="bg-neutral-950 text-white -left-10 inline-flex h-7 select-none items-center justify-center rounded-md px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
Pin note
|
||||
<Tooltip.Arrow className="fill-neutral-950" />
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { ReactionIcon } from "@lume/icons";
|
||||
import * as HoverCard from "@radix-ui/react-hover-card";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { useNoteContext } from "../provider";
|
||||
|
||||
const REACTIONS = [
|
||||
@ -49,7 +48,7 @@ export function NoteReaction() {
|
||||
|
||||
setOpen(false);
|
||||
} catch (e) {
|
||||
toast.error(e);
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
@ -58,22 +57,22 @@ export function NoteReaction() {
|
||||
<HoverCard.Trigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="group inline-flex h-7 w-7 items-center justify-center text-neutral-600 dark:text-neutral-400"
|
||||
className="inline-flex items-center justify-center group h-7 w-7 text-neutral-600 dark:text-neutral-400"
|
||||
>
|
||||
{reaction ? (
|
||||
<img
|
||||
src={getReactionImage(reaction)}
|
||||
alt={reaction}
|
||||
className="size-5"
|
||||
className="size-6"
|
||||
/>
|
||||
) : (
|
||||
<ReactionIcon className="size-5 group-hover:text-blue-500" />
|
||||
<ReactionIcon className="size-6 group-hover:text-blue-500" />
|
||||
)}
|
||||
</button>
|
||||
</HoverCard.Trigger>
|
||||
<HoverCard.Portal>
|
||||
<HoverCard.Content
|
||||
className="select-none rounded-lg bg-neutral-950 px-1 py-1 text-sm will-change-[transform,opacity] data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=top]:animate-slideDownAndFade"
|
||||
className="select-none rounded-lg bg-neutral-950 dark:bg-neutral-50 px-1 py-1 text-sm will-change-[transform,opacity] data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=top]:animate-slideDownAndFade"
|
||||
sideOffset={0}
|
||||
side="top"
|
||||
>
|
||||
@ -81,56 +80,56 @@ export function NoteReaction() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => react("👏")}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10"
|
||||
className="inline-flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-xl hover:bg-white/10 dark:hover:bg-black/10"
|
||||
>
|
||||
<img
|
||||
src="/clapping_hands.png"
|
||||
alt="Clapping Hands"
|
||||
className="h-6 w-6"
|
||||
className="size-6"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => react("🤪")}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10"
|
||||
className="inline-flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-xl hover:bg-white/10 dark:hover:bg-black/10"
|
||||
>
|
||||
<img
|
||||
src="/face_with_tongue.png"
|
||||
alt="Face with Tongue"
|
||||
className="h-6 w-6"
|
||||
className="size-6"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => react("😮")}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10"
|
||||
className="inline-flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-xl hover:bg-white/10 dark:hover:bg-black/10"
|
||||
>
|
||||
<img
|
||||
src="/face_with_open_mouth.png"
|
||||
alt="Face with Open Mouth"
|
||||
className="h-6 w-6"
|
||||
className="size-6"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => react("😢")}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10"
|
||||
className="inline-flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-xl hover:bg-white/10 dark:hover:bg-black/10"
|
||||
>
|
||||
<img
|
||||
src="/crying_face.png"
|
||||
alt="Crying Face"
|
||||
className="h-6 w-6"
|
||||
className="size-6"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => react("🤡")}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10"
|
||||
className="inline-flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-xl hover:bg-white/10 dark:hover:bg-black/10"
|
||||
>
|
||||
<img src="/clown_face.png" alt="Clown Face" className="h-6 w-6" />
|
||||
<img src="/clown_face.png" alt="Clown Face" className="size-6" />
|
||||
</button>
|
||||
</div>
|
||||
<HoverCard.Arrow className="fill-neutral-950" />
|
||||
<HoverCard.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</HoverCard.Content>
|
||||
</HoverCard.Portal>
|
||||
</HoverCard.Root>
|
||||
|
@ -26,15 +26,15 @@ export function NoteReply({
|
||||
}).toString(),
|
||||
})
|
||||
}
|
||||
className="group inline-flex h-7 w-7 items-center justify-center text-neutral-600 dark:text-neutral-400"
|
||||
className="inline-flex items-center justify-center group h-7 w-7 text-neutral-600 dark:text-neutral-400"
|
||||
>
|
||||
<ReplyIcon className="h-5 w-5 group-hover:text-blue-500" />
|
||||
<ReplyIcon className="size-6 group-hover:text-blue-500" />
|
||||
</button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-white will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
Quick reply
|
||||
<Tooltip.Arrow className="fill-neutral-950" />
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
|
@ -29,20 +29,20 @@ export function NoteRepost() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={submit}
|
||||
className="group inline-flex h-7 w-7 items-center justify-center text-neutral-600 dark:text-neutral-400"
|
||||
className="inline-flex items-center justify-center group h-7 w-7 text-neutral-600 dark:text-neutral-400"
|
||||
>
|
||||
<RepostIcon
|
||||
className={twMerge(
|
||||
"h-5 w-5 group-hover:text-blue-600",
|
||||
"size-6 group-hover:text-blue-600",
|
||||
isRepost ? "text-blue-500" : "",
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-white will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||
Repost
|
||||
<Tooltip.Arrow className="fill-neutral-950" />
|
||||
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
|
@ -107,32 +107,32 @@ export function NoteZap() {
|
||||
<Dialog.Trigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="group inline-flex h-7 w-7 items-center justify-center text-neutral-600 dark:text-neutral-400"
|
||||
className="inline-flex items-center justify-center group h-7 w-7 text-neutral-600 dark:text-neutral-400"
|
||||
>
|
||||
<ZapIcon className="h-5 w-5 group-hover:text-blue-500" />
|
||||
<ZapIcon className="size-6 group-hover:text-blue-500" />
|
||||
</button>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-sm dark:bg-black/20" />
|
||||
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
||||
<div className="relative h-min w-full max-w-xl rounded-xl bg-white dark:bg-black">
|
||||
<div className="inline-flex w-full shrink-0 items-center justify-between px-5 py-3">
|
||||
<Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
|
||||
<div className="relative w-full max-w-xl bg-white h-min rounded-xl dark:bg-black">
|
||||
<div className="inline-flex items-center justify-between w-full px-5 py-3 shrink-0">
|
||||
<div className="w-6" />
|
||||
<Dialog.Title className="text-center font-semibold">
|
||||
<Dialog.Title className="font-semibold text-center">
|
||||
Send tip to{" "}
|
||||
{user?.name ||
|
||||
user?.displayName ||
|
||||
displayNpub(event.pubkey, 16)}
|
||||
</Dialog.Title>
|
||||
<Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md bg-neutral-100 dark:bg-neutral-900">
|
||||
<CancelIcon className="h-4 w-4" />
|
||||
<Dialog.Close className="inline-flex items-center justify-center w-6 h-6 rounded-md bg-neutral-100 dark:bg-neutral-900">
|
||||
<CancelIcon className="w-4 h-4" />
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
<div className="overflow-y-auto overflow-x-hidden px-5 pb-5">
|
||||
<div className="px-5 pb-5 overflow-x-hidden overflow-y-auto">
|
||||
{!invoice ? (
|
||||
<>
|
||||
<div className="relative flex h-40 flex-col">
|
||||
<div className="inline-flex h-full flex-1 items-center justify-center gap-1">
|
||||
<div className="relative flex flex-col h-40">
|
||||
<div className="inline-flex items-center justify-center flex-1 h-full gap-1">
|
||||
<CurrencyInput
|
||||
placeholder="0"
|
||||
defaultValue={"21"}
|
||||
@ -142,9 +142,9 @@ export function NoteZap() {
|
||||
max={10000} // 1M sats
|
||||
maxLength={10000} // 1M sats
|
||||
onValueChange={(value) => setAmount(value)}
|
||||
className="w-full flex-1 border-none bg-transparent text-right text-4xl font-semibold placeholder:text-neutral-600 focus:outline-none focus:ring-0 dark:text-neutral-400"
|
||||
className="flex-1 w-full text-4xl font-semibold text-right bg-transparent border-none placeholder:text-neutral-600 focus:outline-none focus:ring-0 dark:text-neutral-400"
|
||||
/>
|
||||
<span className="w-full flex-1 text-left text-4xl font-semibold text-neutral-600 dark:text-neutral-400">
|
||||
<span className="flex-1 w-full text-4xl font-semibold text-left text-neutral-600 dark:text-neutral-400">
|
||||
sats
|
||||
</span>
|
||||
</div>
|
||||
@ -186,7 +186,7 @@ export function NoteZap() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex w-full flex-col gap-2">
|
||||
<div className="flex flex-col w-full gap-2 mt-4">
|
||||
<input
|
||||
name="zapMessage"
|
||||
value={zapMessage}
|
||||
@ -203,7 +203,7 @@ export function NoteZap() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => createZapRequest()}
|
||||
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-blue-500 px-4 font-medium text-white hover:bg-blue-600"
|
||||
className="inline-flex items-center justify-center w-full px-4 font-medium text-white bg-blue-500 rounded-lg h-11 hover:bg-blue-600"
|
||||
>
|
||||
{isCompleted ? (
|
||||
<p className="leading-tight">Successfully zapped</p>
|
||||
@ -229,7 +229,7 @@ export function NoteZap() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => createZapRequest()}
|
||||
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-blue-500 px-4 font-medium text-white hover:bg-blue-600"
|
||||
className="inline-flex items-center justify-center w-full px-4 font-medium text-white bg-blue-500 rounded-lg h-11 hover:bg-blue-600"
|
||||
>
|
||||
Create Lightning invoice
|
||||
</button>
|
||||
@ -238,13 +238,13 @@ export function NoteZap() {
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="mt-3 flex flex-col items-center justify-center gap-4">
|
||||
<div className="rounded-md bg-neutral-100 p-3 dark:bg-neutral-900">
|
||||
<div className="flex flex-col items-center justify-center gap-4 mt-3">
|
||||
<div className="p-3 rounded-md bg-neutral-100 dark:bg-neutral-900">
|
||||
<QRCodeSVG value={invoice} size={256} />
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<h3 className="text-lg font-medium">Scan to zap</h3>
|
||||
<span className="text-center text-sm text-neutral-600 dark:text-neutral-400">
|
||||
<span className="text-sm text-center text-neutral-600 dark:text-neutral-400">
|
||||
You must use Bitcoin wallet which support Lightning
|
||||
<br />
|
||||
such as: Blue Wallet, Bitkit, Phoenix,...
|
||||
|
@ -7,14 +7,14 @@ export function Hashtag({ tag }: { tag: string }) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
addColumn({
|
||||
onClick={async () =>
|
||||
await addColumn({
|
||||
kind: COL_TYPES.hashtag,
|
||||
title: tag,
|
||||
content: tag,
|
||||
})
|
||||
}
|
||||
className="cursor-default break-all text-blue-500 hover:text-blue-600"
|
||||
className="text-blue-500 break-all cursor-default hover:text-blue-600"
|
||||
>
|
||||
{tag}
|
||||
</button>
|
||||
|
@ -24,7 +24,7 @@ export const MentionNote = memo(function MentionNote({
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="w-full cursor-default rounded-lg bg-neutral-100 p-3 dark:bg-neutral-900">
|
||||
<div className="w-full p-3 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900">
|
||||
Loading
|
||||
</div>
|
||||
);
|
||||
@ -32,7 +32,7 @@ export const MentionNote = memo(function MentionNote({
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="w-full cursor-default rounded-lg bg-neutral-100 p-3 dark:bg-neutral-900">
|
||||
<div className="w-full p-3 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900">
|
||||
Failed to fetch event
|
||||
</div>
|
||||
);
|
||||
@ -40,11 +40,11 @@ export const MentionNote = memo(function MentionNote({
|
||||
|
||||
return (
|
||||
<Note.Provider event={data}>
|
||||
<Note.Root className="flex w-full cursor-default flex-col gap-1 rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
||||
<div className="mt-3 px-3">
|
||||
<Note.Root className="flex flex-col w-full gap-1 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900">
|
||||
<div className="px-3 mt-3">
|
||||
<Note.User variant="mention" />
|
||||
</div>
|
||||
<div className="mt-1 px-3 pb-3">
|
||||
<div className="px-3 pb-3 mt-1">
|
||||
{renderKind(data)}
|
||||
<Link
|
||||
to={`/events/${data.id}`}
|
||||
|
@ -13,14 +13,14 @@ export const MentionUser = memo(function MentionUser({
|
||||
|
||||
return (
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger className="break-words text-blue-500 hover:text-blue-600">
|
||||
<DropdownMenu.Trigger className="text-blue-500 break-words hover:text-blue-600">
|
||||
{`@${user?.name || user?.displayName || user?.username || "anon"}`}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content className="left-[50px] z-50 relative flex w-[200px] flex-col overflow-hidden rounded-xl border border-neutral-200 bg-neutral-950 focus:outline-none dark:border-neutral-900">
|
||||
<DropdownMenu.Item asChild>
|
||||
<Link
|
||||
to={`/users/${pubkey}`}
|
||||
className="inline-flex h-10 items-center px-4 text-sm text-white hover:bg-neutral-900 focus:outline-none"
|
||||
className="inline-flex items-center h-10 px-4 text-sm text-white hover:bg-neutral-900 focus:outline-none"
|
||||
>
|
||||
View profile
|
||||
</Link>
|
||||
@ -28,14 +28,14 @@ export const MentionUser = memo(function MentionUser({
|
||||
<DropdownMenu.Item asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
addColumn({
|
||||
onClick={async () =>
|
||||
await addColumn({
|
||||
kind: COL_TYPES.user,
|
||||
title: user?.name || user?.displayName || "",
|
||||
content: pubkey,
|
||||
})
|
||||
}
|
||||
className="inline-flex h-10 items-center px-4 text-sm text-white hover:bg-neutral-900 focus:outline-none"
|
||||
className="inline-flex items-center h-10 px-4 text-sm text-white hover:bg-neutral-900 focus:outline-none"
|
||||
>
|
||||
Pin
|
||||
</button>
|
||||
|
@ -39,7 +39,7 @@ export function ImagePreview({ url }: { url: string }) {
|
||||
|
||||
return (
|
||||
// biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
|
||||
<div onClick={open} className="group relative">
|
||||
<div onClick={open} className="relative my-1 group">
|
||||
<img
|
||||
src={url}
|
||||
alt={url}
|
||||
@ -47,17 +47,17 @@ export function ImagePreview({ url }: { url: string }) {
|
||||
decoding="async"
|
||||
style={{ contentVisibility: "auto" }}
|
||||
onError={fallback}
|
||||
className="h-auto w-full rounded-lg border border-neutral-200/50 object-cover dark:border-neutral-800/50"
|
||||
className="object-cover w-full h-auto border rounded-lg border-neutral-200/50 dark:border-neutral-800/50"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => downloadImage(e)}
|
||||
className="absolute right-2 top-2 z-10 hidden h-10 w-10 items-center justify-center rounded-lg bg-blue-500 group-hover:inline-flex hover:bg-blue-600"
|
||||
className="absolute z-10 items-center justify-center hidden w-10 h-10 bg-blue-500 rounded-lg right-2 top-2 group-hover:inline-flex hover:bg-blue-600"
|
||||
>
|
||||
{downloaded ? (
|
||||
<CheckCircleIcon className="h-5 w-5 text-white" />
|
||||
<CheckCircleIcon className="w-5 h-5 text-white" />
|
||||
) : (
|
||||
<DownloadIcon className="h-5 w-5 text-white" />
|
||||
<DownloadIcon className="w-5 h-5 text-white" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -11,11 +11,11 @@ export function LinkPreview({ url }: { url: string }) {
|
||||
|
||||
if (status === "pending") {
|
||||
return (
|
||||
<div className="flex w-full flex-col rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
||||
<div className="h-48 w-full animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
||||
<div className="flex flex-col w-full my-1 rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
||||
<div className="w-full h-48 animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
||||
<div className="flex flex-col gap-2 px-3 py-3">
|
||||
<div className="h-3 w-2/3 animate-pulse rounded bg-neutral-300 dark:bg-neutral-700" />
|
||||
<div className="h-3 w-3/4 animate-pulse rounded bg-neutral-300 dark:bg-neutral-700" />
|
||||
<div className="w-2/3 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
||||
<div className="w-3/4 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
||||
<span className="mt-2.5 text-sm leading-none text-neutral-600 dark:text-neutral-400">
|
||||
{domain.hostname}
|
||||
</span>
|
||||
@ -42,29 +42,29 @@ export function LinkPreview({ url }: { url: string }) {
|
||||
to={url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="flex w-full flex-col rounded-lg bg-neutral-100 dark:bg-neutral-900"
|
||||
className="flex flex-col w-full my-1 overflow-hidden rounded-lg bg-neutral-100 dark:bg-neutral-900"
|
||||
>
|
||||
{isImage(data.image) ? (
|
||||
<img
|
||||
src={data.image}
|
||||
alt={url}
|
||||
className="h-48 w-full rounded-t-lg bg-white object-cover"
|
||||
className="object-cover w-full h-48 bg-white rounded-t-lg"
|
||||
/>
|
||||
) : null}
|
||||
<div className="flex flex-col items-start px-3 py-3">
|
||||
<div className="flex flex-col items-start gap-1 text-left">
|
||||
{data.title ? (
|
||||
<div className="break-all text-base font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
<div className="text-base font-semibold break-all text-neutral-900 dark:text-neutral-100">
|
||||
{data.title}
|
||||
</div>
|
||||
) : null}
|
||||
{data.description ? (
|
||||
<div className="mb-2 line-clamp-3 break-all text-sm text-neutral-700 dark:text-neutral-400">
|
||||
<div className="mb-2 text-sm break-all line-clamp-3 text-neutral-700 dark:text-neutral-400">
|
||||
{data.description}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="break-all text-sm text-neutral-600 dark:text-neutral-400">
|
||||
<div className="text-sm break-all text-neutral-600 dark:text-neutral-400">
|
||||
{domain.hostname}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@ export function VideoPreview({ url }: { url: string }) {
|
||||
return (
|
||||
<MediaPlayer
|
||||
src={url}
|
||||
className="w-full overflow-hidden rounded-lg"
|
||||
className="w-full my-1 overflow-hidden rounded-lg"
|
||||
aspectRatio="16/9"
|
||||
load="visible"
|
||||
>
|
||||
|
@ -94,7 +94,7 @@ const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
||||
const sqliteAdapter = await Database.load("sqlite:lume_v2.db");
|
||||
|
||||
const storage = new LumeStorage(sqliteAdapter, platformName);
|
||||
storage.init();
|
||||
await storage.init();
|
||||
|
||||
// check for new update
|
||||
if (storage.settings.autoupdate) {
|
||||
@ -193,9 +193,9 @@ const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
||||
return (
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className="relative flex h-screen w-screen items-center justify-center bg-neutral-50 dark:bg-neutral-950"
|
||||
className="relative flex items-center justify-center w-screen h-screen bg-neutral-50 dark:bg-neutral-950"
|
||||
>
|
||||
<div className="flex max-w-2xl flex-col items-start gap-1">
|
||||
<div className="flex flex-col items-start max-w-2xl gap-1">
|
||||
<h5 className="font-semibold uppercase">TIP:</h5>
|
||||
<Markdown
|
||||
options={{
|
||||
@ -214,7 +214,7 @@ const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
||||
</Markdown>
|
||||
</div>
|
||||
<div className="absolute bottom-5 right-5 inline-flex items-center gap-2.5">
|
||||
<LoaderIcon className="h-6 w-6 animate-spin text-blue-500" />
|
||||
<LoaderIcon className="w-6 h-6 text-blue-500 animate-spin" />
|
||||
<p className="font-semibold">
|
||||
{isNewVersion ? "Found a new version, updating..." : "Starting..."}
|
||||
</p>
|
||||
|
@ -1,27 +1,31 @@
|
||||
import { SVGProps } from 'react';
|
||||
import { SVGProps } from "react";
|
||||
|
||||
export function SettingsIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
d="M8.552 5.37l-1.793-.414a1 1 0 00-.932.267l-.604.604a1 1 0 00-.267.932l.414 1.793a1 1 0 01-.42 1.056l-1.755 1.17a1 1 0 00-.445.832v.78a1 1 0 00.445.832l1.755 1.17a1 1 0 01.42 1.056l-.414 1.793a1 1 0 00.267.932l.604.604a1 1 0 00.932.267l1.793-.414a1 1 0 011.056.42l1.17 1.755a1 1 0 00.832.445h.78a1 1 0 00.832-.445l1.17-1.755a1 1 0 011.056-.42l1.793.414a1 1 0 00.932-.267l.604-.604a1 1 0 00.267-.932l-.414-1.793a1 1 0 01.42-1.056l1.755-1.17a1 1 0 00.445-.832v-.78a1 1 0 00-.445-.832l-1.755-1.17a1 1 0 01-.42-1.056l.414-1.793a1 1 0 00-.267-.932l-.604-.604a1 1 0 00-.932-.267l-1.793.414a1 1 0 01-1.056-.42l-1.17-1.755a1 1 0 00-.832-.445h-.78a1 1 0 00-.832.445L9.608 4.95a1 1 0 01-1.056.42z"
|
||||
/>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
d="M14.75 12a2.75 2.75 0 11-5.5 0 2.75 2.75 0 015.5 0z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export function SettingsIcon(
|
||||
props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
|
||||
) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M11 12a1 1 0 112 0 1 1 0 01-2 0z"
|
||||
/>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M8.562 4.32c1.252-.723 1.879-1.085 2.544-1.226a4.3 4.3 0 011.788 0c.665.141 1.292.503 2.544 1.225l1.499.865c1.252.723 1.878 1.084 2.334 1.59.403.447.707.974.894 1.546.21.647.21 1.37.21 2.816v1.728c0 1.446 0 2.169-.21 2.816-.186.572-.49 1.1-.894 1.547-.456.505-1.082.866-2.334 1.59l-1.499.864c-1.252.722-1.879 1.084-2.544 1.225a4.299 4.299 0 01-1.788 0c-.665-.141-1.292-.503-2.544-1.225l-1.499-.865c-1.252-.723-1.879-1.084-2.334-1.59a4.296 4.296 0 01-.894-1.546c-.21-.647-.21-1.37-.21-2.816v-1.728c0-1.446 0-2.169.21-2.816.186-.572.49-1.1.894-1.546.455-.506 1.082-.867 2.334-1.59l1.499-.865z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import {
|
||||
Account,
|
||||
IColumn,
|
||||
NDKCacheEvent,
|
||||
NDKCacheEventTag,
|
||||
NDKCacheUser,
|
||||
NDKCacheUserProfile,
|
||||
WidgetProps,
|
||||
} from "@lume/types";
|
||||
import { appConfigDir, resolveResource } from "@tauri-apps/api/path";
|
||||
import { invoke } from "@tauri-apps/api/primitives";
|
||||
@ -323,7 +323,7 @@ export class LumeStorage {
|
||||
}
|
||||
|
||||
public async getWidgets() {
|
||||
const widgets: Array<WidgetProps> = await this.#db.select(
|
||||
const widgets: Array<IColumn> = await this.#db.select(
|
||||
"SELECT * FROM widgets WHERE account_id = $1 ORDER BY created_at DESC;",
|
||||
[this.account.id],
|
||||
);
|
||||
@ -341,7 +341,7 @@ export class LumeStorage {
|
||||
);
|
||||
|
||||
if (insert) {
|
||||
const widgets: Array<WidgetProps> = await this.#db.select(
|
||||
const widgets: Array<IColumn> = await this.#db.select(
|
||||
"SELECT * FROM widgets ORDER BY id DESC LIMIT 1;",
|
||||
);
|
||||
if (widgets.length < 1) console.error("get created widget failed");
|
||||
@ -351,7 +351,7 @@ export class LumeStorage {
|
||||
console.error("create widget failed");
|
||||
}
|
||||
|
||||
public async removeWidget(id: string) {
|
||||
public async removeWidget(id: number) {
|
||||
const res = await this.#db.execute("DELETE FROM widgets WHERE id = $1;", [
|
||||
id,
|
||||
]);
|
||||
|
2
packages/types/index.d.ts
vendored
2
packages/types/index.d.ts
vendored
@ -34,7 +34,7 @@ export interface WidgetGroupItem {
|
||||
}
|
||||
|
||||
export interface IColumn {
|
||||
id?: string;
|
||||
id?: number;
|
||||
kind: number;
|
||||
title: string;
|
||||
content: string;
|
||||
|
@ -1,24 +1,23 @@
|
||||
import { cn } from "@lume/utils";
|
||||
import { SettingsIcon } from "@lume/icons";
|
||||
import { type Platform } from "@tauri-apps/plugin-os";
|
||||
import { Outlet, ScrollRestoration } from "react-router-dom";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { WindowTitleBar } from "../titlebar";
|
||||
|
||||
export function AuthLayout({ platform }: { platform: Platform }) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-screen w-screen flex-col",
|
||||
platform !== "macos" ? "bg-neutral-50 dark:bg-neutral-950" : "",
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col w-screen h-screen bg-black">
|
||||
{platform !== "macos" ? (
|
||||
<WindowTitleBar platform={platform} />
|
||||
) : (
|
||||
<div data-tauri-drag-region className="h-9 shrink-0" />
|
||||
)}
|
||||
<div className="h-full w-full">
|
||||
<div className="relative w-full h-full">
|
||||
<div className="absolute top-0 right-9">
|
||||
<div className="inline-flex items-center justify-center rounded-lg size-10 bg-neutral-950 group hover:bg-neutral-900">
|
||||
<SettingsIcon className="size-6 text-neutral-700 group-hover:text-neutral-500" />
|
||||
</div>
|
||||
</div>
|
||||
<Outlet />
|
||||
<ScrollRestoration />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -472,6 +472,9 @@ importers:
|
||||
'@lume/tsconfig':
|
||||
specifier: workspace:^
|
||||
version: link:../../tsconfig
|
||||
'@lume/types':
|
||||
specifier: workspace:^
|
||||
version: link:../../types
|
||||
'@types/react':
|
||||
specifier: ^18.2.46
|
||||
version: 18.2.46
|
||||
|
@ -1,21 +1,21 @@
|
||||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||
"tauri": {
|
||||
"windows": [
|
||||
{
|
||||
"width": 1080,
|
||||
"height": 800,
|
||||
"minWidth": 560,
|
||||
"minHeight": 800,
|
||||
"resizable": true,
|
||||
"title": "Lume",
|
||||
"center": true,
|
||||
"fullscreen": false,
|
||||
"hiddenTitle": true,
|
||||
"fileDropEnabled": true,
|
||||
"decorations": false,
|
||||
"transparent": false
|
||||
}
|
||||
]
|
||||
}
|
||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||
"tauri": {
|
||||
"windows": [
|
||||
{
|
||||
"width": 1080,
|
||||
"height": 800,
|
||||
"minWidth": 1080,
|
||||
"minHeight": 800,
|
||||
"resizable": true,
|
||||
"title": "Lume",
|
||||
"center": true,
|
||||
"fullscreen": false,
|
||||
"hiddenTitle": true,
|
||||
"fileDropEnabled": true,
|
||||
"decorations": false,
|
||||
"transparent": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,25 @@
|
||||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||
"tauri": {
|
||||
"windows": [
|
||||
{
|
||||
"width": 1080,
|
||||
"height": 800,
|
||||
"minWidth": 560,
|
||||
"minHeight": 800,
|
||||
"resizable": true,
|
||||
"title": "Lume",
|
||||
"titleBarStyle": "Overlay",
|
||||
"center": true,
|
||||
"fullscreen": false,
|
||||
"hiddenTitle": true,
|
||||
"fileDropEnabled": true,
|
||||
"decorations": true,
|
||||
"transparent": true,
|
||||
"windowEffects": {
|
||||
"effects": ["sidebar"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||
"tauri": {
|
||||
"windows": [
|
||||
{
|
||||
"width": 1080,
|
||||
"height": 800,
|
||||
"minWidth": 1080,
|
||||
"minHeight": 800,
|
||||
"resizable": true,
|
||||
"title": "Lume",
|
||||
"titleBarStyle": "Overlay",
|
||||
"center": true,
|
||||
"fullscreen": false,
|
||||
"hiddenTitle": true,
|
||||
"fileDropEnabled": true,
|
||||
"decorations": true,
|
||||
"transparent": true,
|
||||
"windowEffects": {
|
||||
"effects": ["sidebar"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||
"tauri": {
|
||||
"windows": [
|
||||
{
|
||||
"width": 1080,
|
||||
"height": 800,
|
||||
"minWidth": 560,
|
||||
"minHeight": 800,
|
||||
"resizable": true,
|
||||
"title": "Lume",
|
||||
"center": true,
|
||||
"fullscreen": false,
|
||||
"hiddenTitle": true,
|
||||
"fileDropEnabled": true,
|
||||
"decorations": false,
|
||||
"transparent": true,
|
||||
"windowEffects": {
|
||||
"effects": ["micaLight", "micaDark"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||
"tauri": {
|
||||
"windows": [
|
||||
{
|
||||
"width": 1080,
|
||||
"height": 800,
|
||||
"minWidth": 1080,
|
||||
"minHeight": 800,
|
||||
"resizable": true,
|
||||
"title": "Lume",
|
||||
"center": true,
|
||||
"fullscreen": false,
|
||||
"hiddenTitle": true,
|
||||
"fileDropEnabled": true,
|
||||
"decorations": false,
|
||||
"transparent": true,
|
||||
"windowEffects": {
|
||||
"effects": ["micaLight", "micaDark"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user