feat: add profile and contacts fetching from pubky#824
feat: add profile and contacts fetching from pubky#824ben-kaufman wants to merge 59 commits intomasterfrom
Conversation
app/src/main/java/to/bitkit/ui/screens/profile/ProfileViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/to/bitkit/ui/screens/profile/PubkyRingAuthViewModel.kt
Outdated
Show resolved
Hide resolved
This comment has been minimized.
This comment has been minimized.
app/src/main/java/to/bitkit/ui/screens/profile/ProfileViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/to/bitkit/ui/screens/profile/PubkyRingAuthViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/to/bitkit/ui/screens/profile/PubkyRingAuthViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/to/bitkit/ui/screens/profile/PubkyRingAuthViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/to/bitkit/ui/screens/profile/ProfileScreen.kt
Outdated
Show resolved
Hide resolved
This comment was marked as outdated.
This comment was marked as outdated.
I share the exact same understanding, aka. I confirm! |
There was a problem hiding this comment.
Review Pass 2: Test Profile Create & Edit + Contacts
| nit: Toast should not show here |
|---|
![]() |
| scene: right after profile is created |
Wipe Wallet doesn't fully clear profile
Profile still shows after wipe + create new wallet, and I can still go to profile page.
Should be wiped like all other data.
Unexpected red icon in bottom-right of profile placeholder

Edit Profile Sheets Unpolished
Recommending to use as template one of the simplest sheets we already polished in the app: ActivityAddTagSheet.
Link labels centered - should be left-aligned

Import Profile flow - new observations
1. Save new avatar picture error
Changing avatar pic never work for imported profile, it works for new profile every time I change it.
Error log excerpt:
22:04:02.304 E 2026-04-07 20:04:02.303 ERROR [EditProfileViewModel.kt:160] Failed to upload avatar [IllegalArgumentException='No secret key available'] - EditProfileViewModel
java.lang.IllegalArgumentException: No secret key available
at to.bitkit.repositories.PubkyRepo$uploadAvatar$4$1.invokeSuspend(PubkyRepo.kt:346)2. Contact import warn logs
Got spammed with these logs, most likely occurring for every contact after I already imported in the past testing sessions
2026-04-07 21:05:21.866 WARN [PubkyRepo.kt:571] Failed to import contact 'pubky6n3stw8ucfubm4gja1nr7t7ujk9xcukexkmwhhpy4trye5j5x9ty' [AppError='reason=Request failed: HTTP transport error: error sending request for url (https://_pubky.wzggsym1558jc1d5k6nd5o33rpj5wefypemb1na1niwytbnrm9qy/session)'] - PubkyRepo
to.bitkit.utils.AppError: reason=Request failed: HTTP transport error: error sending request for url (https://_pubky.wzggsym1558jc1d5k6nd5o33rpj5wefypemb1na1niwytbnrm9qy/session)The deeper stack trace seem to be pointing to FFI boilerplate code for PubkyException.AuthFailed.
It might be a non-issue for users if everything else works errorless.
Since it's a side effect or disconnect + & re-import via Pubky Ring.
Also worth mentioning contacts list is still populated after re-import.
3. Critical: Unexpected random disconnect (only observed 2 times yesterday)
After a long time waiting in the app, I get disconnected from my Pubky account in Bitkit (IIRC I traced a http relay gateway timeout error), and afterwards I can’t go to the profile page anymore, any navigation link points me to the onboarding screen in the flow.
| @file:Suppress("ImportOrdering") | ||
|
|
There was a problem hiding this comment.
Not needed, detekt succeeds without this.
| internal fun homegateUrlFor( | ||
| network: Network, | ||
| isLocalE2eBackend: Boolean, | ||
| e2eHomegateUrl: String, | ||
| ): String { | ||
| if (isLocalE2eBackend) { | ||
| return e2eHomegateUrl | ||
| } | ||
|
|
||
| return when (network) { | ||
| Network.BITCOIN -> "https://homegate.pubky.app" | ||
| else -> "https://homegate.staging.pubky.app" | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
nit: logic can be inlined; it's only called once, val homegateUrl is already computed property, no real need for this internal fun homegateUrlFor
| } | ||
| _contacts.update { loadedContacts } | ||
| }.onFailure { | ||
| Logger.error("Failed to load contacts", it, context = TAG) |
There was a problem hiding this comment.
This loadContacts() also fails when creating a profile because that account has no contacts yet. AFAIU it should not be logged as an error, maybe a warning at most.
Could perhaps use 404 + something else as discriminator for the logic -- to make sure it's this case and not 404 because of other reasons
log:
2026-04-09 14:37:31.301 ERROR [PubkyRepo.kt:471] Failed to load contacts [AppError='reason=Request failed: Server responded with an error: 404 Not Found - Directory Not Found'] - PubkyRepo (Fix with AI)
to.bitkit.utils.AppError: reason=Request failed: Server responded with an error: 404 Not Found - Directory Not Found
at to.bitkit.async.ServiceQueue$background$2.invokeSuspend(ServiceQueue.kt:42)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:98)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1154)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:652)
at java.lang.Thread.run(Thread.java:1564)
Caused by: com.synonym.bitkitcore.PubkyException$FetchFailed: reason=Request failed: Server responded with an error: 404 Not Found - Directory Not Found
Create Profile flow testing
1. Create profile - OK. 2. Edit / Delete profile
Screen.Recording.2026-04-10.at.10.54.33.mov
Screen.Recording.2026-04-10.at.11.05.22.mov
Screen.Recording.2026-04-10.at.11.07.26.mov3. Add contact
Screen.Recording.2026-04-10.at.11.34.19.mov
Screen.Recording.2026-04-10.at.12.00.12.mov4. Edit / Delete contact
|
|
Same pubky after delete is expected since we use deterministic derivation based on the seed. So that's the expected behavior there. |
|
No I think the warning makes sense, it deletes the profile, but not the key to it, maybe can remove the word "permenantly" |
Hm, it's also not like the profile is entirely deleted. From what I see only profile details (bio, links, tags) are deleted but the contacts are preserved. |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@piotr-iohk ah yes I missed that, fixed |
|
Additional observation (on 1e38929): 1.
Expected:
Actual:
2. It would be great if we could have same behavior on pubky validation when adding contacts (currently differs on iOS - validation on pubky input vs Android - validation after taping add, on Android also subsequent Retry button does not work). It would make things consistent + easier for e2e tests. 🙏 |
|
About 1, agree it makes sense, but backup and restore should ideally be done in a separate PR since this is already too large and they are a separate area of work. Not sure I understood what you refer to in 2, can you please explain? |
|
Sorry - I've added video on iOS for 2nd point: synonymdev/bitkit-ios#476 (comment) |






