mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-29 16:30:55 +00:00
feat: polish
This commit is contained in:
parent
bef1f136ad
commit
b0a443c002
@ -48,7 +48,7 @@ export function BackupSettingScreen() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => removePrivkey()}
|
onClick={() => removePrivkey()}
|
||||||
className="mt-2 inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg bg-red-200 px-6 font-medium text-red-500 hover:bg-red-500 hover:text-white focus:outline-none dark:hover:text-white"
|
className="mt-2 inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg bg-red-200 dark:bg-red-800 px-6 font-medium text-red-500 hover:bg-red-500 hover:text-white focus:outline-none dark:hover:text-white"
|
||||||
>
|
>
|
||||||
Remove private key
|
Remove private key
|
||||||
</button>
|
</button>
|
||||||
|
@ -10,6 +10,7 @@ import { NDKKind, NDKUserProfile } from "@nostr-dev-kit/ndk";
|
|||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { AvatarUpload } from "./components/avatarUpload";
|
import { AvatarUpload } from "./components/avatarUpload";
|
||||||
import { CoverUpload } from "./components/coverUpload";
|
import { CoverUpload } from "./components/coverUpload";
|
||||||
|
|
||||||
@ -92,6 +93,9 @@ export function ProfileSettingScreen() {
|
|||||||
return content;
|
return content;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// notify
|
||||||
|
toast.success("You've updated profile successfully.");
|
||||||
|
|
||||||
// reset state
|
// reset state
|
||||||
setPicture(null);
|
setPicture(null);
|
||||||
setBanner(null);
|
setBanner(null);
|
||||||
@ -112,7 +116,7 @@ export function ProfileSettingScreen() {
|
|||||||
className="h-full w-full rounded-t-xl object-cover"
|
className="h-full w-full rounded-t-xl object-cover"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="h-full w-full rounded-t-xl bg-neutral-200 dark:bg-neutral-900" />
|
<div className="h-full w-full rounded-t-xl bg-neutral-200 dark:bg-neutral-800" />
|
||||||
)}
|
)}
|
||||||
<div className="absolute right-4 top-4">
|
<div className="absolute right-4 top-4">
|
||||||
<CoverUpload setBanner={setBanner} />
|
<CoverUpload setBanner={setBanner} />
|
||||||
@ -236,7 +240,7 @@ export function ProfileSettingScreen() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={!isValid || loading}
|
disabled={!isValid || loading}
|
||||||
className="mx-auto inline-flex h-10 w-24 transform items-center justify-center gap-1 rounded-lg bg-blue-500 font-medium text-white hover:bg-blue-600 focus:outline-none active:translate-y-1 disabled:pointer-events-none disabled:opacity-50"
|
className="inline-flex items-center justify-center w-24 pb-[2px] font-semibold border-t rounded-lg border-neutral-900 dark:border-neutral-800 h-9 bg-neutral-950 text-neutral-50 dark:bg-neutral-900 hover:bg-neutral-900 dark:hover:bg-neutral-800"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<LoaderIcon className="size-4 animate-spin" />
|
<LoaderIcon className="size-4 animate-spin" />
|
||||||
|
@ -46,11 +46,9 @@ export function ColumnHeader({
|
|||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<div className="flex items-center justify-center gap-2 px-3 w-full border-b h-11 shrink-0 border-neutral-100 dark:border-neutral-900">
|
<div className="flex items-center justify-center gap-2 px-3 w-full border-b h-11 shrink-0 border-neutral-100 dark:border-neutral-900">
|
||||||
<DropdownMenu.Trigger asChild>
|
<DropdownMenu.Trigger asChild>
|
||||||
<div className="inline-flex items-center gap-3">
|
<div className="inline-flex items-center gap-1.5">
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="text-[13px] font-medium">{title}</div>
|
||||||
<div className="text-[13px] font-medium">{title}</div>
|
<ChevronDownIcon className="size-5" />
|
||||||
<ChevronDownIcon className="size-5 mt-px" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
<DropdownMenu.Portal>
|
<DropdownMenu.Portal>
|
||||||
@ -96,7 +94,7 @@ export function ColumnHeader({
|
|||||||
Move right
|
Move right
|
||||||
</button>
|
</button>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Separator className="h-px my-1 bg-white/10 dark:bg-black/10" />
|
<DropdownMenu.Separator className="h-px my-1 bg-black/10 dark:bg-white/10" />
|
||||||
<DropdownMenu.Item asChild>
|
<DropdownMenu.Item asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -110,6 +110,7 @@ export function InterestModal({
|
|||||||
<div className="flex flex-wrap items-center gap-3">
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
{topic.content.map((hashtag) => (
|
{topic.content.map((hashtag) => (
|
||||||
<button
|
<button
|
||||||
|
key={hashtag}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => toggleHashtag(hashtag)}
|
onClick={() => toggleHashtag(hashtag)}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
@ -131,10 +131,18 @@ export function NoteZap() {
|
|||||||
</Tooltip.Root>
|
</Tooltip.Root>
|
||||||
</Tooltip.Provider>
|
</Tooltip.Provider>
|
||||||
<Dialog.Portal>
|
<Dialog.Portal>
|
||||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/10 backdrop-blur-sm dark:bg-white/10" />
|
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-sm dark:bg-white/20" />
|
||||||
<Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
|
<Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
|
||||||
|
<Dialog.Close className="absolute top-5 right-5 z-50">
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<div className="inline-flex items-center justify-center size-10 rounded-lg bg-white dark:bg-black">
|
||||||
|
<CancelIcon className="size-5" />
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-medium">Esc</span>
|
||||||
|
</div>
|
||||||
|
</Dialog.Close>
|
||||||
<div className="relative w-full max-w-xl bg-white h-min rounded-xl dark:bg-black">
|
<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="inline-flex items-center justify-center w-full px-5 py-3 shrink-0">
|
||||||
<div className="w-6" />
|
<div className="w-6" />
|
||||||
<Dialog.Title className="font-semibold text-center">
|
<Dialog.Title className="font-semibold text-center">
|
||||||
Send zap to{" "}
|
Send zap to{" "}
|
||||||
@ -142,13 +150,10 @@ export function NoteZap() {
|
|||||||
user?.displayName ||
|
user?.displayName ||
|
||||||
displayNpub(event.pubkey, 16)}
|
displayNpub(event.pubkey, 16)}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<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>
|
||||||
{!invoice ? (
|
{!invoice ? (
|
||||||
<div className="px-5 pb-5 overflow-x-hidden overflow-y-auto">
|
<div className="px-5 pb-5 overflow-x-hidden overflow-y-auto">
|
||||||
<div className="relative flex flex-col h-40">
|
<div className="relative flex flex-col h-36">
|
||||||
<div className="inline-flex items-center justify-center flex-1 h-full gap-1">
|
<div className="inline-flex items-center justify-center flex-1 h-full gap-1">
|
||||||
<CurrencyInput
|
<CurrencyInput
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
@ -213,13 +218,13 @@ export function NoteZap() {
|
|||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
placeholder="Enter message (optional)"
|
placeholder="Enter message (optional)"
|
||||||
className="w-full resize-none rounded-lg border-transparent bg-neutral-100 px-3 py-3 !outline-none placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:text-neutral-400"
|
className="w-full resize-none rounded-lg border-transparent bg-neutral-100 px-3 py-3 !outline-none placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-950 dark:text-neutral-400"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => createZapRequest()}
|
onClick={() => createZapRequest()}
|
||||||
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"
|
className="inline-flex items-center justify-center w-full pb-[2px] font-semibold border-t rounded-lg border-neutral-900 dark:border-neutral-800 h-9 bg-neutral-950 text-neutral-50 dark:bg-neutral-900 hover:bg-neutral-900 dark:hover:bg-neutral-800"
|
||||||
>
|
>
|
||||||
{isCompleted
|
{isCompleted
|
||||||
? "Zapped"
|
? "Zapped"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { HorizontalDotsIcon } from "@lume/icons";
|
import { HorizontalDotsIcon } from "@lume/icons";
|
||||||
|
import { COL_TYPES } from "@lume/utils";
|
||||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
@ -6,12 +7,13 @@ import { type EventPointer } from "nostr-tools/lib/types/nip19";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { useColumnContext } from "../column/provider";
|
||||||
import { useNoteContext } from "./provider";
|
import { useNoteContext } from "./provider";
|
||||||
|
|
||||||
export function NoteMenu() {
|
export function NoteMenu() {
|
||||||
const event = useNoteContext();
|
const event = useNoteContext();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { addColumn } = useColumnContext();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const copyID = async () => {
|
const copyID = async () => {
|
||||||
@ -52,9 +54,9 @@ export function NoteMenu() {
|
|||||||
<DropdownMenu.Trigger asChild>
|
<DropdownMenu.Trigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex items-center justify-center w-6 h-6"
|
className="inline-flex items-center justify-center size-6"
|
||||||
>
|
>
|
||||||
<HorizontalDotsIcon className="w-4 h-4 text-neutral-800 hover:text-blue-500 dark:text-neutral-200" />
|
<HorizontalDotsIcon className="size-4 hover:text-blue-500 dark:text-neutral-200" />
|
||||||
</button>
|
</button>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
<DropdownMenu.Portal>
|
<DropdownMenu.Portal>
|
||||||
@ -100,9 +102,24 @@ export function NoteMenu() {
|
|||||||
to={`/users/${event.pubkey}`}
|
to={`/users/${event.pubkey}`}
|
||||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||||
>
|
>
|
||||||
View profile
|
View author
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item asChild>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
addColumn({
|
||||||
|
kind: COL_TYPES.user,
|
||||||
|
title: "User",
|
||||||
|
content: event.pubkey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||||
|
>
|
||||||
|
Pin author
|
||||||
|
</button>
|
||||||
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Separator className="h-px my-1 bg-black/10 dark:bg-white/10" />
|
<DropdownMenu.Separator className="h-px my-1 bg-black/10 dark:bg-white/10" />
|
||||||
<DropdownMenu.Item asChild>
|
<DropdownMenu.Item asChild>
|
||||||
<button
|
<button
|
||||||
|
@ -8,7 +8,7 @@ export function ChildReply({
|
|||||||
<Note.Provider event={event}>
|
<Note.Provider event={event}>
|
||||||
<Note.Root className="py-2">
|
<Note.Root className="py-2">
|
||||||
<div className="flex items-center justify-between h-14">
|
<div className="flex items-center justify-between h-14">
|
||||||
<Note.User className="flex-1" />
|
<Note.User className="flex-1 pr-2" />
|
||||||
<Note.Menu />
|
<Note.Menu />
|
||||||
</div>
|
</div>
|
||||||
<Note.Content />
|
<Note.Content />
|
||||||
|
@ -18,7 +18,7 @@ export function Reply({
|
|||||||
<Note.Provider event={event}>
|
<Note.Provider event={event}>
|
||||||
<Note.Root className="pt-2">
|
<Note.Root className="pt-2">
|
||||||
<div className="flex items-center justify-between h-14">
|
<div className="flex items-center justify-between h-14">
|
||||||
<Note.User className="flex-1 pr-1" />
|
<Note.User className="flex-1 pr-2" />
|
||||||
<Note.Menu />
|
<Note.Menu />
|
||||||
</div>
|
</div>
|
||||||
<Note.Content />
|
<Note.Content />
|
||||||
|
@ -93,7 +93,7 @@ export function RepostNote({
|
|||||||
<Note.Provider event={repostEvent}>
|
<Note.Provider event={repostEvent}>
|
||||||
<div className="relative flex flex-col gap-2 px-3">
|
<div className="relative flex flex-col gap-2 px-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Note.User className="flex-1 pr-1" />
|
<Note.User className="flex-1 pr-2" />
|
||||||
<Note.Menu />
|
<Note.Menu />
|
||||||
</div>
|
</div>
|
||||||
<Note.Content />
|
<Note.Content />
|
||||||
|
@ -15,7 +15,7 @@ export function TextNote({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between px-3 h-14">
|
<div className="flex items-center justify-between px-3 h-14">
|
||||||
<Note.User className="flex-1 pr-1" />
|
<Note.User className="flex-1 pr-2" />
|
||||||
<Note.Menu />
|
<Note.Menu />
|
||||||
</div>
|
</div>
|
||||||
<Note.Thread className="mb-2" />
|
<Note.Thread className="mb-2" />
|
||||||
|
@ -87,31 +87,18 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
|||||||
|
|
||||||
async function initNDK() {
|
async function initNDK() {
|
||||||
const explicitRelayUrls = normalizeRelayUrlSet([
|
const explicitRelayUrls = normalizeRelayUrlSet([
|
||||||
|
"wss://nostr.mutinywallet.com/",
|
||||||
"wss://bostr.nokotaro.com/",
|
"wss://bostr.nokotaro.com/",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// #TODO: user should config outbox relays
|
|
||||||
const outboxRelayUrls = normalizeRelayUrlSet(["wss://purplepag.es/"]);
|
|
||||||
|
|
||||||
// #TODO: user should config blacklist relays
|
|
||||||
// Skip connect depot tunnel url
|
|
||||||
const blacklistRelayUrls = normalizeRelayUrlSet(
|
|
||||||
storage.settings.tunnelUrl.length
|
|
||||||
? [storage.settings.tunnelUrl, "wss://brb.io/"]
|
|
||||||
: ["wss://brb.io/"],
|
|
||||||
);
|
|
||||||
|
|
||||||
const tauriCache = new NDKCacheAdapterTauri(storage);
|
const tauriCache = new NDKCacheAdapterTauri(storage);
|
||||||
const ndk = new NDK({
|
const ndk = new NDK({
|
||||||
cacheAdapter: tauriCache,
|
cacheAdapter: tauriCache,
|
||||||
explicitRelayUrls,
|
explicitRelayUrls,
|
||||||
outboxRelayUrls,
|
|
||||||
blacklistRelayUrls,
|
|
||||||
enableOutboxModel: !storage.settings.lowPower,
|
enableOutboxModel: !storage.settings.lowPower,
|
||||||
autoConnectUserRelays: !storage.settings.lowPower,
|
autoConnectUserRelays: !storage.settings.lowPower,
|
||||||
autoFetchUserMutelist: !storage.settings.lowPower,
|
autoFetchUserMutelist: !storage.settings.lowPower,
|
||||||
// clientName: "Lume",
|
clientName: "Lume",
|
||||||
// clientNip89: '',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// use tauri fetch
|
// use tauri fetch
|
||||||
|
@ -17,7 +17,7 @@ export function LogoutIcon(
|
|||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
d="M18.189 9a15 15 0 012.654 2.556c.105.13.157.287.157.444m-2.811 3a14.998 14.998 0 002.654-2.556A.704.704 0 0021 12m0 0H8m5-7.472A6 6 0 003 9v6a6 6 0 0010 4.472"
|
d="M5.812 9a15.001 15.001 0 00-2.655 2.556A.703.703 0 003 12m2.812 3a15 15 0 01-2.655-2.556A.703.703 0 013 12m0 0h13m-5-7.472A6 6 0 0121 9v6a6 6 0 01-10 4.472"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
@ -1,21 +1,24 @@
|
|||||||
import { SVGProps } from 'react';
|
import { SVGProps } from "react";
|
||||||
|
|
||||||
export function UserIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
export function UserIcon(
|
||||||
return (
|
props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
|
||||||
<svg
|
) {
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
return (
|
||||||
width="24"
|
<svg
|
||||||
height="24"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
width="24"
|
||||||
viewBox="0 0 24 24"
|
height="24"
|
||||||
{...props}
|
fill="none"
|
||||||
>
|
viewBox="0 0 24 24"
|
||||||
<path
|
{...props}
|
||||||
stroke="currentColor"
|
>
|
||||||
strokeLinejoin="round"
|
<path
|
||||||
strokeWidth="1.5"
|
stroke="currentColor"
|
||||||
d="M5.857 18.916C7.171 16.996 9.332 15.75 12 15.75c2.668 0 4.83 1.247 6.143 3.166m-12.286 0A9.215 9.215 0 0012 21.25c2.358 0 4.51-.882 6.143-2.334m-12.286 0a9.25 9.25 0 1112.286 0M15.25 10a3.25 3.25 0 11-6.5 0 3.25 3.25 0 016.5 0z"
|
strokeLinecap="round"
|
||||||
></path>
|
strokeLinejoin="round"
|
||||||
</svg>
|
strokeWidth="2"
|
||||||
);
|
d="M18.995 19.147C18.893 17.393 17.367 16 15.5 16h-7c-1.867 0-3.393 1.393-3.495 3.147m13.99 0A9.97 9.97 0 0022 12c0-5.523-4.477-10-10-10S2 6.477 2 12a9.97 9.97 0 003.005 7.147m13.99 0A9.967 9.967 0 0112 22a9.967 9.967 0 01-6.995-2.853M15 10a3 3 0 11-6 0 3 3 0 016 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useArk, useProfile } from "@lume/ark";
|
import { useArk, useProfile } from "@lume/ark";
|
||||||
import { SettingsIcon } from "@lume/icons";
|
import { SettingsIcon, UserIcon } from "@lume/icons";
|
||||||
import { cn, useNetworkStatus } from "@lume/utils";
|
import { cn, useNetworkStatus } from "@lume/utils";
|
||||||
import * as Avatar from "@radix-ui/react-avatar";
|
import * as Avatar from "@radix-ui/react-avatar";
|
||||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||||
@ -54,17 +54,27 @@ export function ActiveAccount() {
|
|||||||
<DropdownMenu.Content
|
<DropdownMenu.Content
|
||||||
side="right"
|
side="right"
|
||||||
sideOffset={5}
|
sideOffset={5}
|
||||||
className="flex w-[200px] p-2 flex-col overflow-hidden rounded-2xl bg-black/70 dark:bg-white/10 backdrop-blur-xl focus:outline-none"
|
className="relative top-5 flex w-[200px] p-2 flex-col overflow-hidden rounded-2xl bg-white/50 dark:bg-black/50 ring-1 ring-black/10 dark:ring-white/10 backdrop-blur-2xl focus:outline-none"
|
||||||
>
|
>
|
||||||
<DropdownMenu.Item asChild>
|
<DropdownMenu.Item asChild>
|
||||||
<Link
|
<Link
|
||||||
to="/settings/"
|
to="/settings/profile"
|
||||||
className="inline-flex items-center gap-2 px-3 text-sm font-medium rounded-lg h-9 text-white/50 hover:bg-black/10 hover:text-white focus:outline-none dark:text-white/50 dark:hover:bg-white/10 dark:hover:text-white"
|
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||||
>
|
>
|
||||||
<SettingsIcon className="size-5" />
|
<UserIcon className="size-4" />
|
||||||
|
Edit profile
|
||||||
|
</Link>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item asChild>
|
||||||
|
<Link
|
||||||
|
to="/settings/"
|
||||||
|
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||||
|
>
|
||||||
|
<SettingsIcon className="size-4" />
|
||||||
Settings
|
Settings
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Separator className="h-px my-1 bg-black/10 dark:bg-white/10" />
|
||||||
<Logout />
|
<Logout />
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Portal>
|
</DropdownMenu.Portal>
|
||||||
|
@ -35,9 +35,9 @@ export function Logout() {
|
|||||||
<AlertDialog.Trigger asChild>
|
<AlertDialog.Trigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex items-center gap-2 px-3 text-sm font-medium rounded-lg h-9 text-white/50 hover:bg-black/10 hover:text-white focus:outline-none dark:text-white/50 dark:hover:bg-white/10 dark:hover:text-white"
|
className="inline-flex items-center gap-3 px-3 text-sm font-medium rounded-lg h-9 text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
||||||
>
|
>
|
||||||
<LogoutIcon className="size-5" />
|
<LogoutIcon className="size-4" />
|
||||||
Logout
|
Logout
|
||||||
</button>
|
</button>
|
||||||
</AlertDialog.Trigger>
|
</AlertDialog.Trigger>
|
||||||
|
@ -106,7 +106,7 @@ const Image = ({ attributes, children, element }) => {
|
|||||||
return (
|
return (
|
||||||
<div {...attributes}>
|
<div {...attributes}>
|
||||||
{children}
|
{children}
|
||||||
<div contentEditable={false} className="relative">
|
<div contentEditable={false} className="relative my-2">
|
||||||
<img
|
<img
|
||||||
src={element.url}
|
src={element.url}
|
||||||
alt={element.url}
|
alt={element.url}
|
||||||
@ -155,7 +155,7 @@ const Event = ({ attributes, element, children }) => {
|
|||||||
<div
|
<div
|
||||||
contentEditable={false}
|
contentEditable={false}
|
||||||
onClick={() => Transforms.removeNodes(editor, { at: path })}
|
onClick={() => Transforms.removeNodes(editor, { at: path })}
|
||||||
className="relative user-select-none"
|
className="relative user-select-none my-2"
|
||||||
>
|
>
|
||||||
<MentionNote
|
<MentionNote
|
||||||
eventId={element.eventId.replace("nostr:", "")}
|
eventId={element.eventId.replace("nostr:", "")}
|
||||||
@ -319,11 +319,9 @@ export function EditorForm() {
|
|||||||
setTarget(null);
|
setTarget(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between h-16 px-3 border-b shrink-0 border-neutral-100 dark:border-neutral-900 bg-neutral-50 dark:bg-neutral-950">
|
<div className="flex items-center justify-between h-16 pl-7 pr-3 border-b shrink-0 border-neutral-100 dark:border-neutral-900 bg-neutral-50 dark:bg-neutral-950">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-neutral-700 dark:text-neutral-500">
|
<h3 className="font-medium">New Post</h3>
|
||||||
New Post
|
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="inline-flex items-center gap-2">
|
||||||
@ -346,7 +344,7 @@ export function EditorForm() {
|
|||||||
<div className="py-6 h-full overflow-y-auto px-7">
|
<div className="py-6 h-full overflow-y-auto px-7">
|
||||||
<Editable
|
<Editable
|
||||||
key={JSON.stringify(editorValue)}
|
key={JSON.stringify(editorValue)}
|
||||||
autoFocus={false}
|
autoFocus={true}
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
autoCorrect="none"
|
autoCorrect="none"
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
@ -361,9 +359,9 @@ export function EditorForm() {
|
|||||||
className="top-[-9999px] left-[-9999px] absolute z-10 w-[250px] p-1 bg-white border border-neutral-50 dark:border-neutral-900 dark:bg-neutral-950 rounded-lg shadow-lg"
|
className="top-[-9999px] left-[-9999px] absolute z-10 w-[250px] p-1 bg-white border border-neutral-50 dark:border-neutral-900 dark:bg-neutral-950 rounded-lg shadow-lg"
|
||||||
>
|
>
|
||||||
{filters.map((contact, i) => (
|
{filters.map((contact, i) => (
|
||||||
// biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
|
<button
|
||||||
<div
|
|
||||||
key={contact.npub}
|
key={contact.npub}
|
||||||
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
Transforms.select(editor, target);
|
Transforms.select(editor, target);
|
||||||
insertMention(editor, contact);
|
insertMention(editor, contact);
|
||||||
@ -379,7 +377,7 @@ export function EditorForm() {
|
|||||||
</div>
|
</div>
|
||||||
</User.Root>
|
</User.Root>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
</div>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Portal>
|
</Portal>
|
||||||
|
@ -2,7 +2,7 @@ import { NDKCacheUserProfile } from "@lume/types";
|
|||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { BaseEditor, Transforms } from "slate";
|
import { BaseEditor, Transforms } from "slate";
|
||||||
import { type ReactEditor } from "slate-react";
|
import { ReactEditor } from "slate-react";
|
||||||
|
|
||||||
export const Portal = ({ children }: { children?: ReactNode }) => {
|
export const Portal = ({ children }: { children?: ReactNode }) => {
|
||||||
return typeof document === "object"
|
return typeof document === "object"
|
||||||
@ -36,6 +36,8 @@ export const insertImage = (editor: ReactEditor | BaseEditor, url: string) => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// @ts-ignore, idk
|
||||||
|
ReactEditor.focus(editor);
|
||||||
Transforms.insertNodes(editor, image);
|
Transforms.insertNodes(editor, image);
|
||||||
Transforms.insertNodes(editor, extraText);
|
Transforms.insertNodes(editor, extraText);
|
||||||
};
|
};
|
||||||
@ -44,15 +46,24 @@ export const insertMention = (
|
|||||||
editor: ReactEditor | BaseEditor,
|
editor: ReactEditor | BaseEditor,
|
||||||
contact: NDKCacheUserProfile,
|
contact: NDKCacheUserProfile,
|
||||||
) => {
|
) => {
|
||||||
|
const text = { text: "" };
|
||||||
const mention = {
|
const mention = {
|
||||||
type: "mention",
|
type: "mention",
|
||||||
npub: `nostr:${contact.npub}`,
|
npub: `nostr:${contact.npub}`,
|
||||||
name: contact.name || contact.displayName || "anon",
|
name: contact.name || contact.displayName || "anon",
|
||||||
children: [{ text: "" }],
|
children: [text],
|
||||||
};
|
};
|
||||||
|
const extraText = [
|
||||||
|
{
|
||||||
|
type: "paragraph",
|
||||||
|
children: [text],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// @ts-ignore, idk
|
||||||
|
ReactEditor.focus(editor);
|
||||||
Transforms.insertNodes(editor, mention);
|
Transforms.insertNodes(editor, mention);
|
||||||
Transforms.move(editor);
|
Transforms.insertNodes(editor, extraText);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const insertNostrEvent = (
|
export const insertNostrEvent = (
|
||||||
|
Loading…
Reference in New Issue
Block a user