Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 19, 2025

Implementation Complete: Add Navigation from User Name/Icon to User Profile

Changes Made

Shared UI Components

  • Created UserProfileLink component for clickable user names
  • Created UserAvatar component that links to profile
  • UserAvatar handles empty/whitespace-only userName with '?' fallback
  • UserAvatar includes onError handler for broken images - falls back to logo/initials when image fails to load
  • Created shared ShareThriftLogo component (DRY principle)

Updated Components

  • sharer-information.tsx: Avatar and name now link to the sharer's profile
  • listing-banner.tsx: User name in conversation headers links to profile
  • message-thread.tsx: Message author avatars link to their profiles with proper display names
  • conversation-box.tsx: Memoized sharerInfo and reserverInfo with useMemo to reduce rerenders
  • Clarified MessageThread props - sharer and reserver are now required (non-optional)
  • requests-table.tsx: Removed defensive null check for reservation period - data validation handled upstream
  • NEW: Removed redundant userId defensive checks - trust data loader guarantees that owner always exists

Routing

  • Added /user/:userId route for viewing other user profiles
  • Created UserProfileViewContainer with GraphQL query for user data
  • Reuses existing ProfileView component with isOwnProfile={false}
  • Email addresses excluded from other users' profiles
  • Removed unnecessary userId guard - let query skip and ComponentQueryLoader handle missing data

Code Quality Improvements

  • Added JSDoc comments to exported interfaces (UserAvatarProps, UserProfileLinkProps)
  • Added useCallback and useMemo memoization for helper functions
  • Added comments clarifying public route access for user profiles
  • Storybook stories demonstrate the navigation patterns
  • Simplified components per code review - removed over-engineering
  • Aligned TypeScript types with runtime guarantees (required props are non-optional)
  • Removed redundant defensive checks - trust upstream data loader guarantees
  • NEW: Cleaned up optional chaining where data is guaranteed by loader

Acceptance Criteria Met

✅ User names and profile images are clickable in major UI locations (listing headers, message threads)
✅ Clicking navigates to the correct user profile view via /user/:userId route
✅ Components leverage Ant Design and are accessible
✅ Storybook stories demonstrate the navigation patterns
✅ Robust error handling for broken avatar images
✅ Code simplified per reviewer feedback
✅ Props clarity improved - removed confusing optionality
✅ Components trust data loader guarantees - no defensive null checks

Original prompt

This section details on the original issue you should resolve

<issue_title>Add Navigation From User Name / Icon to User Profile</issue_title>
<issue_description>## Background
The ShareThrift UI should enable users to easily access the profile view of any user by clicking on their name or profile image wherever it appears—such as in conversation headers, listing/reservation request headers, or descriptions. This supports a seamless navigation experience and aligns with platform usability goals.

Task Description

  • Enable clickable navigation from a user’s name or profile image to their profile view.
    • Applies to conversation headers, listing headers, reservation request headers, and anywhere the user name/profile image is rendered.
  • Ensure the navigation link uses the correct route to the user’s profile per UI routing standards (see layouts and pages instructions).
  • Use accessible Ant Design components (e.g., Avatar, Typography.Link) for image and name rendering.
  • On click, navigate to the profile page of the selected user, passing the correct user identifier (username or userId) in the route.
  • Update all relevant components to support this behavior and ensure consistent UI/UX.
  • Update Storybook stories to demonstrate navigation from clickable name/image to the user profile.
  • Update documentation to clarify navigation patterns for user profiles.

Acceptance Criteria

  • User names and profile images are clickable in all major UI locations (conversation header, listing header, reservation request header, etc.).
  • Clicking navigates to the correct user profile view.
  • Navigation works for both logged-in and guest users (where permitted).
  • Components leverage Ant Design and are accessible.
  • Storybook stories and documentation reflect the new navigation pattern.

References

Image

<agent_instructions>---

These are the additional prompts

@copilot
Please analyze the repository architecture and implement Issue #191 according to the ShareThrift code structure and conventions.
Use the following instructions in addition to the workspace context:


🧭 Repository Architecture You Must Follow

ShareThrift is structured using DDD (Domain-Driven Design) on top of CellixJS, with these layers:

1. Domain Layer (packages/sthrift/domain/...)

  • Contains Entities, Value Objects, Aggregates, Domain Events.
  • All state transitions must happen in aggregates.
  • Domain events must be emitted from aggregate behavior, not resolvers.