Integrates Pubky decentralized identity into Bitkit, allowing users to create a Bitkit-managed profile or connect via Pubky Ring authentication. Once connected, the user's profile name and avatar appear on the home screen header, a full profile page shows their bio, links, tags, and shareable QR code, and a contacts section lists people they follow on the Pubky network.
Description
pubkyauth://)onboarding
restoration fails
pubkyauth://URLs from QR scans and deep links, biometric auth before approval, three-state sheet (authorize → authorizing → success)PubkyService— service layer wrappingbitkit-core(auth relay, PKDNS file fetching, key derivation, homeserver operations) andpaykit(session management)PubkyRepo— manages auth state, session lifecycle, key derivation, profile creation/editing, contact CRUD, homeserver file operations, and auth approvalPubkyProfileDatabackward-compatible decoding —tagsfield defaults to empty array when missing from JSON (older profiles, other clients)bitkit-coreto v0.1.56 — adds key derivation, homeserver session operations, auth approval FFI functionsKey new files
services/PubkyService.ktrepositories/PubkyRepo.ktmodels/PubkyProfile.ktmodels/PubkyAuthRequest.ktui/components/CenteredProfileHeader.ktui/components/ProfileEditForm.ktui/components/AddLinkSheet.ktui/components/AddTagSheet.ktui/screens/profile/PubkyChoiceScreen.ktui/screens/profile/CreateProfileScreen.ktui/screens/profile/EditProfileScreen.ktui/screens/profile/PayContactsScreen.ktui/screens/profile/PubkyAuthApprovalSheet.ktui/screens/contacts/AddContactScreen.ktui/screens/contacts/EditContactScreen.ktui/screens/contacts/ContactImportOverviewScreen.ktui/screens/contacts/ContactImportSelectScreen.kttests/PubkyProfileDataTest.kttests/PubkyAuthRequestTest.ktQA Notes
pubkyauth://QR code → shows approval sheet with permissions → biometric → authorize → successpubkyauth://deep link → same approval flow