Skip to content

feat: add profile and contacts fetching from pubky#824

Open
ben-kaufman wants to merge 26 commits intomasterfrom
feat/pubky-profile
Open

feat: add profile and contacts fetching from pubky#824
ben-kaufman wants to merge 26 commits intomasterfrom
feat/pubky-profile

Conversation

@ben-kaufman
Copy link
Contributor

@ben-kaufman ben-kaufman commented Mar 5, 2026

Integrates Pubky decentralized identity into Bitkit, adding profile authentication, a contacts screen.

What's included

Pubky profile

  • Authentication flow via Pubky Ring deep links (pubkyauth://) with relay-based session exchange, session persistence in Keychain, and automatic session restoration on launch
  • Profile page displaying name, bio, links, and a QR code with profile picture overlaid
  • Home screen integration showing the authenticated user's name and avatar in the header
  • PubkyService — service layer wrapping paykit (session management) and bitkit-core (auth relay, file fetching, profile/contacts)
  • PubkyRepo — manages auth state, session lifecycle, profile, and contacts data with all I/O offloaded to background threads
  • PubkyImage component for loading pubky:// URIs with two-tier (memory + disk) caching
  • PubkyStore DataStore caches profile name and image URI for instant display before full profile loads
  • Suggestion card auto-dismiss when user is already authenticated

Pubky contacts

  • Contacts intro screen with navigation to profile auth if not yet signed in
  • Contacts list with alphabetical grouping and search
  • Contact detail screen showing name, bio, links, and copy/share actions
  • Contacts fetched concurrently on login and session restore; failed fetches fall back to a placeholder

@claude

This comment has been minimized.

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Found 2 CLAUDE.md compliance issues.

@jvsena42
Copy link
Member

jvsena42 commented Mar 9, 2026

Starting review

@ovitrif ovitrif added this to the 2.2.0 milestone Mar 9, 2026
@ben-kaufman ben-kaufman requested review from jvsena42 and ovitrif March 11, 2026 11:01
Copy link
Collaborator

@ovitrif ovitrif left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submitted couple more pending comments and resolved the ones where code changes applied. The goal is to reach a state where agent can be asked to scan through non-resolved review comments and address those in a new pass.

@claude

This comment has been minimized.

@ben-kaufman ben-kaufman changed the title feat: add profile fetching from pubky feat: add profile and contacts fetching from pubky Mar 11, 2026
@ben-kaufman ben-kaufman requested a review from ovitrif March 12, 2026 00:40
@jvsena42
Copy link
Member

resuming review...

Copy link
Collaborator

@ovitrif ovitrif left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall great progress, this is getting closer to being ready.

For some reason my review comments of yesterday night didn't land to your side, likely it was a PEBKAC 🤦🏻 and I forgot to publish review.

There were more nits, but it was lacking the review of contacts review and retest of the new features stack. So this is more comprehensive anyways.

The biggest 2 issues are shown in this video:
https://github.com/user-attachments/assets/b582a575-39fe-4ddf-9e99-868de5a099f2


1. Crash when sharing pk from profile or contact details

1️⃣ 🔴 Basically 2 crash bugs, because shareText is used in viewModels instead of in view layer, the fix is described in comments on the lines which cause the crash. Should be fixed in profile details and contacts details.


2. Inconsistency and missing details on self (my) contact details

2️⃣ 🟠 The other bug (supposedly) is the contact details page for self, seems to be missing the right profile picture. Interesting that in iOS we show the profile page when tapping on the contact list item for self.


3. Inconsistency in sorting irregular profile names

3️⃣ 🔴 Then I noticed a difference between iOS and Android in sorting. iOS seems to do this better: Profiles with non-alphabet starting character are grouped at the top under section header #.
Android shows those at the end of the list, in a separate section for each glyph first char.

contacts.mp4

Comment on lines +25 to +26
suspend fun initialize() =
ServiceQueue.CORE.background { paykitInitialize() }

This comment was marked as resolved.

ovitrif

This comment was marked as resolved.

@piotr-iohk
Copy link
Collaborator

Few issues/comments found during testing:

  1. Hardcoded production domains — no staging support

The capabilities in PubkyService are hardcoded to pubky.app / paykit.app:

/pub/paykit.app/v0/:rw,/pub/pubky.app/profile.json:rw,/pub/pubky.app/follows/:rw

For dev/tnet/E2E builds we should be using staging.pubky.app / staging.paykit.app instead — Pubky Ring already distinguishes between staging and production based on homeserver. This will be needed for E2E test automation.

  1. Unrecoverable "Unable to load profile" state

After hitting a profile load failure (possibly related to the staging mismatch above), the app gets stuck on an "Unable to load profile" screen with no way to disconnect/sign out. Resetting wallet and wiping app data from system settings do not clear it - only a full reinstall works. The session seems to persist somewhere that survives both of those operations (Keychain?).
(both probably apply to iOS as well, have not checked yet)

Screen_Recording_20260313_185242_Bitkit.Regtest.mp4
  1. App crash on tapping "share" icon on pubky profile - I think already mentioned by Ovi.
  2. After authorizing in Pubky Ring, the user stays in Ring instead of being redirected back to Bitkit. Would be great if Ring navigated back automatically after approval. Likely a Pubky Ring-side change.

@piotr-iohk
Copy link
Collaborator

Also My profile in Contacts does not show profile details as opposed to iOS (checked on latest revision 78d16f7), also already mentioned by Ovi:

Screen.Recording.2026-03-16.at.14.03.24.mov

Copy link
Collaborator

@ovitrif ovitrif left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

retested everything, looking much better but I still see these 2 issues:

own profile list glyph headers
Image Image

Network.BITCOIN ->
"/pub/paykit.app/v0/:rw,/pub/pubky.app/profile.json:rw,/pub/pubky.app/follows/:rw"
else ->
"/pub/staging.paykit.app/v0/:rw,/pub/staging.pubky.app/profile.json:rw,/pub/pubky.app/follows/:rw"
Copy link
Collaborator

@ovitrif ovitrif Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/pub/pubky.app still in last entry…

should last entry also use …staging… ?!

maybe best to turn this strings-in-when to a string template, where the computing by network only applies to the app's name.

contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
) {
CircularProgressIndicator(color = Colors.White32)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should start using GradientCircularProgressIndicator in all new implementation from now on, instead of CircularProgressIndicator

}

