feat: profile link to short url
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
import { hexToBech32 } from "@snort/shared";
|
import { profileLink } from "@/utils";
|
||||||
|
|
||||||
interface MentionProps {
|
interface MentionProps {
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
@ -9,9 +9,8 @@ interface MentionProps {
|
|||||||
|
|
||||||
export function Mention({ pubkey }: MentionProps) {
|
export function Mention({ pubkey }: MentionProps) {
|
||||||
const user = useUserProfile(pubkey);
|
const user = useUserProfile(pubkey);
|
||||||
const npub = hexToBech32("npub", pubkey);
|
|
||||||
return (
|
return (
|
||||||
<Link to={`/p/${npub}`} className="text-primary">
|
<Link to={profileLink(user, pubkey)} className="text-primary">
|
||||||
{user?.name || pubkey}
|
{user?.name || pubkey}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
@ -6,6 +6,7 @@ import { hexToBech32 } from "@snort/shared";
|
|||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
import { Avatar } from "./avatar";
|
import { Avatar } from "./avatar";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import { profileLink } from "@/utils";
|
||||||
|
|
||||||
export interface ProfileOptions {
|
export interface ProfileOptions {
|
||||||
showName?: boolean;
|
showName?: boolean;
|
||||||
@ -64,7 +65,7 @@ export function Profile({
|
|||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Link to={`/p/${hexToBech32("npub", pubkey)}`} className={cls} ref={ref}>
|
<Link to={profileLink(pLoaded, pubkey)} className={cls} ref={ref}>
|
||||||
{content}
|
{content}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,6 @@ import { Link, Outlet, useLocation, useNavigate, useParams } from "react-router-
|
|||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { Menu, MenuItem } from "@szhsin/react-menu";
|
import { Menu, MenuItem } from "@szhsin/react-menu";
|
||||||
import { hexToBech32 } from "@snort/shared";
|
|
||||||
|
|
||||||
import { Icon } from "@/element/icon";
|
import { Icon } from "@/element/icon";
|
||||||
import { useLogin, useLoginEvents } from "@/hooks/login";
|
import { useLogin, useLoginEvents } from "@/hooks/login";
|
||||||
@ -14,7 +13,7 @@ import { LoginSignup } from "@/element/login-signup";
|
|||||||
import { Login } from "@/login";
|
import { Login } from "@/login";
|
||||||
import { useLang } from "@/hooks/lang";
|
import { useLang } from "@/hooks/lang";
|
||||||
import { AllLocales } from "@/intl";
|
import { AllLocales } from "@/intl";
|
||||||
import { trackEvent } from "@/utils";
|
import { profileLink, trackEvent } from "@/utils";
|
||||||
import { BorderButton, DefaultButton } from "@/element/buttons";
|
import { BorderButton, DefaultButton } from "@/element/buttons";
|
||||||
import Modal from "@/element/modal";
|
import Modal from "@/element/modal";
|
||||||
import Logo from "@/element/logo";
|
import Logo from "@/element/logo";
|
||||||
@ -89,7 +88,7 @@ export function LayoutPage() {
|
|||||||
}
|
}
|
||||||
align="end"
|
align="end"
|
||||||
gap={5}>
|
gap={5}>
|
||||||
<MenuItem onClick={() => navigate(`/p/${hexToBech32("npub", login.pubkey)}`)}>
|
<MenuItem onClick={() => navigate(profileLink(undefined, login.pubkey))}>
|
||||||
<Icon name="user" size={24} />
|
<Icon name="user" size={24} />
|
||||||
<FormattedMessage defaultMessage="Profile" id="itPgxd" />
|
<FormattedMessage defaultMessage="Profile" id="itPgxd" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./profile-page.css";
|
import "./profile-page.css";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||||
import { CachedMetadata, NostrEvent, NostrLink, NostrPrefix, TaggedNostrEvent, parseNostrLink } from "@snort/system";
|
import { CachedMetadata, NostrEvent, NostrLink, TaggedNostrEvent, parseNostrLink } from "@snort/system";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
import { unwrap } from "@snort/shared";
|
import { unwrap } from "@snort/shared";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
@ -13,7 +13,7 @@ import { FollowButton } from "@/element/follow-button";
|
|||||||
import { MuteButton } from "@/element/mute-button";
|
import { MuteButton } from "@/element/mute-button";
|
||||||
import { useProfile } from "@/hooks/profile";
|
import { useProfile } from "@/hooks/profile";
|
||||||
import { Text } from "@/element/text";
|
import { Text } from "@/element/text";
|
||||||
import { findTag } from "@/utils";
|
import { findTag, profileLink } from "@/utils";
|
||||||
import { StatePill } from "@/element/state-pill";
|
import { StatePill } from "@/element/state-pill";
|
||||||
import { Avatar } from "@/element/avatar";
|
import { Avatar } from "@/element/avatar";
|
||||||
import { StreamState } from "@/const";
|
import { StreamState } from "@/const";
|
||||||
@ -198,7 +198,7 @@ function ProfileClip({ ev }: { ev: NostrEvent }) {
|
|||||||
values={{
|
values={{
|
||||||
name: (
|
name: (
|
||||||
<Link
|
<Link
|
||||||
to={`/p/${new NostrLink(NostrPrefix.PublicKey, ev.pubkey).encode()}`}
|
to={profileLink(profile, ev.pubkey)}
|
||||||
className="font-medium text-primary">
|
className="font-medium text-primary">
|
||||||
{getName(ev.pubkey, profile)}
|
{getName(ev.pubkey, profile)}
|
||||||
</Link>
|
</Link>
|
||||||
|
13
src/utils.ts
13
src/utils.ts
@ -1,9 +1,10 @@
|
|||||||
import { NostrEvent, NostrLink, TaggedNostrEvent } from "@snort/system";
|
import { CachedMetadata, NostrEvent, NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||||
|
|
||||||
import type { Tags } from "@/types";
|
import type { Tags } from "@/types";
|
||||||
import { LIVE_STREAM, StreamState } from "@/const";
|
import { LIVE_STREAM, StreamState } from "@/const";
|
||||||
import { GameInfo } from "./service/game-database";
|
import { GameInfo } from "./service/game-database";
|
||||||
import { AllCategories } from "./pages/category";
|
import { AllCategories } from "./pages/category";
|
||||||
|
import { hexToBech32 } from "@snort/shared";
|
||||||
|
|
||||||
export function toAddress(e: NostrEvent): string {
|
export function toAddress(e: NostrEvent): string {
|
||||||
if (e.kind && e.kind >= 30000 && e.kind <= 40000) {
|
if (e.kind && e.kind >= 30000 && e.kind <= 40000) {
|
||||||
@ -34,6 +35,14 @@ export function getHost(ev?: NostrEvent) {
|
|||||||
return ev?.tags.find(a => a[0] === "p" && a[3] === "host")?.[1] ?? ev?.pubkey ?? "";
|
return ev?.tags.find(a => a[0] === "p" && a[3] === "host")?.[1] ?? ev?.pubkey ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function profileLink(meta: CachedMetadata | undefined, pubkey: string) {
|
||||||
|
if (meta && meta.nip05 && meta.nip05.endsWith("@zap.stream") && meta.isNostrAddressValid) {
|
||||||
|
const [name,] = meta.nip05.split("@");
|
||||||
|
return `/p/${name}`;
|
||||||
|
}
|
||||||
|
return `/p/${hexToBech32("npub", pubkey)}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function openFile(): Promise<File | undefined> {
|
export function openFile(): Promise<File | undefined> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const elm = document.createElement("input");
|
const elm = document.createElement("input");
|
||||||
@ -75,7 +84,7 @@ export function uniqBy<T>(vals: Array<T>, key: (x: T) => string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getPlaceholder(id: string) {
|
export function getPlaceholder(id: string) {
|
||||||
return `https://robohash.v0l.io/${id}.png`;
|
return `https://nostr.api.v0l.io/api/v1/avatar/robots/${id}.webp`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function debounce(time: number, fn: () => void): () => void {
|
export function debounce(time: number, fn: () => void): () => void {
|
||||||
|
Reference in New Issue
Block a user