Skip to main content

Unified Profile

With Unified Profile, profile taps inside the community open your own profile screens, and your app can open and read members' activity, profile, community data and comments.

Android ≥ 1.13.0iOS ≥ 1.13.0Flutter ≥ 1.13.0React Native ≥ 1.13.0Unity ≥ 1.13.0

Before you begin​

How it works​

By default, tapping a member's profile inside the community opens the SDK profile screens. With Unified Profile, the SDK hands each tap back to your app with the tapped member's client user id, so you open your own profile screen. The SDK never hands you an Octopus id in this callback.

Unified Profile also:

When Unified Profile is on, profile taps go here:

Tapped profileWhat opens
A member with a client user id, including the connected user's own name or avatar in a post or commentYour callback, with that client user id
A member without a client user id: a guest, or a profile created from the back officeThe SDK member activity screen. Your callback never gets a null id.
The floating button on the main feedThe SDK Activity screen

Activation​

Unified Profile turns on only when both of these are true:

  1. Your community exposes client user ids. The Octopus team turns this on for each community: ask your Octopus contact.
  2. Your app sets the profile tap callback: see Route profile taps.

Until both are true, the SDK keeps its own profile screens everywhere. You can ship the callback before the community setting is on, or the other way around.


Route profile taps to your own profile screen​

Set the callback, then open your own profile screen for the client user id it receives.

Parameters:

  • onNavigateToProfile: ((clientUserId: String) -> Unit)?: called when the user taps any profile in the community. Leave it null, the default, to keep the SDK profile screens. It exists on octopusComposables, OctopusHomeScreen, OctopusHomeContent, OctopusPostDetailsContent and OctopusGroupDetailsContent.
NavHost(navController = navController, startDestination = HomeRoute) {
octopusComposables(
navController = navController,
onNavigateToLogin = { navController.navigate(LoginRoute) },
onNavigateToProfileEdit = { fieldToEdit -> navController.navigate(EditProfileRoute) },
onNavigateToProfile = { clientUserId ->
navController.navigate(YourProfileRoute(userId = clientUserId))
},
) { backStackEntry, content ->
OctopusTheme {
content()
}
}
}

The connected user's Activity screen​

When Unified Profile is on, the connected user's avatar on the main feed's floating button becomes an Activity icon. It opens the Activity screen, with Notifications and Posts tabs and a Comments tab ≥ 1.14.0, and no profile header. Its overflow menu holds:

  • View my profile: calls your profile tap callback with the connected user's client user id. Shown for a signed-in member with a client user id.
  • Edit my profile: calls your profile edit callback, described below. Shown when that callback is set and View my profile is shown.
  • The community's legal links: community guidelines, privacy policy and terms of use.
  • Report inappropriate content.

Choose the initial profile or Activity tab​

Android ≥ 1.14.0iOS not availableFlutter not availableReact Native not availableUnity not available

With octopusComposables in your NavHost, navigate to a predefined OctopusDestination rather than a raw tab index. Each destination has its own tab order:

DestinationPostsCommentsNotifications
OctopusDestination.CurrentUserProfileSummary012
OctopusDestination.Activity210

Parameters, when you build a destination yourself:

  • selectedTabIndex: Int = 0: initial tab, with the mapping above.
  • Activity.userId: String? = null and Activity.clientUserId: String? = null: leave both unset for the connected user. For another member, use the member activity helpers: they ignore selectedTabIndex.
import com.octopuscommunity.sdk.ui.OctopusDestination

// The connected user's profile, on Comments
navController.navigate(OctopusDestination.CurrentUserProfileSummary.Comments)

// The connected user's Activity, on Posts
navController.navigate(OctopusDestination.Activity.Posts)
warning

≥ 1.14.0 The indices changed when the Comments tab arrived: 1 now opens Comments on both destinations. Profile Notifications moved from 1 to 2, and Activity Posts from 1 to 2. Prefer the predefined destinations: CurrentUserProfileSummary provides Posts, Comments and Notifications, Activity provides Posts and Notifications, and Activity(selectedTabIndex = 1) opens its Comments tab.

Open your profile editor from the Activity screen​

The Edit my profile menu item calls a profile edit callback with a null field: open your full profile editor.

Parameters:

  • onNavigateToProfileEdit: ((fieldToEdit: ProfileField?) -> Unit)?: the same parameter of octopusComposables and OctopusHomeContent described in Display the community.
octopusComposables(
navController = navController,
onNavigateToProfileEdit = { fieldToEdit ->
// null when opened from the Activity screen: open your full profile editor
navController.navigate(EditProfileRoute)
},
onNavigateToProfile = { clientUserId ->
navController.navigate(YourProfileRoute(userId = clientUserId))
},
)

Customize the Activity icon​

The Activity icon follows the rules of the other customizable icons: the SDK tints it, and 24×24 fits best. Without an override, the SDK shows its bell icon.

import com.octopuscommunity.sdk.ui.OctopusIconsDefaults
import com.octopuscommunity.sdk.ui.OctopusImagesDefaults
import com.octopuscommunity.sdk.ui.OctopusTheme