fun copyPublicKey() {
context.setClipboardText(publicKey, context.getString(R.string.profile__public_key))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aside: this was the other option I didn't share as I am still skeptical on it, but I guess it's ok; the thing to fix was the crash, this alternative fixes it too, at the expense of having to bubble up N callbacks just to lift the copy-to-clipboard action to the viewModel without breaking the principle of not passing viewmodel to inner composables.

Comment on lines +237 to +271
private fun ActionButton(
iconRes: Int? = null,
imageVector: ImageVector? = null,
onClick: () -> Unit,
enabled: Boolean = true,
) {
IconButton(
onClick = rememberDebouncedClick(onClick = onClick),
enabled = enabled,
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
.background(
Brush.verticalGradient(listOf(Colors.Gray5, Colors.Gray6)),
CircleShape,
)
.border(1.dp, Colors.White10, CircleShape)
) {
val tint = if (enabled) Colors.White else Colors.White32
when {
iconRes != null -> Icon(
painter = painterResource(iconRes),
contentDescription = null,
tint = tint,
modifier = Modifier.size(24.dp)
)
imageVector != null -> Icon(
imageVector = imageVector,
contentDescription = null,
tint = tint,
modifier = Modifier.size(24.dp)
)
}
}
}
Copy link
Collaborator

@ovitrif ovitrif Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it looks to me like these are the same implementations we have in contact detail page.

if this is 100% the same, we should extract to .ui.components, share and reuse.

Suggestion likely impacts:

  • ActionButton
  • ProfileLinkRow

@jvsena42
Copy link
Member

What is the best way to testing this? A Pubky Ring on staging?

@ovitrif
Copy link
Collaborator

ovitrif commented Mar 16, 2026

What is the best way to testing this? A Pubky Ring on staging?

Test in prod, yolo!

@piotr-iohk
Copy link
Collaborator

What is the best way to testing this? A Pubky Ring on staging?

Test in prod, yolo!

From what I see staging doesn't work still? Or maybe it is because of HTTP-auth required on staging 🤔 (IMO we should solve this and allow testing on staging):

Screen.Recording.2026-03-16.at.18.22.17.mov

@piotr-iohk
Copy link
Collaborator

What is the best way to testing this? A Pubky Ring on staging?

Test in prod, yolo!

From what I see staging doesn't work still? Or maybe it is because of HTTP-auth required on staging 🤔 (IMO we should solve this and allow testing on staging):

Screen.Recording.2026-03-16.at.18.22.17.mov

Actually started to work after I http-auth'ed to staging.pubky.app in browser... 🤷 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants