feat: nip-38
This commit is contained in:
parent
a44a4ab69b
commit
950b0dbf4d
@ -30,6 +30,7 @@ Snort supports the following NIP's:
|
||||
- [x] NIP-30: Custom Emoji
|
||||
- [x] NIP-31: Alt tag for unknown events
|
||||
- [x] NIP-36: Sensitive Content
|
||||
- [x] NIP-38: User Statuses
|
||||
- [ ] NIP-40: Expiration Timestamp
|
||||
- [x] NIP-42: Authentication of clients to relays
|
||||
- [x] NIP-44: Versioned encryption
|
||||
|
27
packages/app/src/Feed/StatusFeed.ts
Normal file
27
packages/app/src/Feed/StatusFeed.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { EventKind, NoteCollection, RequestBuilder } from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { findTag } from "SnortUtils";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export function useStatusFeed(id?: string, leaveOpen = false) {
|
||||
const sub = useMemo(() => {
|
||||
if(!id) return null;
|
||||
|
||||
const rb = new RequestBuilder(`statud:${id}`);
|
||||
rb.withOptions({leaveOpen});
|
||||
rb.withFilter()
|
||||
.kinds([30315 as EventKind])
|
||||
.authors([id]);
|
||||
|
||||
return rb;
|
||||
}, [id]);
|
||||
|
||||
const status = useRequestBuilder(NoteCollection, sub);
|
||||
|
||||
const general = status.data?.find(a => findTag(a, "d") === "general");
|
||||
const music = status.data?.find(a => findTag(a, "d") === "music");
|
||||
|
||||
return {
|
||||
general, music
|
||||
}
|
||||
}
|
@ -76,6 +76,16 @@ export interface UserPreferences {
|
||||
* Collect usage metrics
|
||||
*/
|
||||
telemetry?: boolean;
|
||||
|
||||
/**
|
||||
* Show badges on profiles
|
||||
*/
|
||||
showBadges?: boolean;
|
||||
|
||||
/**
|
||||
* Show user status messages on profiles
|
||||
*/
|
||||
showStatus?: boolean;
|
||||
}
|
||||
|
||||
export const DefaultPreferences = {
|
||||
@ -93,4 +103,6 @@ export const DefaultPreferences = {
|
||||
defaultZapAmount: 50,
|
||||
autoZap: false,
|
||||
telemetry: true,
|
||||
showBadges: false,
|
||||
showStatus: true,
|
||||
} as UserPreferences;
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
import { LNURL } from "@snort/shared";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
|
||||
import { getReactions, unwrap } from "SnortUtils";
|
||||
import { findTag, getReactions, unwrap } from "SnortUtils";
|
||||
import { formatShort } from "Number";
|
||||
import Note from "Element/Note";
|
||||
import Bookmarks from "Element/Bookmarks";
|
||||
@ -55,6 +55,7 @@ import { EmailRegex } from "Const";
|
||||
import { getNip05PubKey } from "Pages/LoginPage";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { ZapTarget } from "Zapper";
|
||||
import { useStatusFeed } from "Feed/StatusFeed";
|
||||
|
||||
import messages from "./messages";
|
||||
|
||||
@ -114,7 +115,8 @@ export default function ProfilePage() {
|
||||
const navigate = useNavigate();
|
||||
const [id, setId] = useState<string>();
|
||||
const user = useUserProfile(id);
|
||||
const loginPubKey = useLogin().publicKey;
|
||||
const login = useLogin();
|
||||
const loginPubKey = login.publicKey;
|
||||
const isMe = loginPubKey === id;
|
||||
const [showLnQr, setShowLnQr] = useState<boolean>(false);
|
||||
const [showProfileQr, setShowProfileQr] = useState<boolean>(false);
|
||||
@ -128,14 +130,19 @@ export default function ProfilePage() {
|
||||
// ignored
|
||||
}
|
||||
})();
|
||||
const showBadges = login.preferences.showBadges ?? false;
|
||||
const showStatus = login.preferences.showStatus ?? true;
|
||||
|
||||
const website_url =
|
||||
user?.website && !user.website.startsWith("http") ? "https://" + user.website : user?.website || "";
|
||||
// feeds
|
||||
const { blocked } = useModeration();
|
||||
const pinned = usePinnedFeed(id);
|
||||
const muted = useMutedFeed(id);
|
||||
const badges = useProfileBadges(id);
|
||||
const badges = useProfileBadges(showBadges ? id : undefined);
|
||||
const follows = useFollowsFeed(id);
|
||||
const status = useStatusFeed(showStatus ? id : undefined, true);
|
||||
|
||||
// tabs
|
||||
const ProfileTab = {
|
||||
Notes: {
|
||||
@ -243,6 +250,23 @@ export default function ProfilePage() {
|
||||
setTab(ProfileTab.Notes);
|
||||
}, [params]);
|
||||
|
||||
function musicStatus() {
|
||||
if (!status.music) return;
|
||||
|
||||
const link = findTag(status.music, "r");
|
||||
const cover = findTag(status.music, "cover");
|
||||
const inner = () => {
|
||||
return <div className="flex g8">
|
||||
{cover && <ProxyImg src={cover} size={40} />}
|
||||
<small>🎵 {unwrap(status.music).content}</small>
|
||||
</div>
|
||||
}
|
||||
if (link) {
|
||||
return <a href={link} rel="noreferer" target="_blank" className="ext">{inner()}</a>;
|
||||
}
|
||||
return inner();
|
||||
}
|
||||
|
||||
function username() {
|
||||
return (
|
||||
<>
|
||||
@ -253,7 +277,10 @@ export default function ProfilePage() {
|
||||
</h2>
|
||||
{user?.nip05 && <Nip05 nip05={user.nip05} pubkey={user.pubkey} />}
|
||||
</div>
|
||||
<BadgeList badges={badges} />
|
||||
{showBadges && <BadgeList badges={badges} />}
|
||||
{showStatus && <>
|
||||
{musicStatus()}
|
||||
</>}
|
||||
<div className="link-section">
|
||||
<Copy text={npub} />
|
||||
{links()}
|
||||
@ -295,14 +322,14 @@ export default function ProfilePage() {
|
||||
targets={
|
||||
lnurl?.lnurl && id
|
||||
? [
|
||||
{
|
||||
type: "lnurl",
|
||||
value: lnurl?.lnurl,
|
||||
weight: 1,
|
||||
name: user?.display_name || user?.name,
|
||||
zap: { pubkey: id },
|
||||
} as ZapTarget,
|
||||
]
|
||||
{
|
||||
type: "lnurl",
|
||||
value: lnurl?.lnurl,
|
||||
weight: 1,
|
||||
name: user?.display_name || user?.name,
|
||||
zap: { pubkey: id },
|
||||
} as ZapTarget,
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
show={showLnQr}
|
||||
|
@ -207,6 +207,40 @@ const PreferencesPage = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex f-space w-max">
|
||||
<div className="flex-column g8">
|
||||
<h4>
|
||||
<FormattedMessage defaultMessage="Show Badges" />
|
||||
</h4>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="Show badges on profile pages" />
|
||||
</small>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={perf.showBadges ?? false}
|
||||
onChange={e => updatePreferences(login, { ...perf, showBadges: e.target.checked })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex f-space w-max">
|
||||
<div className="flex-column g8">
|
||||
<h4>
|
||||
<FormattedMessage defaultMessage="Show Status" />
|
||||
</h4>
|
||||
<small>
|
||||
<FormattedMessage defaultMessage="Show status messages on profile pages" />
|
||||
</small>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={perf.showStatus ?? true}
|
||||
onChange={e => updatePreferences(login, { ...perf, showStatus: e.target.checked })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex f-space w-max">
|
||||
<div className="flex-column g8">
|
||||
<h4>
|
||||
|
@ -26,6 +26,7 @@ enum EventKind {
|
||||
ProfileBadges = 30008, // NIP-58
|
||||
LongFormTextNote = 30023, // NIP-23
|
||||
LiveEvent = 30311, // NIP-102
|
||||
UserStatus = 30315, // NIP-38
|
||||
ZapstrTrack = 31337,
|
||||
SimpleChatMetadata = 39_000, // NIP-29
|
||||
ZapRequest = 9734, // NIP 57
|
||||
|
Loading…
x
Reference in New Issue
Block a user