OctopusTheme(
images = OctopusImagesDefaults.images(
icons = OctopusIconsDefaults.icons(
activityButton = { painterResource(R.drawable.your_activity_icon) },
),
),
) {
OctopusHomeContent(navController = navController)
}

Open a member's activity screen directly​

From your own profile screen, open the SDK member activity screen. It shows the member's posts, plus a Comments tab ≥ 1.14.0 when your community shows members' comments on their profile, which is a back-office setting. Identify the member in one of two ways:

  • By their client user id. This needs the community to expose client user ids.
  • By their Octopus profile id, for example one returned by the community data API.

An id that does not resolve shows the empty state. An id that resolves to the connected user opens their own Activity screen.

Register octopusComposables() in your NavHost first: see Display the community.

// By client user id
navController.navigateToOctopusActivityByClientUserId(clientUserId = "YOUR_USER_ID")

// By Octopus profile id
navController.navigateToOctopusActivity(userId = octopusUserId)

Open a member's profile screen directly​

From your own UI, for example a member list, open a member's Octopus profile. It is a read-only view of their profile and posts, even when the id is the connected user's. To open the connected user's editable profile, use the entry points in Open a specific screen.

A client user id that does not resolve shows an error state. It never falls back to the connected user's profile.

Register octopusComposables() in your NavHost first: see Display the community.

// By Octopus profile id
navController.navigateToOctopusProfile(userId = octopusUserId)

// By client user id
navController.navigateToOctopusProfileByClientUserId(clientUserId = "YOUR_USER_ID")

Read a member's community data​

Show a member's community stats in your own profile screen. The SDK gives a read-only snapshot per member, fetched on demand or observed. It holds:

  • The member's Octopus profile id.
  • Their message count: posts, comments and replies. null when the community does not show it.
  • Their gamification standing, null when gamification is off: a level, starting at 0, and a score, always null for other members for now.

The client user id variants need the community to expose client user ids. The Octopus id variants always work. Observed values are null while the member is unknown, and update after each fetch.

The Octopus id variants are fetchCommunityData(userId) and communityDataFlow(userId).

// Fetch once (suspend). null when the member is unknown or the lookup fails.
val data: OctopusCommunityData? =
OctopusSDK.fetchCommunityDataByClientUserId(clientUserId = "YOUR_USER_ID")

// Observe. Emits null while the member is unknown or the lookup fails.
OctopusSDK.communityDataFlowByClientUserId(clientUserId = "YOUR_USER_ID")
.collect { data ->
val messageCount: Int? = data?.messageCount
val level: Int? = data?.gamification?.level
}
tip

The connected user's own client user id is also on their profile object, as clientUserId, next to the fields in Read connected-user entitlements. It is null for guests and for users signed in with Octopus authentication.


Read a member's comments and replies​

Android ≥ 1.14.0iOS not availableFlutter not availableReact Native not availableUnity not available

A comments feed holds the comments and replies one member wrote, with their parent content. Follow the community's visibility setting when you show another member's comments in your own UI.

Show another member's comments only when CommunityConfig.showCommentsOnOtherProfiles is true (default false) and their Profile.commentsFeeds.descendingId is not empty.

After initialization, OctopusSDK.userCommentRepository gives the UserCommentRepository read API. Both methods suspend and return OctopusResult<UserCommentsPage, Nothing>:

  • firstPage(feedId: String, pageSize: Int): feedId is the member's Profile.commentsFeeds.descendingId; skip the call when it is null or empty. pageSize is the maximum number of entries per page, with no default.
  • nextPage(pageCursor: String, pageSize: Int): pageCursor is the previous page's nextPageCursor. Stop when it is null.
import com.octopuscommunity.sdk.OctopusSDK
import com.octopuscommunity.sdk.domain.model.Profile
import com.octopuscommunity.sdk.domain.model.UserComment
import com.octopuscommunity.sdk.domain.network.OctopusResult

suspend fun readComments(profile: Profile): OctopusResult<List<UserComment>, Nothing> {
val feedId = profile.commentsFeeds.descendingId
?.takeIf { it.isNotEmpty() } ?: return OctopusResult.Success(emptyList())
val repository = OctopusSDK.userCommentRepository
val comments = mutableListOf<UserComment>()
var result = repository.firstPage(feedId = feedId, pageSize = 20)
while (true) {
when (val pageResult = result) {
is OctopusResult.Success -> {
comments.addAll(pageResult.data.comments)
val cursor = pageResult.data.nextPageCursor
?: return OctopusResult.Success(comments)
result = repository.nextPage(pageCursor = cursor, pageSize = 20)
}
is OctopusResult.Failure -> {
// Let the caller show an error or a retry
return pageResult
}
}
}
}
info

UserCommentsPage.comments lists the most recent first. Each UserComment has id, updateDate, content, a non-null post, and parentComment, non-null for replies. content is UserComment.Content.OfComment (with comment) or UserComment.Content.OfReply (with reply). Use nextPageCursor, not the page length, to decide whether to load more. Show a failure as an error, never as an empty feed, and in a scrolling UI load one page at a time.

Behavior and limits​

  • Unified Profile needs both the community setting and your callback. Without either, the SDK keeps its own profile screens.
  • The profile tap callback always gets a client user id. Members without one open the SDK member activity screen.

Next steps​