2. Application Services (packages/sthrift/application-services/...)

  • Coordinates domain logic and persistence (Unit of Work).

  • Responsible for:

    • authorization (verifiedJwt)
    • orchestrating domain calls
    • saving via UoW
    • emitting integration events if needed

3. GraphQL Layer (packages/sthrift/graphql/...)

  • GraphQL schema + resolvers
  • Should not contain domain logic
  • Should call application services only

4. Persistence Layer (packages/sthrift/persistence/...)

  • Mongo using Mongoose
  • Everything saved via repositories and UoW

5. UI Layer (apps/ui-*)


🛠️ Your Objective (Issue #191)

Implement the feature EXACTLY according to the Issue description:

🔹 Read the Issue
🔹 Read the acceptance criteria
🔹 Read related tasks in the milestone
🔹 Respect all DDD boundaries
🔹 Follow existing patterns in the repo

Then implement:

✔ Domain additions

  • Add missing methods / state transitions
  • Add related domain events
  • Add validations / invariants

✔ Application-service orchestration

  • Add new command interface
  • Add service function
  • Use UoW with correct repository

✔ GraphQL additions

  • Add SDL entries
  • Add resolver mapping
  • Return proper result type objects

✔ Tests

  • Add unit tests in domain
  • Add application-service tests
  • Add resolver tests if needed

✔ Coding Style

Follow existing project patterns:

  • strict TypeScript
  • kebab-case filenames
  • no logic inside resolvers
  • domain event emission via aggregates
  • Biome formatting

📎 Extra Requirements

1. Follow existing naming conventions

E.g. item-listing-updated.event.ts, *.application-service.ts, *.uow.ts.

2. Follow existing DTO and result types

Return objects similar to AcceptReservationRequestResult or existing patterns.

3. Do NOT hardcode strings

Use domain constants or value objects.
...


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Summary by Sourcery

Add public user profile viewing and consistent navigation from user names and avatars to profile pages across the UI.

New Features:

  • Introduce public /user/:userId route and page for viewing user profiles, backed by a new UserProfileViewContainer that queries profile data by user ID.
  • Add reusable UserProfileLink and UserAvatar shared components, plus a ShareThriftLogo and centralized getUserProfilePath helper for user-related navigation.

Bug Fixes:

  • Ensure requested-by user IDs are exposed from listing requests to the UI so that requester names in the My Listings requests table can link to the correct profile.

Enhancements:

  • Update sharer info, listing banners, message threads, and requests table to use the new user profile navigation components for a consistent clickable experience.
  • Improve message thread user context by computing sharer/reserver display names and passing them to message bubbles and avatars.
  • Refine tracing tests and environment setup utilities for clearer global typing and consistent formatting.

Build:

  • Normalize formatting in the mock OAuth2 server package.json and setup-environment utilities for consistency.

Documentation:

  • Add user-profile-navigation documentation describing routes, shared components, GraphQL usage, accessibility, and best practices for linking to user profiles.

Tests:

  • Add Storybook stories for UserProfileLink, UserAvatar, requests table, listing banner, message thread, and the user profile view page/container to demonstrate and validate profile navigation behavior.
  • Add unit tests for reservation-request pagination/mapping, including coverage for the new requestedById field and sorting edge cases.
  • Add Storybook coverage for the isValidUserId utility to document expected behavior for valid, invalid, and edge-case user IDs.

Copilot AI and others added 3 commits November 19, 2025 20:05
Co-authored-by: dani-vaibhav <182140623+dani-vaibhav@users.noreply.github.com>
Co-authored-by: dani-vaibhav <182140623+dani-vaibhav@users.noreply.github.com>
Co-authored-by: dani-vaibhav <182140623+dani-vaibhav@users.noreply.github.com>
Copilot AI changed the title [WIP] Add navigation from user name/icon to user profile Add user profile navigation from names and avatars Nov 19, 2025
Copilot AI requested a review from dani-vaibhav November 19, 2025 20:17
@gidich
Copy link
Member

gidich commented Nov 26, 2025

@sourcery-ai review

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Nov 26, 2025

Reviewer's Guide

Implements shared, route-safe user profile navigation from names and avatars across the UI, exposes requester IDs via GraphQL to support linking, adds a public /user/:userId profile view, and includes Storybook/tests plus minor refactors and cleanup.

Sequence diagram for loading a public user profile after clicking a name or avatar

sequenceDiagram
  actor user
  participant browser
  participant user_profile_link as UserProfileLink
  participant router as ReactRouter
  participant view_user_profile as ViewUserProfile
  participant user_profile_container as UserProfileViewContainer
  participant apollo_client as ApolloClient
  participant graphql_api as GraphQLServer
  participant profile_view as ProfileView

  user->>browser: Click name_or_avatar
  browser->>user_profile_link: Handle click on Link
  user_profile_link->>router: Push route getUserProfilePath(userId)
  router-->>browser: Update URL to /user/userId

  router->>view_user_profile: Render for route user_userId
  view_user_profile->>user_profile_container: Render container

  user_profile_container->>apollo_client: useQuery(HomeViewUserProfileContainerUserById, userId)
  apollo_client->>graphql_api: Query userById(id_userId)
  graphql_api-->>apollo_client: Return PersonalUser_or_AdminUser

  apollo_client-->>user_profile_container: Provide data_loading_error
  user_profile_container->>profile_view: Render ProfileView(user, listings_empty, isOwnProfile_false)
  profile_view-->>browser: Display public_profile_read_only
Loading

Entity relationship diagram for ListingRequest requestedById and User

erDiagram
  ListingRequest {
    ObjectID id
    string title
    string image
    string requestedBy
    ObjectID requestedById
    string requestedOn
    string reservationPeriod
    string status
  }

  User {
    ObjectID id
    string username
    string firstName
    string lastName
  }

  User ||--o{ ListingRequest : requestedBy
Loading

Updated class diagram for shared user profile navigation components and reservation requests

classDiagram

  class UserProfileLink {
    +userId string
    +displayName string
    +className string
    +style React_CSSProperties
    +render() ReactElement
  }

  class UserAvatar {
    +userId string
    +userName string
    +size number
    +avatarUrl string
    +className string
    +style React_CSSProperties
    +shape string
    +render() ReactElement
  }

  class ShareThriftLogo {
    +size number
    +fill string
    +className string
    +style React_CSSProperties
    +render() ReactElement
  }

  class UserRoutesUtil {
    +getUserProfilePath(userId string) string
  }

  class UserValidationUtil {
    +isValidUserId(userId string) boolean
  }

  class ViewUserProfile {
    +render() ReactElement
  }

  class UserProfileViewContainer {
    -userId string
    +render() ReactElement
    +handleListingClick(listingId string) void
    +profileUser any
  }

  class MessageThread {
    +conversationId string
    +messages Message_array
    +currentUserId string
    +sharer UserInfo
    +reserver UserInfo
    +render() ReactElement
    +getAuthorDisplayName(authorId string) string
  }

  class ConversationBox {
    +data Conversation
    +messageText string
    +render() ReactElement
    +handleSendMessage(event React_FormEvent) void
    +getUserDisplayName(user ConversationUser) string
  }

  class ListingBanner {
    +owner User
    +render() ReactElement
  }

  class RequestsTable {
    +data ListingRequestData_array
    +render() ReactElement
  }

  class ListingRequestData {
    +id string
    +title string
    +image string
    +requestedBy string
    +requestedById string
    +requestedOn string
    +reservationPeriod string
    +status string
  }

  class ListingRequestDomainShape {
    +id any
    +state string
    +createdAt Date
    +reservationPeriodStart Date
    +reservationPeriodEnd Date
    +listing any
    +reserver any
  }

  class ListingRequestUiShape {
    +id string
    +title string
    +image string
    +requestedBy string
    +requestedById string
    +requestedOn string
    +reservationPeriod string
    +status string
    +_raw any
  }

  class ReservationRequestResolversHelper {
    +paginateAndFilterListingRequests(requests ListingRequestDomainShape_array, options PaginateOptions) PaginatedListingRequests
  }

  class PaginateOptions {
    +page number
    +pageSize number
    +statusFilters string_array
    +searchText string
    +sorter any
  }

  class PaginatedListingRequests {
    +items ListingRequestUiShape_array
    +page number
    +pageSize number
    +total number
  }

  class ProfileView

  UserProfileLink ..> UserRoutesUtil : uses
  UserProfileLink ..> UserValidationUtil : uses

  UserAvatar ..> UserRoutesUtil : uses
  UserAvatar ..> UserValidationUtil : uses
  UserAvatar ..> ShareThriftLogo : uses

  ViewUserProfile ..> UserProfileViewContainer : renders

  UserProfileViewContainer ..> ProfileView : renders

  ConversationBox ..> ListingBanner : uses
  ConversationBox ..> MessageThread : uses

  MessageThread ..> UserAvatar : uses

  RequestsTable ..> ListingRequestData : displays
  RequestsTable ..> UserProfileLink : uses

  ReservationRequestResolversHelper ..> ListingRequestDomainShape : maps_from
  ReservationRequestResolversHelper ..> ListingRequestUiShape : maps_to
  ReservationRequestResolversHelper ..> PaginateOptions : uses
  ReservationRequestResolversHelper ..> PaginatedListingRequests : returns
Loading

File-Level Changes

Change Details Files
Introduce shared user profile navigation components and utilities, and apply them to sharer and messaging UIs.
  • Add UserProfileLink component that renders a Link to the user profile when given a valid userId, falling back to plain text when invalid.
  • Add UserAvatar component that renders a branded Avatar optionally wrapped in a profile Link, with fallback initial or '?' for missing/whitespace names.
  • Create shared ShareThriftLogo SVG component and centralized getUserProfilePath and isValidUserId utilities for DRY routing and validation.
  • Update sharer-information to use UserAvatar and UserProfileLink for the sharer avatar and name instead of inline Avatar markup.
  • Update listing-banner to wrap the owner name in UserProfileLink, preserving styling.
apps/ui-sharethrift/src/components/shared/user-profile-link.tsx
apps/ui-sharethrift/src/components/shared/user-avatar.tsx
apps/ui-sharethrift/src/components/shared/sharethrift-logo.tsx
apps/ui-sharethrift/src/components/shared/utils/user-routes.ts
apps/ui-sharethrift/src/components/shared/utils/user-validation.ts
apps/ui-sharethrift/src/components/layouts/home/components/view-listing/sharer-information/sharer-information.tsx
apps/ui-sharethrift/src/components/layouts/home/messages/components/listing-banner.tsx
Add a public user profile route and container wired to GraphQL, and integrate it into the home layout routing.
  • Add ViewUserProfile page that renders UserProfileViewContainer and route it under /user/:userId inside HomeRoutes SectionLayout.
  • Implement UserProfileViewContainer that reads :userId from params, queries userById via HomeViewUserProfileContainerUserByIdDocument, and renders ProfileView in read-only mode using ComponentQueryLoader.
  • Define a public GraphQL query and fragments for fetching limited PersonalUser/AdminUser profile data by ID.
  • Document the new navigation behavior, shared components, and route helper in user-profile-navigation.md.
  • Add Storybook stories for UserProfileViewContainer and ViewUserProfile to demonstrate a full routed profile view with mocked GraphQL data.
apps/ui-sharethrift/src/components/layouts/home/index.tsx
apps/ui-sharethrift/src/components/layouts/home/pages/view-user-profile-page.tsx
apps/ui-sharethrift/src/components/layouts/home/components/view-user-profile/user-profile-view.container.tsx
apps/ui-sharethrift/src/components/layouts/home/components/view-user-profile/user-profile-view.container.graphql
apps/ui-sharethrift/src/components/layouts/home/components/view-user-profile/user-profile-view.container.stories.tsx
apps/ui-sharethrift/src/components/layouts/home/pages/view-user-profile-page.stories.tsx
apps/ui-sharethrift/docs/user-profile-navigation.md
Surface requester IDs through GraphQL reservation requests and update My Listings requests table to link to requester profiles.
  • Extend ListingRequest GraphQL type with requestedById field and ensure reservation-request resolver’s domain shape includes reserver.id.
  • Expose requestedById in the UI mapping (ListingRequestUiShape) and pagination/filter helper, returning null-safe IDs.
  • Export paginateAndFilterListingRequests for direct unit testing and add tests covering mapping defaults, filtering, sorting (including requestedById), and pagination behavior.
  • Update ListingRequestData UI type and requests-table GraphQL fragment to include requestedById.
  • Render the RequestsTable "Requested By" column as a UserProfileLink when requestedById is present, otherwise as styled plain text; keep blue (#1890ff) link styling for affordance.
packages/sthrift/graphql/src/schema/types/listing/item-listing.graphql
packages/sthrift/graphql/src/schema/types/reservation-request/reservation-request.resolvers.ts
packages/sthrift/graphql/src/schema/types/reservation-request/reservation-request.paginate.test.ts
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/my-listings-dashboard.types.ts
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/requests-table.container.graphql
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/requests-table.tsx
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/requests-table.stories.tsx
Enhance messaging UI to show correct display names and profile-linked avatars for conversation participants.
  • Refactor ConversationBox to include a pure getUserDisplayName helper and memoized sharerInfo/reserverInfo objects (id + displayName) via useMemo.
  • Update MessageThread props to accept sharer/reserver metadata and compute human-friendly authorDisplayName via useCallback.
  • Pass authorDisplayName into MessageBubble and swap the inline Avatar for the shared UserAvatar, so message author avatars are clickable profile links when possible.
  • Update MessageThread Storybook story to pass mock sharer/reserver IDs consistent with authorId, and wrap stories in a Router decorator for working Links.
apps/ui-sharethrift/src/components/layouts/home/messages/components/conversation-box.tsx
apps/ui-sharethrift/src/components/layouts/home/messages/components/message-thread.tsx
apps/ui-sharethrift/src/components/layouts/home/messages/stories/MessageThread.stories.tsx
Add Storybook coverage and examples for new shared components/utilities and profile navigation usages.
  • Create UserProfileLink stories that verify link rendering, href generation via getUserProfilePath, non-link fallback for invalid IDs, and contextual usage.
  • Create UserAvatar stories covering various sizes, shapes, multiple avatars, and the non-clickable behavior when userId is missing.
  • Add Storybook-based demonstrations/tests for isValidUserId, including valid, invalid, and edge-case inputs.
  • Augment ListingBanner and RequestsTable stories to include router decorators and assertions that names link to /user/:userId where applicable.
apps/ui-sharethrift/src/components/shared/user-profile-link.stories.tsx
apps/ui-sharethrift/src/components/shared/user-avatar.stories.tsx
apps/ui-sharethrift/src/components/shared/utils/user-validation.stories.tsx
apps/ui-sharethrift/src/components/layouts/home/messages/stories/ListingBanner.stories.tsx
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/requests-table.stories.tsx
Miscellaneous cleanup and test improvements in non-UI areas.
  • Use a typed globals object when storing mock span state in tracing tests for clearer typings and safer access.
  • Reformat mock-oauth2-server package.json and setup-environment.ts to the project’s preferred indentation/formatting and keep logging minimal.
  • Remove the unnecessary node-forge devDependency at the repo root (mentioned in description but not visible in diff) and ensure root package.json has a trailing newline.
packages/sthrift/graphql/src/helpers/tracing.test.ts
packages/cellix/mock-oauth2-server/package.json
packages/cellix/mock-oauth2-server/src/setup-environment.ts
package.json

Assessment against linked issues

Issue Objective Addressed Explanation
#191 Implement clickable navigation from user names and profile images to the user profile view in all major UI locations using Ant Design components and the correct route, working for both logged-in and guest users.
#191 Expose and use the correct routing and data needed for profile navigation, including a user profile route/page and any required GraphQL fields or helpers so components can navigate with the correct user identifier.
#191 Update Storybook stories and documentation to demonstrate and describe the new user profile navigation pattern.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • In UserProfileViewContainer, the early if (!viewedUser) return null; short-circuits the ComponentQueryLoader so you never see loading/error states; consider moving the viewedUser-dependent logic inside the hasDataComponent or relying solely on ComponentQueryLoader to gate rendering.
  • In UserProfileLink, wrapping Typography.Link inside a React Router Link nests two interactive elements; it would be cleaner to style the router Link directly or use Ant’s Link with component={RouterLink} to avoid redundant semantics.
  • Several call sites (e.g. ListingBanner with owner?.id || '') may render UserProfileLink/UserAvatar with an empty userId, which generates /user/ URLs; it would be safer to only render these as links when a valid ID is present and fall back to non-clickable text/avatar otherwise.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `UserProfileViewContainer`, the early `if (!viewedUser) return null;` short-circuits the `ComponentQueryLoader` so you never see loading/error states; consider moving the `viewedUser`-dependent logic inside the `hasDataComponent` or relying solely on `ComponentQueryLoader` to gate rendering.
- In `UserProfileLink`, wrapping `Typography.Link` inside a React Router `Link` nests two interactive elements; it would be cleaner to style the router `Link` directly or use Ant’s `Link` with `component={RouterLink}` to avoid redundant semantics.
- Several call sites (e.g. `ListingBanner` with `owner?.id || ''`) may render `UserProfileLink`/`UserAvatar` with an empty `userId`, which generates `/user/` URLs; it would be safer to only render these as links when a valid ID is present and fall back to non-clickable text/avatar otherwise.

## Individual Comments

### Comment 1
<location> `apps/ui-sharethrift/src/components/layouts/home/components/view-user-profile/user-profile-view.container.tsx:27-33` </location>
<code_context>
+		navigate(`/listing/${listingId}`);
+	};
+
+	const viewedUser = userQueryData?.userById;
+
+	if (!userId) {
+		return <div>User ID is required</div>;
+	}
+
+	if (!viewedUser) {
+		return null;
+	}
</code_context>

<issue_to_address>
**issue (bug_risk):** Avoid returning `null` before `ComponentQueryLoader` can handle loading and error states.

Because the query is initially loading, `userQueryData` (and thus `viewedUser`) is `undefined`, so the component returns `null` immediately and `ComponentQueryLoader` never renders. Please remove the early `if (!viewedUser) return null;` and let `ComponentQueryLoader`’s `loading`, `error`, and `hasData` props control the UI instead.
</issue_to_address>

### Comment 2
<location> `apps/ui-sharethrift/src/components/layouts/home/messages/components/listing-banner.tsx:74-75` </location>
<code_context>
+```tsx
+import { UserProfileLink } from '../shared/user-profile-link.tsx';
+
+<UserProfileLink 
+  userId="507f1f77bcf86cd799439011" 
+  displayName="John Doe"
</code_context>

<issue_to_address>
**issue (bug_risk):** Guard against rendering a profile link when `owner.id` is missing.

If `owner` or `owner.id` is missing, this will still render a link pointing to `/user/`, which is likely an invalid profile and confusing for users. Instead, only render `UserProfileLink` when a valid ID is available, and otherwise show plain text or nothing.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@gidich
Copy link
Member

gidich commented Nov 26, 2025

@dani-vaibhav - Sourcery has some good feedback you!

@dani-vaibhav
Copy link

@copilot
Please address the comments from this code review:

Overall Comments

  • In UserProfileViewContainer, the early if (!viewedUser) return null; short-circuits the ComponentQueryLoader so you never see loading/error states; consider moving the viewedUser-dependent logic inside the hasDataComponent or relying solely on ComponentQueryLoader to gate rendering.
  • In UserProfileLink, wrapping Typography.Link inside a React Router Link nests two interactive elements; it would be cleaner to style the router Link directly or use Ant’s Link with component={RouterLink} to avoid redundant semantics.
  • Several call sites (e.g. ListingBanner with owner?.id || '') may render UserProfileLink/UserAvatar with an empty userId, which generates /user/ URLs; it would be safer to only render these as links when a valid ID is present and fall back to non-clickable text/avatar otherwise.

Individual Comments

Comment 1

apps/ui-sharethrift/src/components/layouts/home/components/view-user-profile/user-profile-view.container.tsx:27-33
<code_context>

  •   navigate(`/listing/${listingId}`);
    
  • };
  • const viewedUser = userQueryData?.userById;
  • if (!userId) {
  •   return <div>User ID is required</div>;
    
  • }
  • if (!viewedUser) {
  •   return null;
    
  • }
    </code_context>

<issue_to_address>
issue (bug_risk): Avoid returning null before ComponentQueryLoader can handle loading and error states.

Because the query is initially loading, userQueryData (and thus viewedUser) is undefined, so the component returns null immediately and ComponentQueryLoader never renders. Please remove the early if (!viewedUser) return null; and let ComponentQueryLoader’s loading, error, and hasData props control the UI instead.
</issue_to_address>

Comment 2

apps/ui-sharethrift/src/components/layouts/home/messages/components/listing-banner.tsx:74-75
<code_context>
+```tsx
+import { UserProfileLink } from '../shared/user-profile-link.tsx';
+
+<UserProfileLink

  • userId="507f1f77bcf86cd799439011"
  • displayName="John Doe"
    </code_context>

<issue_to_address>
issue (bug_risk): Guard against rendering a profile link when owner.id is missing.

If owner or owner.id is missing, this will still render a link pointing to /user/, which is likely an invalid profile and confusing for users. Instead, only render UserProfileLink when a valid ID is available, and otherwise show plain text or nothing.
</issue_to_address>

Co-authored-by: dani-vaibhav <182140623+dani-vaibhav@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Nov 28, 2025

@copilot
Please address the comments from this code review:

...

Addressed all code review feedback in commit 7ab4250:

  1. UserProfileViewContainer: Removed the early if (!viewedUser) return null; check. Now ComponentQueryLoader properly handles loading/error states.

  2. UserProfileLink: Removed the nested Typography.Link inside router Link. Now styles the router Link directly with textDecoration: 'none' and color: 'inherit'.

  3. UserProfileLink/UserAvatar: Added guards to check for valid userIds using !!userId && userId.trim() !== ''. When invalid, renders as plain text (UserProfileLink) or non-clickable avatar (UserAvatar) instead of generating broken /user/ URLs.

@dani-vaibhav
Copy link

@copilot
#294 (comment)
#294 (comment)

Address the two discussions and implement the suggested changes.

Copy link
Contributor Author

Copilot AI commented Nov 28, 2025

@copilot
#294 (comment)
#294 (comment)
...

Both issues were already addressed in commit 7ab4250:

  1. Discussion r2565600756 (user-profile-view.container.tsx): The early if (!viewedUser) return null; was removed. Now ComponentQueryLoader properly controls loading/error states via hasData={profileUser}.

  2. Discussion r2565600763 (listing-banner.tsx): The UserProfileLink component now guards against empty/invalid userIds. When props.owner?.id is missing, it renders plain text instead of a broken /user/ link (see lines 16-24 in user-profile-link.tsx).

@dani-vaibhav
Copy link

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and found some issues that need to be addressed.

  • In HomeRoutes you switched the layout element from SectionLayout to HomeTabsLayout but did not import or define HomeTabsLayout, which will cause a runtime/compile error—either import the correct layout component or revert to the existing SectionLayout.
  • In message-thread.tsx, the UserAvatar is always given userName="User", which degrades initials and ARIA labelling; if possible, pass the actual display name/username from the message metadata so the avatar and accessible label are meaningful.
  • The new node-forge devDependency in package.json doesn’t appear to be used by any of the added code; consider removing it if it’s not required to avoid unnecessary dependency bloat.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `HomeRoutes` you switched the layout element from `SectionLayout` to `HomeTabsLayout` but did not import or define `HomeTabsLayout`, which will cause a runtime/compile error—either import the correct layout component or revert to the existing `SectionLayout`.
- In `message-thread.tsx`, the `UserAvatar` is always given `userName="User"`, which degrades initials and ARIA labelling; if possible, pass the actual display name/username from the message metadata so the avatar and accessible label are meaningful.
- The new `node-forge` devDependency in `package.json` doesn’t appear to be used by any of the added code; consider removing it if it’s not required to avoid unnecessary dependency bloat.

## Individual Comments

### Comment 1
<location> `apps/ui-sharethrift/src/components/layouts/home/index.tsx:18` </location>
<code_context>
-}
+  return (
+    <Routes>
+      <Route path="" element={<HomeTabsLayout />}>
+        <Route path="" element={<Listings />} />
+        <Route path="listing/:listingId" element={<ViewListing />} />
</code_context>

<issue_to_address>
**issue (bug_risk):** HomeTabsLayout is used but not defined/imported in this file, which will cause a runtime/compile-time error.

This module only imports `SectionLayout`, but the JSX now uses `HomeTabsLayout`. Unless `HomeTabsLayout` is globally available, this will fail to compile or throw at runtime. Please either revert to `SectionLayout` or import/define `HomeTabsLayout` here to match the JSX.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

… optionality

Co-authored-by: dani-vaibhav <182140623+dani-vaibhav@users.noreply.github.com>
Co-authored-by: dani-vaibhav <182140623+dani-vaibhav@users.noreply.github.com>
Co-authored-by: dani-vaibhav <182140623+dani-vaibhav@users.noreply.github.com>
className={className}
currentUserId={currentUserId}
isCreating={isCreating}
isMobile={isMobile}
Copy link
Contributor

Choose a reason for hiding this comment

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

Confused on why isMobile is being passed down, can be checked at any level, doesnt seem needed to me.

className={className}
currentUserId={currentUserId}
isCreating={isCreating}
isMobile={isMobile}
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here

{ fields: command.fields },
);

const mapped: ListingRequestPage['items'] = requests.map((r) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This whole map function shouldnt be needed, it says to me the data isnt type matching correctly, seems like a lot of weird type checks are happening

refactor: simplify access to environment variables in the code
test: update mock data source access in admin and personal user tests
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.

Add Navigation From User Name / Icon to User Profile

5 participants