wip: update browse user screen

This commit is contained in:
Ren Amamiya 2023-09-24 15:42:49 +07:00
parent 41b12746a7
commit c049fa8865
8 changed files with 128 additions and 27 deletions

View File

@ -18,6 +18,7 @@
"**/*.{ts, tsx, css, md, html, json}": "prettier --cache --write" "**/*.{ts, tsx, css, md, html, json}": "prettier --cache --write"
}, },
"dependencies": { "dependencies": {
"@dnd-kit/core": "^6.0.8",
"@getalby/sdk": "^2.4.0", "@getalby/sdk": "^2.4.0",
"@nostr-dev-kit/ndk": "^1.2.1", "@nostr-dev-kit/ndk": "^1.2.1",
"@nostr-fetch/adapter-ndk": "^0.12.2", "@nostr-fetch/adapter-ndk": "^0.12.2",
@ -57,6 +58,7 @@
"react-router-dom": "^6.16.0", "react-router-dom": "^6.16.0",
"react-textarea-autosize": "^8.5.3", "react-textarea-autosize": "^8.5.3",
"react-virtuoso": "^4.6.0", "react-virtuoso": "^4.6.0",
"react-zoom-pan-pinch": "^3.1.0",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1", "tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1",
"tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#v1", "tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#v1",

View File

@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
'@dnd-kit/core':
specifier: ^6.0.8
version: 6.0.8(react-dom@18.2.0)(react@18.2.0)
'@getalby/sdk': '@getalby/sdk':
specifier: ^2.4.0 specifier: ^2.4.0
version: 2.4.0 version: 2.4.0
@ -122,6 +125,9 @@ dependencies:
react-virtuoso: react-virtuoso:
specifier: ^4.6.0 specifier: ^4.6.0
version: 4.6.0(react-dom@18.2.0)(react@18.2.0) version: 4.6.0(react-dom@18.2.0)(react@18.2.0)
react-zoom-pan-pinch:
specifier: ^3.1.0
version: 3.1.0(react-dom@18.2.0)(react@18.2.0)
remark-gfm: remark-gfm:
specifier: ^3.0.1 specifier: ^3.0.1
version: 3.0.1 version: 3.0.1
@ -374,6 +380,37 @@ packages:
'@babel/helper-validator-identifier': 7.22.20 '@babel/helper-validator-identifier': 7.22.20
to-fast-properties: 2.0.0 to-fast-properties: 2.0.0
/@dnd-kit/accessibility@3.0.1(react@18.2.0):
resolution: {integrity: sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==}
peerDependencies:
react: '>=16.8.0'
dependencies:
react: 18.2.0
tslib: 2.6.2
dev: false
/@dnd-kit/core@6.0.8(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-lYaoP8yHTQSLlZe6Rr9qogouGUz9oRUj4AHhDQGQzq/hqaJRpFo65X+JKsdHf8oUFBzx5A+SJPUvxAwTF2OabA==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
'@dnd-kit/accessibility': 3.0.1(react@18.2.0)
'@dnd-kit/utilities': 3.2.1(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
tslib: 2.6.2
dev: false
/@dnd-kit/utilities@3.2.1(react@18.2.0):
resolution: {integrity: sha512-OOXqISfvBw/1REtkSK2N3Fi2EQiLMlWUlqnOK/UpOISqBZPWpE6TqL+jcPtMOkE8TqYGiURvRdPSI9hltNUjEA==}
peerDependencies:
react: '>=16.8.0'
dependencies:
react: 18.2.0
tslib: 2.6.2
dev: false
/@emotion/babel-plugin@11.11.0: /@emotion/babel-plugin@11.11.0:
resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
dependencies: dependencies:
@ -5407,6 +5444,17 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/react-zoom-pan-pinch@3.1.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-a3LlP8QPgTikvteCNkZ3X6wIWC0lrg1geP5WkUJyx2MXXAhHQek3r17N1nT/esOiWGuPIECnsd9AGoK8jOeGcg==}
engines: {node: '>=8', npm: '>=5'}
peerDependencies:
react: '*'
react-dom: '*'
dependencies:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/react@18.2.0: /react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}

View File

@ -1,10 +1,10 @@
import { useDraggable } from '@dnd-kit/core';
import * as Dialog from '@radix-ui/react-dialog'; import * as Dialog from '@radix-ui/react-dialog';
import { memo, useEffect, useState } from 'react'; import { memo, useEffect, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
import { PlusIcon } from '@shared/icons';
import { Image } from '@shared/image'; import { Image } from '@shared/image';
import { NIP05 } from '@shared/nip05'; import { NIP05 } from '@shared/nip05';
import { TextNote } from '@shared/notes'; import { TextNote } from '@shared/notes';
@ -18,9 +18,19 @@ export const UserDrawer = memo(function UserDrawer({ pubkey }: { pubkey: string
const { db } = useStorage(); const { db } = useStorage();
const { status, user } = useProfile(pubkey); const { status, user } = useProfile(pubkey);
const { addContact, removeContact } = useNostr(); const { addContact, removeContact } = useNostr();
const { attributes, listeners, setNodeRef, transform } = useDraggable({
id: pubkey,
});
const [followed, setFollowed] = useState(false); const [followed, setFollowed] = useState(false);
const style = transform
? {
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
zIndex: 20,
}
: undefined;
const followUser = (pubkey: string) => { const followUser = (pubkey: string) => {
try { try {
addContact(pubkey); addContact(pubkey);
@ -51,26 +61,20 @@ export const UserDrawer = memo(function UserDrawer({ pubkey }: { pubkey: string
return ( return (
<Dialog.Root> <Dialog.Root>
<div className="group relative"> <Dialog.Trigger asChild>
<Dialog.Trigger asChild> <button
<button type="button" className="relative z-10"> type="button"
<User pubkey={pubkey} variant="avatar" /> ref={setNodeRef}
</button> style={style}
</Dialog.Trigger> {...listeners}
<div className="absolute -bottom-14 left-0 flex flex-col opacity-0 transition-all duration-300 ease-smooth group-hover:-bottom-16 group-hover:opacity-100"> {...attributes}
<div className="mt-4"> >
<button <User pubkey={pubkey} variant="avatar" />
type="button" </button>
className="inline-flex h-12 w-12 items-center justify-center rounded-lg bg-white/10 backdrop-blur-xl hover:bg-white/20" </Dialog.Trigger>
>
<PlusIcon className="h-4 w-4 text-white" />
</button>
</div>
</div>
</div>
<Dialog.Portal> <Dialog.Portal>
<Dialog.Content className="fixed right-0 top-0 z-50 flex h-full w-[400px] items-center justify-center px-4 pb-4 pt-16"> <Dialog.Content className="fixed right-0 top-0 z-50 flex h-full w-[400px] items-center justify-center px-4 pb-4 pt-16">
<div className="h-full w-full overflow-y-auto rounded-lg border-t border-white/10 bg-white/20 px-3 py-3 backdrop-blur-xl"> <div className="h-full w-full overflow-y-auto rounded-lg border-t border-white/10 bg-white/20 px-3 py-3 backdrop-blur-3xl">
{status === 'loading' ? ( {status === 'loading' ? (
<div> <div>
<p>Loading...</p> <p>Loading...</p>

View File

@ -0,0 +1,22 @@
import { useDroppable } from '@dnd-kit/core';
import { twMerge } from 'tailwind-merge';
import { PlusIcon } from '@shared/icons';
export function UserDropable() {
const { isOver, setNodeRef } = useDroppable({
id: 'newBlock',
});
return (
<div
ref={setNodeRef}
className={twMerge(
'inline-flex h-12 w-12 items-center justify-center rounded-lg border-t border-white/10 backdrop-blur-xl',
isOver ? 'bg-fuchsia-500' : 'bg-white/20 hover:bg-white/30'
)}
>
<PlusIcon className="h-4 w-4 text-white" />
</div>
);
}

View File

@ -37,7 +37,7 @@ export function BrowseScreen() {
<div className="absolute z-10 h-full w-full"> <div className="absolute z-10 h-full w-full">
<DotsPattern className="h-full w-full text-white/10" /> <DotsPattern className="h-full w-full text-white/10" />
</div> </div>
<div className="relative z-20"> <div className="relative z-20 h-full w-full">
<Outlet /> <Outlet />
</div> </div>
</div> </div>

View File

@ -1,6 +1,8 @@
import { DndContext } from '@dnd-kit/core';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { UserDrawer } from '@app/browse/components/userDrawer'; import { UserDrawer } from '@app/browse/components/userDrawer';
import { UserDropable } from '@app/browse/components/userDropable';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
@ -10,16 +12,31 @@ import { getMultipleRandom } from '@utils/transform';
export function BrowseUsersScreen() { export function BrowseUsersScreen() {
const { db } = useStorage(); const { db } = useStorage();
const data = useMemo(() => getMultipleRandom(db.account.follows, 10), []); const data = useMemo(() => getMultipleRandom(db.account.follows, 10), []);
const handleDragEnd = (event) => {
console.log(event.id);
};
return ( return (
<div> <DndContext onDragEnd={handleDragEnd}>
<User pubkey={db.account.pubkey} variant="avatar" /> <div className="scrollbar-hide flex h-full w-full flex-col items-center justify-center overflow-x-auto overflow-y-auto">
<div className="flex items-center gap-4"> <div className="flex items-center gap-16">
{data.map((user) => ( <div className="flex flex-col gap-1">
<UserDrawer key={user} pubkey={user} /> <h3 className="text-sm font-semibold text-fuchsia-500">Follows</h3>
))} <div className="grid grid-cols-5 gap-6 rounded-lg border border-fuchsia-500/50 bg-fuchsia-500/10 p-4">
{data.map((user) => (
<UserDrawer key={user} pubkey={user} />
))}
</div>
</div>
<div className="flex items-center gap-16">
<User pubkey={db.account.pubkey} variant="avatar" />
<UserDropable />
</div>
</div>
</div> </div>
</div> </DndContext>
); );
} }

View File

@ -2,6 +2,10 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
.border {
background-clip: padding-box;
}
.scrollbar-hide::-webkit-scrollbar { .scrollbar-hide::-webkit-scrollbar {
display: none; display: none;
} }

View File

@ -35,6 +35,10 @@ export const User = memo(function User({
const createdAt = time ? formatCreatedAt(time, variant === 'chat') : 0; const createdAt = time ? formatCreatedAt(time, variant === 'chat') : 0;
if (status === 'loading') { if (status === 'loading') {
if (variant === 'avatar') {
<div className="h-12 w-12 animate-pulse overflow-hidden rounded-lg bg-white/10 backdrop-blur-xl" />;
}
if (variant === 'mention') { if (variant === 'mention') {
return ( return (
<div className="relative flex items-center gap-3"> <div className="relative flex items-center gap-3">