Add custom profile fields

This commit is contained in:
Alex Gleason 2024-11-20 08:56:38 -06:00
parent d2d29aef8f
commit 154056f8d6
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
3 changed files with 26 additions and 4 deletions

View file

@ -15,6 +15,7 @@ import { renderAccounts, renderEventAccounts, renderStatuses } from '@/views.ts'
import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts'; import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts';
import { renderRelationship } from '@/views/mastodon/relationships.ts'; import { renderRelationship } from '@/views/mastodon/relationships.ts';
import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts'; import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts';
import { metadataSchema } from '@/schemas/nostr.ts';
import { hydrateEvents } from '@/storages/hydrate.ts'; import { hydrateEvents } from '@/storages/hydrate.ts';
import { bech32ToPubkey } from '@/utils.ts'; import { bech32ToPubkey } from '@/utils.ts';
import { addTag, deleteTag, findReplyTag, getTagSet } from '@/utils/tags.ts'; import { addTag, deleteTag, findReplyTag, getTagSet } from '@/utils/tags.ts';
@ -269,6 +270,7 @@ const updateCredentialsSchema = z.object({
pleroma_settings_store: z.record(z.string(), z.unknown()).optional(), pleroma_settings_store: z.record(z.string(), z.unknown()).optional(),
lud16: z.string().email().or(z.literal('')).optional(), lud16: z.string().email().or(z.literal('')).optional(),
website: z.string().url().or(z.literal('')).optional(), website: z.string().url().or(z.literal('')).optional(),
fields_attributes: z.object({ name: z.string(), value: z.string() }).array().optional(),
}); });
const updateCredentialsController: AppController = async (c) => { const updateCredentialsController: AppController = async (c) => {
@ -284,11 +286,12 @@ const updateCredentialsController: AppController = async (c) => {
const event = await updateEvent( const event = await updateEvent(
{ kinds: [0], authors: [pubkey], limit: 1 }, { kinds: [0], authors: [pubkey], limit: 1 },
async (prev) => { async (prev) => {
const meta = n.json().pipe(n.metadata()).catch({}).parse(prev.content); const meta = n.json().pipe(metadataSchema).catch({}).parse(prev.content);
const { const {
avatar: avatarFile, avatar: avatarFile,
header: headerFile, header: headerFile,
display_name, display_name,
fields_attributes,
note, note,
nip05, nip05,
lud16, lud16,
@ -316,6 +319,10 @@ const updateCredentialsController: AppController = async (c) => {
if (lud16 === '') delete meta.lud16; if (lud16 === '') delete meta.lud16;
if (website === '') delete meta.website; if (website === '') delete meta.website;
if (fields_attributes) {
meta.fields = fields_attributes.map(({ name, value }) => [name, value]);
}
return { return {
kind: 0, kind: 0,
content: JSON.stringify(meta), content: JSON.stringify(meta),

View file

@ -9,6 +9,11 @@ const signedEventSchema = n.event()
.refine((event) => event.id === getEventHash(event), 'Event ID does not match hash') .refine((event) => event.id === getEventHash(event), 'Event ID does not match hash')
.refine(verifyEvent, 'Event signature is invalid'); .refine(verifyEvent, 'Event signature is invalid');
/** Kind 0 standardized fields extended with Ditto custom fields. */
const metadataSchema = n.metadata().and(z.object({
fields: z.tuple([z.string(), z.string()]).array().optional().catch(undefined),
}));
/** /**
* Stored in the kind 0 content. * Stored in the kind 0 content.
* https://developer.mozilla.org/en-US/docs/Web/Manifest/screenshots * https://developer.mozilla.org/en-US/docs/Web/Manifest/screenshots
@ -63,4 +68,12 @@ const emojiTagSchema = z.tuple([z.literal('emoji'), z.string(), z.string().url()
/** NIP-30 custom emoji tag. */ /** NIP-30 custom emoji tag. */
type EmojiTag = z.infer<typeof emojiTagSchema>; type EmojiTag = z.infer<typeof emojiTagSchema>;
export { type EmojiTag, emojiTagSchema, relayInfoDocSchema, screenshotsSchema, serverMetaSchema, signedEventSchema }; export {
type EmojiTag,
emojiTagSchema,
metadataSchema,
relayInfoDocSchema,
screenshotsSchema,
serverMetaSchema,
signedEventSchema,
};

View file

@ -4,6 +4,7 @@ import { nip19, UnsignedEvent } from 'nostr-tools';
import { Conf } from '@/config.ts'; import { Conf } from '@/config.ts';
import { MastodonAccount } from '@/entities/MastodonAccount.ts'; import { MastodonAccount } from '@/entities/MastodonAccount.ts';
import { type DittoEvent } from '@/interfaces/DittoEvent.ts'; import { type DittoEvent } from '@/interfaces/DittoEvent.ts';
import { metadataSchema } from '@/schemas/nostr.ts';
import { getLnurl } from '@/utils/lnurl.ts'; import { getLnurl } from '@/utils/lnurl.ts';
import { parseAndVerifyNip05 } from '@/utils/nip05.ts'; import { parseAndVerifyNip05 } from '@/utils/nip05.ts';
import { parseNoteContent } from '@/utils/note.ts'; import { parseNoteContent } from '@/utils/note.ts';
@ -42,7 +43,8 @@ async function renderAccount(
lud06, lud06,
lud16, lud16,
website, website,
} = n.json().pipe(n.metadata()).catch({}).parse(event.content); fields,
} = n.json().pipe(metadataSchema).catch({}).parse(event.content);
const npub = nip19.npubEncode(pubkey); const npub = nip19.npubEncode(pubkey);
const nprofile = nip19.nprofileEncode({ pubkey, relays: [Conf.relay] }); const nprofile = nip19.nprofileEncode({ pubkey, relays: [Conf.relay] });
@ -69,7 +71,7 @@ async function renderAccount(
discoverable: true, discoverable: true,
display_name: name ?? '', display_name: name ?? '',
emojis: renderEmojis(event), emojis: renderEmojis(event),
fields: [], fields: fields?.map(([name, value]) => ({ name, value, verified_at: null })) ?? [],
follow_requests_count: 0, follow_requests_count: 0,
followers_count: event.author_stats?.followers_count ?? 0, followers_count: event.author_stats?.followers_count ?? 0,
following_count: event.author_stats?.following_count ?? 0, following_count: event.author_stats?.following_count ?? 0,