mirror of
https://github.com/lumehq/lume.git
synced 2025-03-28 02:31:49 +01:00
fix errors
This commit is contained in:
parent
bbfdb139c6
commit
8f4cf7e948
@ -44,9 +44,6 @@
|
||||
"react-string-replace": "^1.1.1",
|
||||
"react-virtuoso": "^4.4.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"slate": "^0.94.1",
|
||||
"slate-history": "^0.93.0",
|
||||
"slate-react": "^0.97.2",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tauri-plugin-autostart-api": "github:tauri-apps/tauri-plugin-autostart#v1",
|
||||
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql",
|
||||
|
102
pnpm-lock.yaml
generated
102
pnpm-lock.yaml
generated
@ -85,15 +85,6 @@ dependencies:
|
||||
remark-gfm:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
slate:
|
||||
specifier: ^0.94.1
|
||||
version: 0.94.1
|
||||
slate-history:
|
||||
specifier: ^0.93.0
|
||||
version: 0.93.0(slate@0.94.1)
|
||||
slate-react:
|
||||
specifier: ^0.97.2
|
||||
version: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1)
|
||||
tailwind-merge:
|
||||
specifier: ^1.14.0
|
||||
version: 1.14.0
|
||||
@ -875,10 +866,6 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
dev: true
|
||||
|
||||
/@juggle/resize-observer@3.4.0:
|
||||
resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==}
|
||||
dev: false
|
||||
|
||||
/@noble/curves@1.0.0:
|
||||
resolution: {integrity: sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==}
|
||||
dependencies:
|
||||
@ -1800,10 +1787,6 @@ packages:
|
||||
'@types/unist': 2.0.7
|
||||
dev: false
|
||||
|
||||
/@types/is-hotkey@0.1.7:
|
||||
resolution: {integrity: sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ==}
|
||||
dev: false
|
||||
|
||||
/@types/json-schema@7.0.12:
|
||||
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
||||
|
||||
@ -1811,10 +1794,6 @@ packages:
|
||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||
dev: false
|
||||
|
||||
/@types/lodash@4.14.195:
|
||||
resolution: {integrity: sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==}
|
||||
dev: false
|
||||
|
||||
/@types/mdast@3.0.12:
|
||||
resolution: {integrity: sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==}
|
||||
dependencies:
|
||||
@ -2273,7 +2252,7 @@ packages:
|
||||
hasBin: true
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001517
|
||||
electron-to-chromium: 1.4.465
|
||||
electron-to-chromium: 1.4.466
|
||||
node-releases: 2.0.13
|
||||
update-browserslist-db: 1.0.11(browserslist@4.21.9)
|
||||
dev: true
|
||||
@ -2480,10 +2459,6 @@ packages:
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/compute-scroll-into-view@1.0.20:
|
||||
resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
|
||||
dev: false
|
||||
|
||||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
@ -2684,11 +2659,6 @@ packages:
|
||||
dependencies:
|
||||
path-type: 4.0.0
|
||||
|
||||
/direction@1.0.4:
|
||||
resolution: {integrity: sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/dlv@1.1.3:
|
||||
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
|
||||
dev: true
|
||||
@ -2735,8 +2705,8 @@ packages:
|
||||
/eastasianwidth@0.2.0:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
|
||||
/electron-to-chromium@1.4.465:
|
||||
resolution: {integrity: sha512-XQcuHvEJRMU97UJ75e170mgcITZoz0lIyiaVjk6R+NMTJ8KBIvUHYd1779swgOppUlzxR+JsLpq59PumaXS1jQ==}
|
||||
/electron-to-chromium@1.4.466:
|
||||
resolution: {integrity: sha512-TSkRvbXRXD8BwhcGlZXDsbI2lRoP8dvqR7LQnqQNk9KxXBc4tG8O+rTuXgTyIpEdiqSGKEBSqrxdqEntnjNncA==}
|
||||
dev: true
|
||||
|
||||
/emoji-regex@8.0.0:
|
||||
@ -3649,10 +3619,6 @@ packages:
|
||||
resolution: {integrity: sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==}
|
||||
dev: false
|
||||
|
||||
/immer@9.0.21:
|
||||
resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==}
|
||||
dev: false
|
||||
|
||||
/import-fresh@3.3.0:
|
||||
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
||||
engines: {node: '>=6'}
|
||||
@ -3783,10 +3749,6 @@ packages:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
|
||||
/is-hotkey@0.1.8:
|
||||
resolution: {integrity: sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==}
|
||||
dev: false
|
||||
|
||||
/is-lambda@1.0.1:
|
||||
resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
|
||||
dev: false
|
||||
@ -3819,11 +3781,6 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/is-plain-object@5.0.0:
|
||||
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/is-regex@1.1.4:
|
||||
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -4086,6 +4043,7 @@ packages:
|
||||
|
||||
/lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
dev: true
|
||||
|
||||
/log-symbols@4.1.0:
|
||||
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
|
||||
@ -5713,12 +5671,6 @@ packages:
|
||||
loose-envify: 1.4.0
|
||||
dev: false
|
||||
|
||||
/scroll-into-view-if-needed@2.2.31:
|
||||
resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==}
|
||||
dependencies:
|
||||
compute-scroll-into-view: 1.0.20
|
||||
dev: false
|
||||
|
||||
/semver@5.7.2:
|
||||
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
|
||||
hasBin: true
|
||||
@ -5784,44 +5736,6 @@ packages:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
/slate-history@0.93.0(slate@0.94.1):
|
||||
resolution: {integrity: sha512-Gr1GMGPipRuxIz41jD2/rbvzPj8eyar56TVMyJBvBeIpQSSjNISssvGNDYfJlSWM8eaRqf6DAcxMKzsLCYeX6g==}
|
||||
peerDependencies:
|
||||
slate: '>=0.65.3'
|
||||
dependencies:
|
||||
is-plain-object: 5.0.0
|
||||
slate: 0.94.1
|
||||
dev: false
|
||||
|
||||
/slate-react@0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1):
|
||||
resolution: {integrity: sha512-jVUbTU+0MnbT7l09thQnWfM7gneTFGMsybmXX9utryQnbttKwIo3NynPI6chGwiz0N4/6k5Yb4fc8N/eZkYHUw==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
slate: '>=0.65.3'
|
||||
dependencies:
|
||||
'@juggle/resize-observer': 3.4.0
|
||||
'@types/is-hotkey': 0.1.7
|
||||
'@types/lodash': 4.14.195
|
||||
direction: 1.0.4
|
||||
is-hotkey: 0.1.8
|
||||
is-plain-object: 5.0.0
|
||||
lodash: 4.17.21
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
scroll-into-view-if-needed: 2.2.31
|
||||
slate: 0.94.1
|
||||
tiny-invariant: 1.0.6
|
||||
dev: false
|
||||
|
||||
/slate@0.94.1:
|
||||
resolution: {integrity: sha512-GH/yizXr1ceBoZ9P9uebIaHe3dC/g6Plpf9nlUwnvoyf6V1UOYrRwkabtOCd3ZfIGxomY4P7lfgLr7FPH8/BKA==}
|
||||
dependencies:
|
||||
immer: 9.0.21
|
||||
is-plain-object: 5.0.0
|
||||
tiny-warning: 1.0.3
|
||||
dev: false
|
||||
|
||||
/slice-ansi@3.0.0:
|
||||
resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
|
||||
engines: {node: '>=8'}
|
||||
@ -6137,14 +6051,6 @@ packages:
|
||||
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
|
||||
dev: true
|
||||
|
||||
/tiny-invariant@1.0.6:
|
||||
resolution: {integrity: sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==}
|
||||
dev: false
|
||||
|
||||
/tiny-warning@1.0.3:
|
||||
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
|
||||
dev: false
|
||||
|
||||
/tlds@1.240.0:
|
||||
resolution: {integrity: sha512-1OYJQenswGZSOdRw7Bql5Qu7uf75b+F3HFBXbqnG/ifHa0fev1XcG+3pJf3pA/KC6RtHQzfKgIf1vkMlMG7mtQ==}
|
||||
hasBin: true
|
||||
|
@ -40,7 +40,7 @@ export function HashtagBlock({ params }: { params: Block }) {
|
||||
>
|
||||
{status === 'loading' ? (
|
||||
<div className="px-3 py-1.5">
|
||||
<div className="shadow-input rounded-md bg-zinc-900 px-3 py-3 shadow-black/20">
|
||||
<div className="rounded-xl border-t border-zinc-800/50 bg-zinc-900 px-3 pt-3">
|
||||
<NoteSkeleton />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,125 +1,100 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { useRef } from 'react';
|
||||
|
||||
import { UserFeed } from '@app/user/components/feed';
|
||||
import { UserMetadata } from '@app/user/components/metadata';
|
||||
import { useNDK } from '@libs/ndk/provider';
|
||||
|
||||
import { ZapIcon } from '@shared/icons';
|
||||
import { Image } from '@shared/image';
|
||||
import { NoteKind_1, NoteSkeleton } from '@shared/notes';
|
||||
import { TitleBar } from '@shared/titleBar';
|
||||
import { UserProfile } from '@shared/userProfile';
|
||||
|
||||
import { DEFAULT_AVATAR } from '@stores/constants';
|
||||
|
||||
import { useProfile } from '@utils/hooks/useProfile';
|
||||
import { useSocial } from '@utils/hooks/useSocial';
|
||||
import { shortenKey } from '@utils/shortenKey';
|
||||
import { Block } from '@utils/types';
|
||||
import { nHoursAgo } from '@utils/date';
|
||||
import { Block, LumeEvent } from '@utils/types';
|
||||
|
||||
export function UserBlock({ params }: { params: Block }) {
|
||||
const { user } = useProfile(params.content);
|
||||
const { status, userFollows, follow, unfollow } = useSocial();
|
||||
const parentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [followed, setFollowed] = useState(false);
|
||||
const { fetcher, relayUrls } = useNDK();
|
||||
const { status, data } = useQuery(['user-feed', params.content], async () => {
|
||||
const events = await fetcher.fetchAllEvents(
|
||||
relayUrls,
|
||||
{ kinds: [1], authors: [params.content] },
|
||||
{ since: nHoursAgo(48) },
|
||||
{ sort: true }
|
||||
);
|
||||
return events as unknown as LumeEvent[];
|
||||
});
|
||||
|
||||
const followUser = (pubkey: string) => {
|
||||
try {
|
||||
follow(pubkey);
|
||||
const rowVirtualizer = useVirtualizer({
|
||||
count: data ? data.length : 0,
|
||||
getScrollElement: () => parentRef.current,
|
||||
estimateSize: () => 400,
|
||||
});
|
||||
|
||||
// update state
|
||||
setFollowed(true);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const unfollowUser = (pubkey: string) => {
|
||||
try {
|
||||
unfollow(pubkey);
|
||||
|
||||
// update state
|
||||
setFollowed(false);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (status === 'success' && userFollows) {
|
||||
if (userFollows.includes(params.content)) {
|
||||
setFollowed(true);
|
||||
}
|
||||
}
|
||||
}, [status]);
|
||||
const itemsVirtualizer = rowVirtualizer.getVirtualItems();
|
||||
|
||||
return (
|
||||
<div className="w-[400px] shrink-0 border-r border-zinc-900">
|
||||
<TitleBar id={params.id} title={params.title} />
|
||||
<div className="scrollbar-hide flex h-full w-full flex-col gap-1.5 overflow-y-auto pb-20 pt-1.5">
|
||||
<div
|
||||
ref={parentRef}
|
||||
className="scrollbar-hide flex h-full w-full flex-col gap-1.5 overflow-y-auto pt-1.5"
|
||||
>
|
||||
<div className="px-3 pt-1.5">
|
||||
<Image
|
||||
src={user?.picture || user?.image}
|
||||
fallback={DEFAULT_AVATAR}
|
||||
alt={params.content}
|
||||
className="h-14 w-14 rounded-md ring-2 ring-black"
|
||||
/>
|
||||
<div className="mt-2 flex flex-1 flex-col gap-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h5 className="text-lg font-semibold leading-none">
|
||||
{user?.displayName || user?.name || 'No name'}
|
||||
</h5>
|
||||
<span className="max-w-[15rem] truncate text-sm leading-none text-zinc-500">
|
||||
{user?.nip05 || shortenKey(params.content)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="mt-2 max-w-[500px] select-text break-words text-zinc-100">
|
||||
{user?.about}
|
||||
</p>
|
||||
<UserMetadata pubkey={params.content} />
|
||||
</div>
|
||||
<div className="mt-4 inline-flex items-center gap-2">
|
||||
{status === 'loading' ? (
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-zinc-900 text-sm font-medium hover:bg-fuchsia-500"
|
||||
>
|
||||
Loading...
|
||||
</button>
|
||||
) : followed ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => unfollowUser(params.content)}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-zinc-900 text-sm font-medium hover:bg-fuchsia-500"
|
||||
>
|
||||
Unfollow
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => followUser(params.content)}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-zinc-900 text-sm font-medium hover:bg-fuchsia-500"
|
||||
>
|
||||
Follow
|
||||
</button>
|
||||
)}
|
||||
<Link
|
||||
to={`/app/chat/${params.content}`}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-zinc-900 text-sm font-medium hover:bg-fuchsia-500"
|
||||
>
|
||||
Message
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="group inline-flex h-10 w-10 items-center justify-center rounded-md bg-zinc-900 text-sm font-medium hover:bg-orange-500"
|
||||
>
|
||||
<ZapIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<UserProfile pubkey={params.content} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="mt-2 px-3 text-lg font-semibold text-zinc-300">Timeline</h3>
|
||||
<UserFeed pubkey={params.content} />
|
||||
<h3 className="mt-2 px-3 text-lg font-semibold text-zinc-300">
|
||||
Latest activities
|
||||
</h3>
|
||||
<div
|
||||
className="flex h-full w-full flex-col justify-between gap-1.5"
|
||||
style={{ contain: 'strict' }}
|
||||
>
|
||||
{status === 'loading' ? (
|
||||
<div className="px-3 py-1.5">
|
||||
<div className="shadow-input rounded-md bg-zinc-900 px-3 py-3 shadow-black/20">
|
||||
<NoteSkeleton />
|
||||
</div>
|
||||
</div>
|
||||
) : itemsVirtualizer.length === 0 ? (
|
||||
<div className="px-3 py-1.5">
|
||||
<div className="rounded-xl border-t border-zinc-800/50 bg-zinc-900 px-3 py-6">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<p className="text-center text-sm text-zinc-300">
|
||||
No new posts about this hashtag in 48 hours ago
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="relative w-full"
|
||||
style={{
|
||||
height: `${rowVirtualizer.getTotalSize()}px`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="absolute left-0 top-0 w-full"
|
||||
style={{
|
||||
transform: `translateY(${
|
||||
itemsVirtualizer[0].start - rowVirtualizer.options.scrollMargin
|
||||
}px)`,
|
||||
}}
|
||||
>
|
||||
{itemsVirtualizer.map((virtualRow) => (
|
||||
<div
|
||||
key={virtualRow.key}
|
||||
data-index={virtualRow.index}
|
||||
ref={rowVirtualizer.measureElement}
|
||||
>
|
||||
<NoteKind_1 event={data[virtualRow.index]} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -37,16 +37,16 @@ export function Repost({ event }: { event: LumeEvent }) {
|
||||
<div className="flex flex-col">
|
||||
<div className="isolate flex flex-col -space-y-4 overflow-hidden">
|
||||
<RepostUser pubkey={event.pubkey} />
|
||||
<User pubkey={data.pubkey} time={event.created_at} isRepost={true} />
|
||||
<User pubkey={data.pubkey} time={data.created_at} isRepost={true} />
|
||||
</div>
|
||||
<div className="relative z-20 flex items-start gap-3">
|
||||
<div className="w-11 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<NoteContent content={data.content} />
|
||||
<NoteActions id={event.event_id} pubkey={event.pubkey} />
|
||||
<NoteActions id={repostID} pubkey={data.pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
<NoteMetadata id={event.event_id} />
|
||||
<NoteMetadata id={repostID} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@ export function SubNote({ id }: { id: string }) {
|
||||
|
||||
if (status === 'loading') {
|
||||
return (
|
||||
<div className="relative overflow-hidden rounded-xl border-t border-zinc-800/50 bg-zinc-900 px-3 pt-3">
|
||||
<div className="relative mb-5 overflow-hidden rounded-xl bg-zinc-900 pt-3">
|
||||
<NoteSkeleton />
|
||||
</div>
|
||||
);
|
||||
@ -16,7 +16,7 @@ export function SubNote({ id }: { id: string }) {
|
||||
|
||||
if (status === 'error') {
|
||||
return (
|
||||
<div className="flex items-center justify-center overflow-hidden rounded-xl border-t border-zinc-800/50 bg-zinc-900 px-3 py-3">
|
||||
<div className="mb-5 flex overflow-hidden rounded-xl bg-zinc-800 px-3 py-3">
|
||||
<p className="text-zinc-400">Failed to fetch</p>
|
||||
</div>
|
||||
);
|
||||
|
@ -2,16 +2,16 @@ export function NoteSkeleton() {
|
||||
return (
|
||||
<div className="flex h-min flex-col pb-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="relative h-11 w-11 shrink overflow-hidden rounded-md bg-zinc-700" />
|
||||
<div className="relative h-11 w-11 shrink overflow-hidden rounded-lg bg-zinc-700" />
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<div className="h-4 w-20 rounded bg-zinc-700" />
|
||||
<div className="h-3 w-20 rounded bg-zinc-700" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="-mt-5 animate-pulse pl-[49px]">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="h-4 w-full rounded-sm bg-zinc-700" />
|
||||
<div className="h-3 w-2/3 rounded-sm bg-zinc-700" />
|
||||
<div className="h-3 w-1/2 rounded-sm bg-zinc-700" />
|
||||
<div className="h-3 w-full rounded bg-zinc-700" />
|
||||
<div className="h-3 w-2/3 rounded bg-zinc-700" />
|
||||
<div className="h-3 w-1/2 rounded bg-zinc-700" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
115
src/shared/userProfile.tsx
Normal file
115
src/shared/userProfile.tsx
Normal file
@ -0,0 +1,115 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { UserMetadata } from '@app/user/components/metadata';
|
||||
|
||||
import { ZapIcon } from '@shared/icons';
|
||||
import { Image } from '@shared/image';
|
||||
|
||||
import { DEFAULT_AVATAR } from '@stores/constants';
|
||||
|
||||
import { useProfile } from '@utils/hooks/useProfile';
|
||||
import { useSocial } from '@utils/hooks/useSocial';
|
||||
import { displayNpub } from '@utils/shortenKey';
|
||||
|
||||
export function UserProfile({ pubkey }: { pubkey: string }) {
|
||||
const { user } = useProfile(pubkey);
|
||||
const { status, userFollows, follow, unfollow } = useSocial();
|
||||
|
||||
const [followed, setFollowed] = useState(false);
|
||||
|
||||
const followUser = (pubkey: string) => {
|
||||
try {
|
||||
follow(pubkey);
|
||||
|
||||
// update state
|
||||
setFollowed(true);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const unfollowUser = (pubkey: string) => {
|
||||
try {
|
||||
unfollow(pubkey);
|
||||
|
||||
// update state
|
||||
setFollowed(false);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (status === 'success' && userFollows) {
|
||||
if (userFollows.includes(pubkey)) {
|
||||
setFollowed(true);
|
||||
}
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Image
|
||||
src={user?.picture || user?.image}
|
||||
fallback={DEFAULT_AVATAR}
|
||||
alt={pubkey}
|
||||
className="h-14 w-14 rounded-md ring-2 ring-black"
|
||||
/>
|
||||
<div className="mt-2 flex flex-1 flex-col gap-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h5 className="text-lg font-semibold leading-none">
|
||||
{user?.displayName || user?.name || 'No name'}
|
||||
</h5>
|
||||
<span className="max-w-[15rem] truncate text-sm leading-none text-zinc-500">
|
||||
{user?.nip05 || displayNpub(pubkey, 16)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="mt-2 max-w-[500px] select-text break-words text-zinc-100">
|
||||
{user?.about}
|
||||
</p>
|
||||
<UserMetadata pubkey={pubkey} />
|
||||
</div>
|
||||
<div className="mt-4 inline-flex items-center gap-2">
|
||||
{status === 'loading' ? (
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-zinc-900 text-sm font-medium hover:bg-fuchsia-500"
|
||||
>
|
||||
Loading...
|
||||
</button>
|
||||
) : followed ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => unfollowUser(pubkey)}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-zinc-900 text-sm font-medium hover:bg-fuchsia-500"
|
||||
>
|
||||
Unfollow
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => followUser(pubkey)}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-zinc-900 text-sm font-medium hover:bg-fuchsia-500"
|
||||
>
|
||||
Follow
|
||||
</button>
|
||||
)}
|
||||
<Link
|
||||
to={`/app/chat/${pubkey}`}
|
||||
className="inline-flex h-10 w-36 items-center justify-center rounded-md bg-zinc-900 text-sm font-medium hover:bg-fuchsia-500"
|
||||
>
|
||||
Message
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="group inline-flex h-10 w-10 items-center justify-center rounded-md bg-zinc-900 text-sm font-medium hover:bg-orange-500"
|
||||
>
|
||||
<ZapIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -47,7 +47,7 @@ export function useEvent(id: string, fallback?: string) {
|
||||
}
|
||||
return event;
|
||||
} else {
|
||||
return null;
|
||||
throw new Error('Event not found');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,18 @@
|
||||
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { useNDK } from '@libs/ndk/provider';
|
||||
import { createNote } from '@libs/storage';
|
||||
|
||||
import { dateToUnix, getHourAgo, nHoursAgo } from '@utils/date';
|
||||
import { nHoursAgo } from '@utils/date';
|
||||
import { useAccount } from '@utils/hooks/useAccount';
|
||||
import { usePublish } from '@utils/hooks/usePublish';
|
||||
import { nip02ToArray } from '@utils/transform';
|
||||
import { LumeEvent } from '@utils/types';
|
||||
|
||||
export function useSocial() {
|
||||
const queryClient = useQueryClient();
|
||||
const publish = usePublish();
|
||||
|
||||
const { ndk, fetcher, relayUrls } = useNDK();
|
||||
const { fetcher, relayUrls } = useNDK();
|
||||
const { account } = useAccount();
|
||||
const { status, data: userFollows } = useQuery(
|
||||
['userFollows', account.pubkey],
|
||||
|
Loading…
x
Reference in New Issue
Block a user