feat: Edit Profile for project founders (mirrors angor-profile web app)#778
Conversation
- Add relay service methods: FetchProfileMetadataAsync, FetchAppSpecificDataAsync, PublishAppSpecificDataAsync - Add UpdateProjectProfile SDK operation (derives project Nostr key, publishes kind 0 + kind 30078 events) - Add UpdateProjectProfile to IProjectAppService and ProjectAppService - Create EditProfileViewModel with tabs: Profile, Project, FAQ, Members, Media, Relays - Create EditProfileView.axaml with multi-tab UI matching angor-profile web app structure - Create EditProfileView.axaml.cs with tab switching and save/back actions - Wire PART_EditButton in MyProjectsView to open edit profile panel - Add SelectedEditProject state to MyProjectsViewModel - Register EditProfileViewModel factory in CompositionRoot Agent-Logs-Url: https://github.com/block-core/angor/sessions/67d28a2c-7d1b-4ef3-a0ce-7888423dc938 Co-authored-by: dangershony <7487930+dangershony@users.noreply.github.com>
…ind IProjectAppService - Add FetchProjectProfileData SDK operation that fetches profile + all app-specific data - Add FaqItem and MediaItem shared data types in FetchProjectProfileData.cs - Update UpdateProjectProfile to resolve project via IProjectService internally (not via ProjectSeedDto) - Add FetchProjectProfileData and UpdateProjectProfile to IProjectAppService/ProjectAppService - Fix EditProfileViewModel to use only IProjectAppService (no direct IRelayService/IProjectService) - Fix camelCase field naming convention in FaqItemViewModel, MediaItemViewModel, EditProfileViewModel - Update CompositionRoot factory accordingly Agent-Logs-Url: https://github.com/block-core/angor/sessions/67d28a2c-7d1b-4ef3-a0ce-7888423dc938 Co-authored-by: dangershony <7487930+dangershony@users.noreply.github.com>
|
|
||
| // Publish app-specific data events (kind 30078) | ||
| if (request.ProjectContent != null) | ||
| await relayService.PublishAppSpecificDataAsync("angor:project", request.ProjectContent, nostrKeyHex, NoopCallback); |
There was a problem hiding this comment.
@copilot why do we publish to PublishAppSpecificDataAsync on each content are they not stored all in one event?
There was a problem hiding this comment.
Each content type is intentionally a separate kind-30078 (NIP-78 AppSpecificData) event, differentiated by its d tag (angor:project, angor:faq, angor:members, angor:media). This is NIP-33 "parameterized replaceable events" — each unique (pubkey, kind, d) triple identifies one replaceable event, so a founder can update just their FAQ without republishing the project description, members list, etc.
The angor-profile web app does exactly the same: saveProjectContent, saveFaqContent, saveMediaContent, and saveMembers are separate functions each publishing their own kind-30078 event with the appropriate d tag (see createEventsFromData in relay.service.ts which constructs one event per section and then publishes them all in parallel via Promise.all).
Implements the "Edit" button in My Projects → project card. Opens a full edit profile page matching the tab structure and functionality of profile.angor.io.
Relay Service (
Angor.Shared)FetchProfileMetadataAsync— async task-based wrapper for kind-0 profile lookupFetchAppSpecificDataAsync— fetches kind-30078 (NIP-78) events bydtag (client-side filtered)PublishAppSpecificDataAsync— publishes kind-30078 events with a givendtagSDK Operations (
Angor.Sdk)FetchProjectProfileData— resolves project → fetches kind-0 metadata +angor:project/angor:faq/angor:members/angor:mediain parallel; exposesFaqItem/MediaItemshared typesUpdateProjectProfile— resolves founder key viaIProjectService, derives project Nostr private key from wallet seed, publishes kind-0 + kind-30078 eventsBoth operations are exposed through
IProjectAppService/ProjectAppService.App Layer (
App)EditProfileViewModel— reactive VM injecting onlyIProjectAppService(no direct SDK-layer service access);[Reactive]fields follow camelCase-without-underscore conventionEditProfileView.axaml— 6-tab UI: Profile | Project | FAQ | Members | Media | RelaysEditProfileView.axaml.cs— tab switching, back navigation, bubbled remove-item button handlingMyProjectsViewModel—OpenEditProfile()/CloseEditProfile()+SelectedEditProjectreactive stateMyProjectsView.axaml.cs—PART_EditButtonnow callsvm.OpenEditProfile(project), visibility subscription mirrors the manage-project patternCompositionRoot— registersFunc<MyProjectItemViewModel, EditProfileViewModel>factoryData flow