Profile hover cards
This commit is contained in:
parent
b62b877f5a
commit
8d882a0844
@ -16,6 +16,7 @@
|
|||||||
"@snort/system-web": "workspace:*",
|
"@snort/system-web": "workspace:*",
|
||||||
"@szhsin/react-menu": "^3.3.1",
|
"@szhsin/react-menu": "^3.3.1",
|
||||||
"@types/use-sync-external-store": "^0.0.4",
|
"@types/use-sync-external-store": "^0.0.4",
|
||||||
|
"@uidotdev/usehooks": "^2.3.1",
|
||||||
"@void-cat/api": "^1.0.4",
|
"@void-cat/api": "^1.0.4",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"dexie": "^3.2.4",
|
"dexie": "^3.2.4",
|
||||||
|
@ -248,9 +248,13 @@ export default function Text({
|
|||||||
chunks.push(<CashuNuts token={element.content} />);
|
chunks.push(<CashuNuts token={element.content} />);
|
||||||
}
|
}
|
||||||
if (element.type === "link" || (element.type === "media" && element.mimeType?.startsWith("unknown"))) {
|
if (element.type === "link" || (element.type === "media" && element.mimeType?.startsWith("unknown"))) {
|
||||||
chunks.push(
|
if (disableMedia ?? false) {
|
||||||
<HyperText link={element.content} depth={depth} showLinkPreview={!(disableLinkPreview ?? false)} />,
|
chunks.push(<DisableMedia content={element.content} />);
|
||||||
);
|
} else {
|
||||||
|
chunks.push(
|
||||||
|
<HyperText link={element.content} depth={depth} showLinkPreview={!(disableLinkPreview ?? false)} />,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (element.type === "custom_emoji") {
|
if (element.type === "custom_emoji") {
|
||||||
chunks.push(<ProxyImg src={element.content} size={15} className="custom-emoji" />);
|
chunks.push(<ProxyImg src={element.content} size={15} className="custom-emoji" />);
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
.follow-button {
|
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
import "./FollowButton.css";
|
|
||||||
import FormattedMessage from "Element/FormattedMessage";
|
import FormattedMessage from "Element/FormattedMessage";
|
||||||
import { HexKey } from "@snort/system";
|
import { HexKey } from "@snort/system";
|
||||||
|
|
||||||
@ -20,7 +19,7 @@ export default function FollowButton(props: FollowButtonProps) {
|
|||||||
const publisher = useEventPublisher();
|
const publisher = useEventPublisher();
|
||||||
const { follows, relays, readonly } = useLogin(s => ({ follows: s.follows, relays: s.relays, readonly: s.readonly }));
|
const { follows, relays, readonly } = useLogin(s => ({ follows: s.follows, relays: s.relays, readonly: s.readonly }));
|
||||||
const isFollowing = follows.item.includes(pubkey);
|
const isFollowing = follows.item.includes(pubkey);
|
||||||
const baseClassname = `${props.className ? ` ${props.className}` : ""}follow-button`;
|
const baseClassname = props.className ? `${props.className} ` : "";
|
||||||
|
|
||||||
async function follow(pubkey: HexKey) {
|
async function follow(pubkey: HexKey) {
|
||||||
if (publisher) {
|
if (publisher) {
|
||||||
@ -42,9 +41,12 @@ export default function FollowButton(props: FollowButtonProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncButton
|
<AsyncButton
|
||||||
className={isFollowing ? `${baseClassname} secondary` : baseClassname}
|
className={isFollowing ? `${baseClassname} secondary` : `${baseClassname} primary`}
|
||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
onClick={() => (isFollowing ? unfollow(pubkey) : follow(pubkey))}>
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
isFollowing ? unfollow(pubkey) : follow(pubkey);
|
||||||
|
}}>
|
||||||
{isFollowing ? <FormattedMessage {...messages.Unfollow} /> : <FormattedMessage {...messages.Follow} />}
|
{isFollowing ? <FormattedMessage {...messages.Unfollow} /> : <FormattedMessage {...messages.Follow} />}
|
||||||
</AsyncButton>
|
</AsyncButton>
|
||||||
);
|
);
|
||||||
|
@ -43,3 +43,15 @@ a.pfp {
|
|||||||
background-color: var(--gray-superdark);
|
background-color: var(--gray-superdark);
|
||||||
transform: rotate(135deg);
|
transform: rotate(135deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.profile-card {
|
||||||
|
width: 360px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: var(--gray-superdark);
|
||||||
|
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-card > div {
|
||||||
|
color: white;
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import "./ProfileImage.css";
|
import "./ProfileImage.css";
|
||||||
|
|
||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode, useEffect, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { HexKey, UserMetadata } from "@snort/system";
|
import { HexKey, UserMetadata } from "@snort/system";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
|
import { useHover } from "@uidotdev/usehooks";
|
||||||
|
import { ControlledMenu } from "@szhsin/react-menu";
|
||||||
|
|
||||||
import { profileLink } from "SnortUtils";
|
import { profileLink } from "SnortUtils";
|
||||||
import Avatar from "Element/User/Avatar";
|
import Avatar from "Element/User/Avatar";
|
||||||
@ -11,6 +13,9 @@ import Nip05 from "Element/User/Nip05";
|
|||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import DisplayName from "./DisplayName";
|
import DisplayName from "./DisplayName";
|
||||||
|
import Text from "Element/Text";
|
||||||
|
import FollowButton from "Element/User/FollowButton";
|
||||||
|
import { UserWebsiteLink } from "Element/User/UserWebsiteLink";
|
||||||
|
|
||||||
export interface ProfileImageProps {
|
export interface ProfileImageProps {
|
||||||
pubkey: HexKey;
|
pubkey: HexKey;
|
||||||
@ -27,6 +32,7 @@ export interface ProfileImageProps {
|
|||||||
imageOverlay?: ReactNode;
|
imageOverlay?: ReactNode;
|
||||||
showFollowingMark?: boolean;
|
showFollowingMark?: boolean;
|
||||||
icons?: ReactNode;
|
icons?: ReactNode;
|
||||||
|
showProfileCard?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ProfileImage({
|
export default function ProfileImage({
|
||||||
@ -44,11 +50,29 @@ export default function ProfileImage({
|
|||||||
onClick,
|
onClick,
|
||||||
showFollowingMark = true,
|
showFollowingMark = true,
|
||||||
icons,
|
icons,
|
||||||
|
showProfileCard,
|
||||||
}: ProfileImageProps) {
|
}: ProfileImageProps) {
|
||||||
const user = useUserProfile(profile ? "" : pubkey) ?? profile;
|
const user = useUserProfile(profile ? "" : pubkey) ?? profile;
|
||||||
const nip05 = defaultNip ? defaultNip : user?.nip05;
|
const nip05 = defaultNip ? defaultNip : user?.nip05;
|
||||||
const { follows } = useLogin();
|
const { follows } = useLogin();
|
||||||
const doesFollow = follows.item.includes(pubkey);
|
const doesFollow = follows.item.includes(pubkey);
|
||||||
|
const [ref, hovering] = useHover<HTMLDivElement>();
|
||||||
|
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
||||||
|
const [t, setT] = useState<ReturnType<typeof setTimeout>>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hovering) {
|
||||||
|
const tn = setTimeout(() => {
|
||||||
|
setShowProfileMenu(true);
|
||||||
|
}, 1000);
|
||||||
|
setT(tn);
|
||||||
|
} else {
|
||||||
|
if (t) {
|
||||||
|
clearTimeout(t);
|
||||||
|
setT(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [hovering]);
|
||||||
|
|
||||||
function handleClick(e: React.MouseEvent) {
|
function handleClick(e: React.MouseEvent) {
|
||||||
if (link === "") {
|
if (link === "") {
|
||||||
@ -60,7 +84,7 @@ export default function ProfileImage({
|
|||||||
function inner() {
|
function inner() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="avatar-wrapper">
|
<div className="avatar-wrapper" ref={ref}>
|
||||||
<Avatar
|
<Avatar
|
||||||
pubkey={pubkey}
|
pubkey={pubkey}
|
||||||
user={user}
|
user={user}
|
||||||
@ -93,20 +117,61 @@ export default function ProfileImage({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function profileCard() {
|
||||||
|
if (showProfileCard ?? true) {
|
||||||
|
return (
|
||||||
|
<ControlledMenu
|
||||||
|
state={showProfileMenu ? "open" : "closed"}
|
||||||
|
anchorRef={ref}
|
||||||
|
menuClassName="profile-card"
|
||||||
|
onClose={() => setShowProfileMenu(false)}>
|
||||||
|
<div className="flex-column g8">
|
||||||
|
<div className="flex f-space">
|
||||||
|
<ProfileImage pubkey={""} profile={user} showProfileCard={false} link="" />
|
||||||
|
<div className="flex g8">
|
||||||
|
{/*<button type="button">
|
||||||
|
<FormattedMessage defaultMessage="Stalk" />
|
||||||
|
</button>*/}
|
||||||
|
<FollowButton pubkey={pubkey} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Text
|
||||||
|
id={`profile-card-${pubkey}`}
|
||||||
|
content={user?.about ?? ""}
|
||||||
|
creator={pubkey}
|
||||||
|
tags={[]}
|
||||||
|
disableMedia={true}
|
||||||
|
disableLinkPreview={true}
|
||||||
|
truncate={250}
|
||||||
|
/>
|
||||||
|
<UserWebsiteLink user={user} />
|
||||||
|
</div>
|
||||||
|
</ControlledMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (link === "") {
|
if (link === "") {
|
||||||
return (
|
return (
|
||||||
<div className={`pfp${className ? ` ${className}` : ""}`} onClick={handleClick}>
|
<>
|
||||||
{inner()}
|
<div className={`pfp${className ? ` ${className}` : ""}`} onClick={handleClick}>
|
||||||
</div>
|
{inner()}
|
||||||
|
</div>
|
||||||
|
{profileCard()}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Link
|
<>
|
||||||
className={`pfp${className ? ` ${className}` : ""}`}
|
<Link
|
||||||
to={link === undefined ? profileLink(pubkey) : link}
|
className={`pfp${className ? ` ${className}` : ""}`}
|
||||||
onClick={handleClick}>
|
to={link === undefined ? profileLink(pubkey) : link}
|
||||||
{inner()}
|
onClick={handleClick}>
|
||||||
</Link>
|
{inner()}
|
||||||
|
</Link>
|
||||||
|
{profileCard()}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,3 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-preview button {
|
|
||||||
min-width: 98px;
|
|
||||||
}
|
|
||||||
|
@ -12,6 +12,7 @@ export interface ProfilePreviewProps {
|
|||||||
options?: {
|
options?: {
|
||||||
about?: boolean;
|
about?: boolean;
|
||||||
linkToProfile?: boolean;
|
linkToProfile?: boolean;
|
||||||
|
profileCards?: boolean;
|
||||||
};
|
};
|
||||||
profile?: UserMetadata;
|
profile?: UserMetadata;
|
||||||
actions?: ReactNode;
|
actions?: ReactNode;
|
||||||
@ -45,6 +46,7 @@ export default function ProfilePreview(props: ProfilePreviewProps) {
|
|||||||
profile={props.profile}
|
profile={props.profile}
|
||||||
link={options.linkToProfile ?? true ? undefined : ""}
|
link={options.linkToProfile ?? true ? undefined : ""}
|
||||||
subHeader={options.about ? <div className="about">{user?.about}</div> : undefined}
|
subHeader={options.about ? <div className="about">{user?.about}</div> : undefined}
|
||||||
|
showProfileCard={options.profileCards}
|
||||||
/>
|
/>
|
||||||
{props.actions ?? (
|
{props.actions ?? (
|
||||||
<div className="follow-button-container">
|
<div className="follow-button-container">
|
||||||
|
13
packages/app/src/Element/User/UserWebsiteLink.css
Normal file
13
packages/app/src/Element/User/UserWebsiteLink.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.user-profile-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-profile-link a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-profile-link a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
29
packages/app/src/Element/User/UserWebsiteLink.tsx
Normal file
29
packages/app/src/Element/User/UserWebsiteLink.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import "./UserWebsiteLink.css";
|
||||||
|
import { MetadataCache, UserMetadata } from "@snort/system";
|
||||||
|
import Icon from "Icons/Icon";
|
||||||
|
|
||||||
|
export function UserWebsiteLink({ user }: { user?: MetadataCache | UserMetadata }) {
|
||||||
|
const website_url =
|
||||||
|
user?.website && !user.website.startsWith("http") ? "https://" + user.website : user?.website || "";
|
||||||
|
|
||||||
|
function tryFormatWebsite(url: string) {
|
||||||
|
try {
|
||||||
|
const u = new URL(url);
|
||||||
|
return `${u.hostname}${u.pathname !== "/" ? u.pathname : ""}`;
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user?.website) {
|
||||||
|
return (
|
||||||
|
<div className="user-profile-link f-ellipsis">
|
||||||
|
<Icon name="link-02" size={16} />
|
||||||
|
<a href={website_url} target="_blank" rel="noreferrer">
|
||||||
|
{tryFormatWebsite(user.website)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -3,11 +3,14 @@ import { ParsedFragment, transformText } from "@snort/system";
|
|||||||
const TextCache = new Map<string, Array<ParsedFragment>>();
|
const TextCache = new Map<string, Array<ParsedFragment>>();
|
||||||
|
|
||||||
export function transformTextCached(id: string, content: string, tags: Array<Array<string>>) {
|
export function transformTextCached(id: string, content: string, tags: Array<Array<string>>) {
|
||||||
const cached = TextCache.get(id);
|
if (content.length > 0) {
|
||||||
if (cached) return cached;
|
const cached = TextCache.get(id);
|
||||||
const newCache = transformText(content, tags);
|
if (cached) return cached;
|
||||||
TextCache.set(id, newCache);
|
const newCache = transformText(content, tags);
|
||||||
return newCache;
|
TextCache.set(id, newCache);
|
||||||
|
return newCache;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTextTransformer(id: string, content: string, tags: Array<Array<string>>) {
|
export function useTextTransformer(id: string, content: string, tags: Array<Array<string>>) {
|
||||||
|
@ -138,14 +138,6 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile .website a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile .website a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile .link svg {
|
.profile .link svg {
|
||||||
color: var(--highlight);
|
color: var(--highlight);
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ import { useStatusFeed } from "Feed/StatusFeed";
|
|||||||
|
|
||||||
import messages from "./messages";
|
import messages from "./messages";
|
||||||
import { SpotlightMediaModal } from "Element/Deck/SpotlightMedia";
|
import { SpotlightMediaModal } from "Element/Deck/SpotlightMedia";
|
||||||
|
import { UserWebsiteLink } from "Element/User/UserWebsiteLink";
|
||||||
|
|
||||||
const NOTES = 0;
|
const NOTES = 0;
|
||||||
const REACTIONS = 1;
|
const REACTIONS = 1;
|
||||||
@ -135,8 +136,6 @@ export default function ProfilePage() {
|
|||||||
const showBadges = login.preferences.showBadges ?? false;
|
const showBadges = login.preferences.showBadges ?? false;
|
||||||
const showStatus = login.preferences.showStatus ?? true;
|
const showStatus = login.preferences.showStatus ?? true;
|
||||||
|
|
||||||
const website_url =
|
|
||||||
user?.website && !user.website.startsWith("http") ? "https://" + user.website : user?.website || "";
|
|
||||||
// feeds
|
// feeds
|
||||||
const { blocked } = useModeration();
|
const { blocked } = useModeration();
|
||||||
const pinned = usePinnedFeed(id);
|
const pinned = usePinnedFeed(id);
|
||||||
@ -295,28 +294,10 @@ export default function ProfilePage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryFormatWebsite(url: string) {
|
|
||||||
try {
|
|
||||||
const u = new URL(url);
|
|
||||||
return `${u.hostname}${u.pathname !== "/" ? u.pathname : ""}`;
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
function links() {
|
function links() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{user?.website && (
|
<UserWebsiteLink user={user} />
|
||||||
<div className="link website f-ellipsis">
|
|
||||||
<Icon name="link-02" size={16} />
|
|
||||||
<a href={website_url} target="_blank" rel="noreferrer">
|
|
||||||
{tryFormatWebsite(user.website)}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{lnurl && (
|
{lnurl && (
|
||||||
<div className="link lnurl f-ellipsis" onClick={() => setShowLnQr(true)}>
|
<div className="link lnurl f-ellipsis" onClick={() => setShowLnQr(true)}>
|
||||||
<Icon name="zapCircle" size={16} />
|
<Icon name="zapCircle" size={16} />
|
||||||
@ -433,7 +414,7 @@ export default function ProfilePage() {
|
|||||||
<Avatar pubkey={id ?? ""} user={user} onClick={() => setModalImage(user?.picture || "")} className="pointer" />
|
<Avatar pubkey={id ?? ""} user={user} onClick={() => setModalImage(user?.picture || "")} className="pointer" />
|
||||||
<div className="profile-actions">
|
<div className="profile-actions">
|
||||||
{renderIcons()}
|
{renderIcons()}
|
||||||
{!isMe && id && <FollowButton className="primary" pubkey={id} />}
|
{!isMe && id && <FollowButton pubkey={id} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -113,6 +113,21 @@ code {
|
|||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
line-height: 1.3em;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.ext {
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: initial;
|
||||||
|
}
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
@ -498,21 +513,6 @@ input:disabled {
|
|||||||
max-width: -moz-available;
|
max-width: -moz-available;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
line-height: 1.3em;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.ext {
|
|
||||||
word-break: break-all;
|
|
||||||
white-space: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.form {
|
div.form {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-flow: row;
|
grid-auto-flow: row;
|
||||||
|
@ -233,7 +233,7 @@ export function transformText(body: string, tags: Array<Array<string>>) {
|
|||||||
fragments = fragments
|
fragments = fragments
|
||||||
.map(a => {
|
.map(a => {
|
||||||
if (typeof a === "string") {
|
if (typeof a === "string") {
|
||||||
if (a.trim().length > 0) {
|
if (a.length > 0) {
|
||||||
return { type: "text", content: a } as ParsedFragment;
|
return { type: "text", content: a } as ParsedFragment;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
11
yarn.lock
11
yarn.lock
@ -2714,6 +2714,7 @@ __metadata:
|
|||||||
"@types/webtorrent": ^0.109.3
|
"@types/webtorrent": ^0.109.3
|
||||||
"@typescript-eslint/eslint-plugin": ^6.1.0
|
"@typescript-eslint/eslint-plugin": ^6.1.0
|
||||||
"@typescript-eslint/parser": ^6.1.0
|
"@typescript-eslint/parser": ^6.1.0
|
||||||
|
"@uidotdev/usehooks": ^2.3.1
|
||||||
"@void-cat/api": ^1.0.4
|
"@void-cat/api": ^1.0.4
|
||||||
"@webbtc/webln-types": ^1.0.10
|
"@webbtc/webln-types": ^1.0.10
|
||||||
"@webpack-cli/generators": ^3.0.4
|
"@webpack-cli/generators": ^3.0.4
|
||||||
@ -3852,6 +3853,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@uidotdev/usehooks@npm:^2.3.1":
|
||||||
|
version: 2.3.1
|
||||||
|
resolution: "@uidotdev/usehooks@npm:2.3.1"
|
||||||
|
peerDependencies:
|
||||||
|
react: ">=18.0.0"
|
||||||
|
react-dom: ">=18.0.0"
|
||||||
|
checksum: a1339b91bdb4176f59fc2dd8273065fccacb17749b7022879982ff874bda8e4e54a3f8d74f126e6224164fb2ad422f1cc40dac8705467960df525b207fcd3a79
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@void-cat/api@npm:^1.0.4":
|
"@void-cat/api@npm:^1.0.4":
|
||||||
version: 1.0.7
|
version: 1.0.7
|
||||||
resolution: "@void-cat/api@npm:1.0.7"
|
resolution: "@void-cat/api@npm:1.0.7"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user