Skip to content

Conversation

@rahulharpal1603
Copy link
Contributor

@rahulharpal1603 rahulharpal1603 commented Nov 15, 2025

Fixes issues in PR #551

Major changes done by @Vaibhaviitian.

Summary by CodeRabbit

  • New Features
    • Mark images as favourites and toggle favourite status (persisted)
    • New Favourites page to browse all marked images
    • Favourites menu item added to the sidebar for quick access
    • Favourite state shown across image views and thumbnails; controls updated to use "Favourites" labeling and accessible text

@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 15, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds server-side favourite support: a new isFavourite column and toggle function in backend DB, a POST /images/toggle-favourite API and OpenAPI docs, frontend endpoint and hook to call it, UI changes to read/write isFavourite (new MyFav page and routing), and removal of prior local favorites hook. Also removes contents of backend/run.bat and fixes some file EOF newlines.

Changes

Cohort / File(s) Change Summary
Backend — DB & Routes
backend/app/database/images.py, backend/app/routes/images.py
Added isFavourite column (default 0); included isFavourite in image retrieval output; added db_toggle_image_favourite_status(image_id: str) -> bool; added isFavourite to ImageData, new ToggleFavouriteRequest, ImageInfoResponse, and new /images/toggle-favourite route with error handling.
Backend — Docs & Scripts
docs/backend/backend_python/openapi.json, backend/run.bat
OpenAPI: added POST /images/toggle-favourite, ToggleFavouriteRequest schema, and isFavourite property on ImageData; backend/run.bat file content removed.
Frontend — API surface
frontend/src/api/api-functions/togglefav.ts, frontend/src/api/apiEndpoints.ts
Added togglefav(image_id) API function and new imagesEndpoints.setFavourite (/images/toggle-favourite).
Frontend — Hooks
frontend/src/hooks/useToggleFav.ts, frontend/src/hooks/useFavorites.ts
Added useToggleFav hook (mutation + invalidation + feedback integration); removed old useFavorites hook that managed local favorites state.
Frontend — Types
frontend/src/types/Media.ts
Added optional isFavourite?: boolean to Image interface.
Frontend — Media components
frontend/src/components/Media/ImageCard.tsx, frontend/src/components/Media/MediaThumbnails.tsx, frontend/src/components/Media/MediaView.tsx, frontend/src/components/Media/MediaViewControls.tsx
Switched from local favorites to server-driven image.isFavourite; removed favorites prop and local helpers; renamed props onToggleFavoriteonToggleFavourite, isFavoriteisFavourite; integrated useToggleFav and adjusted button/icon rendering and styling.
Frontend — Pages & Routing
frontend/src/pages/Home/MyFav.tsx, frontend/src/pages/Home/Home.tsx, frontend/src/routes/AppRoutes.tsx, frontend/src/constants/routes.ts, frontend/src/components/Navigation/Sidebar/AppSidebar.tsx
Added MyFav page showing images with isFavourite === true; Home refactored to use centralized mutation feedback; added ROUTES.FAVOURITES, route entry for /favourites, and sidebar menu item with Heart icon.
Scripts — minor formatting
frontend/scripts/setup_env.sh, frontend/scripts/setup_win.ps1
Added missing trailing newlines (formatting-only).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ImageCard
    participant useToggleFav
    participant API as Backend API
    participant DB as Database

    User->>ImageCard: Click Favourite Button
    ImageCard->>useToggleFav: toggleFavourite(image_id)
    useToggleFav->>API: POST /images/toggle-favourite
    API->>DB: Toggle isFavourite flag
    DB-->>API: Updated status
    API-->>useToggleFav: Return status
    useToggleFav->>ImageCard: Invalidate/re-query images
    ImageCard->>User: UI updates to new isFavourite
Loading
sequenceDiagram
    participant User
    participant Sidebar
    participant MyFav
    participant API

    User->>Sidebar: Click "Favourites"
    Sidebar->>MyFav: Navigate to /favourites
    MyFav->>API: Fetch images (usePictoQuery)
    API-->>MyFav: Return images with isFavourite
    MyFav->>MyFav: Filter images where isFavourite === true
    alt Favourite images exist
        MyFav->>User: Render ChronologicalGallery
    else no favourites
        MyFav->>User: Show empty state message
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas needing extra attention:

  • Database schema changes and migrations in backend/app/database/images.py (initialization/defaults, query shape).
  • Correctness and transactional safety of db_toggle_image_favourite_status() (validation, rollback).
  • Frontend-data sync: ensure useToggleFav invalidates/re-queries images and avoids stale UI.
  • Widespread prop renames (onToggleFavoriteonToggleFavourite, isFavoriteisFavourite) and removal of favorites prop — check all callsites.
  • Deletion of backend/run.bat — confirm removal is intentional.

Possibly related PRs

Suggested labels

bug, frontend, backend, UI

Poem

🐇 I hopped through code with a tiny cheer,
A heart persisted — now favourites appear.
Toggle a beat, send it server-side,
Galleries bloom where loved images hide.
Huzzah — a rabbit's small digital pride! 💚

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Fix Issues in favourites PR' is vague and generic, using non-descriptive terms like 'Issues' that don't convey specific information about what was fixed. Provide a more specific title that describes the particular issues or changes made, e.g., 'Fix favourite toggle persistence and UI state sync' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09ab8be and 077199c.

📒 Files selected for processing (1)
  • frontend/src/components/Media/MediaView.tsx (5 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rahulharpal1603 rahulharpal1603 changed the title Fix/favourite issues Fix Issues in favourites PR Nov 15, 2025
@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

2 similar comments
@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/app/database/images.py (1)

52-70: Add schema migration for isFavourite column to handle existing databases

The review comment correctly identifies a backward compatibility issue. The code adds isFavourite BOOLEAN DEFAULT 0 to the CREATE TABLE IF NOT EXISTS images statement (line 66), and then references this column in db_get_all_images (line 147) and db_toggle_image_favourite_status (line 409). However, codebase search confirms there is no ALTER TABLE, schema checking (PRAGMA), or migration logic anywhere to add this column to existing databases. Deployments with pre-existing images tables will fail on queries referencing isFavourite.

Add a migration function (e.g., db_ensure_is_favourite_column()) that runs during startup to check if the column exists and add it if missing, as suggested in the review comment. Alternatively, ensure your deployment process rebuilds/resets the database schema.

Also note: db_toggle_image_favourite_status (line 399) should use _connect() instead of sqlite3.connect(DATABASE_PATH) directly for consistency with other functions in this module.

🧹 Nitpick comments (11)
frontend/src/pages/Home/Home.tsx (1)

25-46: Consider using errorMessage from usePictoQuery and guarding against missing data

Two small, optional improvements here:

  1. usePictoQuery already derives a user-friendly errorMessage. You could destructure it and pass it through to useMutationFeedback for richer error feedback instead of a static string, e.g.:
-  const { data, isLoading, isSuccess, isError, error } = usePictoQuery({
+  const {
+    data,
+    isLoading,
+    isSuccess,
+    isError,
+    error,
+    errorMessage,
+  } = usePictoQuery({
     queryKey: ['images'],
     queryFn: () => fetchAllImages(),
     enabled: !isSearchActive,
   });

-  useMutationFeedback(
-    { isPending: isLoading, isSuccess, isError, error },
-    {
-      loadingMessage: 'Loading images',
-      showSuccess: false,
-      errorTitle: 'Error',
-      errorMessage: 'Failed to load images. Please try again later.',
-    },
-  );
+  useMutationFeedback(
+    { isPending: isLoading, isSuccess, isError, error },
+    {
+      loadingMessage: 'Loading images',
+      showSuccess: false,
+      errorTitle: 'Error',
+      errorMessage: errorMessage ?? 'Failed to load images. Please try again later.',
+    },
+  );
  1. In the effect, isSuccess should imply data is present, but defensively defaulting to an empty array avoids surprises if that ever changes:
-    if (!isSearchActive && isSuccess) {
-      const images = data?.data as Image[];
-      dispatch(setImages(images));
-    }
+    if (!isSearchActive && isSuccess) {
+      const images = (data?.data as Image[]) ?? [];
+      dispatch(setImages(images));
+    }

Both are optional, but they make the behavior a bit more robust and user-friendly.

frontend/src/api/apiEndpoints.ts (1)

1-4: Endpoint naming vs behaviour (optional polish)

The new setFavourite: '/images/toggle-favourite' endpoint looks correct, but the name suggests “set” while the backend route semantically “toggles” the flag. Consider renaming the key to toggleFavourite for clarity if it won’t cause churn.

frontend/src/api/api-functions/togglefav.ts (1)

1-11: togglefav helper is correct; consider naming consistency

The POST call, typing, and payload shape all look good. For consistency with the rest of the codebase (useToggleFav, isFavourite), consider renaming this function to toggleFavourite (and adjusting imports) so the naming is uniform.

frontend/src/hooks/useToggleFav.ts (1)

5-17: Tighten toggleFavourite parameter typing

Implementation and invalidation logic look good, but toggleFavourite: (id: any) loses type safety even though the mutation clearly expects a string ID.

You can strengthen this by typing the variable and mutation generics:

-export const useToggleFav = () => {
-  const toggleFavouriteMutation = usePictoMutation({
-    mutationFn: async (image_id: string) => togglefav(image_id),
+export const useToggleFav = () => {
+  const toggleFavouriteMutation = usePictoMutation<APIResponse, unknown, string>({
+    mutationFn: async (image_id: string) => togglefav(image_id),
     autoInvalidateTags: ['images'],
   });
   …
   return {
-    toggleFavourite: (id: any) => toggleFavouriteMutation.mutate(id),
+    toggleFavourite: (id: string) => toggleFavouriteMutation.mutate(id),
     toggleFavouritePending: toggleFavouriteMutation.isPending,
   };
};

(or similar, depending on your existing usePictoMutation typings).

backend/app/database/images.py (2)

167-191: isFavourite mapping in db_get_all_images is fine; align types for clarity

The new unpacked is_favourite field and bool(is_favourite) mapping into "isFavourite" in the image dict are correct and match frontend expectations. To keep your schema and types in sync, consider adding isFavourite: bool to the ImageRecord TypedDict (and any related record types) so future inserts and tooling reflect the full table structure.


399-421: Reuse _connect and type aliases in db_toggle_image_favourite_status

Functionality is correct, but this helper is slightly inconsistent with the rest of the module:

  • It calls sqlite3.connect(DATABASE_PATH) directly instead of using _connect(), so it bypasses the shared connection setup (including the PRAGMA foreign_keys = ON).
  • It takes image_id: str instead of the existing ImageId alias.

You can align it with the rest of the file like this:

-def db_toggle_image_favourite_status(image_id: str) -> bool:
-    conn = sqlite3.connect(DATABASE_PATH)
+def db_toggle_image_favourite_status(image_id: ImageId) -> bool:
+    conn = _connect()
     cursor = conn.cursor()

The rest of the function can stay as‑is.

frontend/src/components/Media/ImageCard.tsx (1)

30-36: Tidy up favourite handler (remove debug log, optional UX polish).

The toggle wiring via useToggleFav and handleToggleFavourite looks good, but:

  • Line 81’s console.log(image); should be removed before shipping.
  • You may also want to use toggleFavouritePending from useToggleFav to disable the button while the mutation is in flight to avoid rapid repeat toggles (optional).

Also applies to: 75-84

docs/backend/backend_python/openapi.json (1)

890-928: Align toggle-favourite OpenAPI response with backend payload.

The additions of ImageData.isFavourite and ToggleFavouriteRequest look consistent with the backend models. However, the /images/toggle-favourite 200 response currently uses an empty schema ({}), while the route returns a structured object (success, image_id, isFavourite). It would be better to define and reference a concrete response schema (e.g. ToggleFavouriteResponse or reuse ImageData/ImageInfoResponse) so clients get accurate typing from the OpenAPI spec.

Also applies to: 2111-2164, 2553-2565

backend/app/routes/images.py (1)

122-130: Remove or wire up unused ImageInfoResponse model.

ImageInfoResponse is defined but not referenced as a response_model or used elsewhere. Either hook it up (e.g. as the response model for /images/toggle-favourite) or remove it to avoid dead code.

frontend/src/pages/Home/MyFav.tsx (1)

50-59: Clarify title/count semantics for favourites view.

title uses images.length to show Face Search Results (N found) when a search is active, but the page actually renders only favouriteImages (filtered by isFavourite === true). This can make the count misleading when there are search hits that are not favourites.

Consider either:

  • Basing the count on favouriteImages.length when on this favourites page, or
  • Changing the title text to explicitly indicate you’re viewing the favourite subset of the search results.

Also, because you return early when favouriteImages.length === 0, the EmptyGalleryState branch inside the gallery (lines 90-100) is unreachable and can be removed for clarity.

Also applies to: 60-81

frontend/src/components/Media/MediaView.tsx (1)

24-35: Defaulting images to an empty array plus guard is reasonable.

Providing a default images = [] and guarding with if (!images?.length || currentViewIndex === -1 || !currentImage) prevents the viewer from rendering in invalid states and avoids length access on undefined. Just note that MediaViewProps.images is still non-optional; if you intend to truly make it optional, consider updating the type as well.

Also applies to: 140-143

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cec1303 and 09ab8be.

⛔ Files ignored due to path filters (2)
  • __pycache__/app.cpython-313.pyc is excluded by !**/*.pyc
  • utils/__pycache__/cache.cpython-313.pyc is excluded by !**/*.pyc
📒 Files selected for processing (20)
  • backend/app/database/images.py (5 hunks)
  • backend/app/routes/images.py (4 hunks)
  • backend/run.bat (0 hunks)
  • docs/backend/backend_python/openapi.json (4 hunks)
  • frontend/scripts/setup_env.sh (1 hunks)
  • frontend/scripts/setup_win.ps1 (1 hunks)
  • frontend/src/api/api-functions/togglefav.ts (1 hunks)
  • frontend/src/api/apiEndpoints.ts (1 hunks)
  • frontend/src/components/Media/ImageCard.tsx (3 hunks)
  • frontend/src/components/Media/MediaThumbnails.tsx (0 hunks)
  • frontend/src/components/Media/MediaView.tsx (5 hunks)
  • frontend/src/components/Media/MediaViewControls.tsx (3 hunks)
  • frontend/src/components/Navigation/Sidebar/AppSidebar.tsx (2 hunks)
  • frontend/src/constants/routes.ts (1 hunks)
  • frontend/src/hooks/useFavorites.ts (0 hunks)
  • frontend/src/hooks/useToggleFav.ts (1 hunks)
  • frontend/src/pages/Home/Home.tsx (2 hunks)
  • frontend/src/pages/Home/MyFav.tsx (1 hunks)
  • frontend/src/routes/AppRoutes.tsx (2 hunks)
  • frontend/src/types/Media.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • frontend/src/hooks/useFavorites.ts
  • backend/run.bat
  • frontend/src/components/Media/MediaThumbnails.tsx
🧰 Additional context used
🧬 Code graph analysis (9)
frontend/src/api/api-functions/togglefav.ts (3)
frontend/src/types/API.ts (1)
  • APIResponse (1-8)
frontend/src/api/axiosConfig.ts (1)
  • apiClient (5-12)
frontend/src/api/apiEndpoints.ts (1)
  • imagesEndpoints (1-4)
frontend/src/pages/Home/MyFav.tsx (10)
frontend/src/features/imageSelectors.ts (1)
  • selectImages (5-7)
frontend/src/components/Media/ChronologicalGallery.tsx (2)
  • MonthMarker (10-14)
  • ChronologicalGallery (25-187)
frontend/src/app/store.ts (1)
  • RootState (22-22)
frontend/src/hooks/useQueryExtension.ts (1)
  • usePictoQuery (80-108)
frontend/src/api/api-functions/images.ts (1)
  • fetchAllImages (5-14)
frontend/src/hooks/useMutationFeedback.tsx (1)
  • useMutationFeedback (60-135)
frontend/src/types/Media.ts (1)
  • Image (13-23)
frontend/src/features/imageSlice.ts (1)
  • setImages (18-20)
frontend/src/components/EmptyStates/EmptyGalleryState.tsx (1)
  • EmptyGalleryState (3-28)
frontend/src/components/Timeline/TimelineScrollbar.tsx (1)
  • TimelineScrollbar (38-409)
frontend/src/components/Media/ImageCard.tsx (1)
frontend/src/hooks/useToggleFav.ts (1)
  • useToggleFav (5-18)
frontend/src/routes/AppRoutes.tsx (2)
frontend/src/constants/routes.ts (1)
  • ROUTES (1-12)
frontend/src/pages/Home/MyFav.tsx (1)
  • MyFav (18-115)
frontend/src/components/Navigation/Sidebar/AppSidebar.tsx (1)
frontend/src/constants/routes.ts (1)
  • ROUTES (1-12)
backend/app/routes/images.py (1)
backend/app/database/images.py (2)
  • db_toggle_image_favourite_status (399-421)
  • db_get_all_images (123-214)
frontend/src/hooks/useToggleFav.ts (3)
frontend/src/hooks/useQueryExtension.ts (1)
  • usePictoMutation (26-78)
frontend/src/api/api-functions/togglefav.ts (1)
  • togglefav (5-11)
frontend/src/hooks/useMutationFeedback.tsx (1)
  • useMutationFeedback (60-135)
frontend/src/pages/Home/Home.tsx (5)
frontend/src/hooks/useQueryExtension.ts (1)
  • usePictoQuery (80-108)
frontend/src/api/api-functions/images.ts (1)
  • fetchAllImages (5-14)
frontend/src/hooks/useMutationFeedback.tsx (1)
  • useMutationFeedback (60-135)
frontend/src/types/Media.ts (1)
  • Image (13-23)
frontend/src/features/imageSlice.ts (1)
  • setImages (18-20)
frontend/src/components/Media/MediaView.tsx (5)
frontend/src/types/Media.ts (1)
  • MediaViewProps (35-39)
frontend/src/features/imageSelectors.ts (1)
  • selectCurrentViewIndex (9-10)
frontend/src/hooks/useToggleFav.ts (1)
  • useToggleFav (5-18)
frontend/src/hooks/useSlideshow.ts (1)
  • useSlideshow (3-30)
frontend/src/components/Media/MediaViewControls.tsx (1)
  • MediaViewControls (16-100)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: sync-labels
  • GitHub Check: Backend Tests
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Tauri Build Check (windows-latest)
🔇 Additional comments (10)
frontend/scripts/setup_win.ps1 (1)

73-73: Non-functional EOF formatting change is fine

Only the trailing newline/EOF formatting changed here; the message and behavior remain the same and look good.

frontend/scripts/setup_env.sh (1)

118-118: EOF newline addition only; behavior unchanged

This change just ensures the file ends with a newline; the note about restarting the terminal remains correct and helpful.

frontend/src/pages/Home/Home.tsx (1)

15-39: Centralizing loader/error handling with useMutationFeedback looks solid

Mapping the query state into useMutationFeedback (isPending: isLoading, isSuccess, isError, error) and disabling success to avoid a dialog on every successful fetch is a clean way to reuse the global loader/error UI without duplicating logic.

frontend/src/types/Media.ts (1)

13-23: isFavourite field addition is consistent and non-breaking

Adding isFavourite?: boolean to Image cleanly models the new favourites feature while keeping the property optional so existing usages of Image remain valid.

frontend/src/components/Navigation/Sidebar/AppSidebar.tsx (1)

15-47: Favourites sidebar entry is wired correctly

Importing Heart and adding the Favourites menu item pointing to /${ROUTES.FAVOURITES} integrates cleanly with the existing menu structure and active-route logic.

frontend/src/constants/routes.ts (1)

3-3: New FAVOURITES route is consistent with existing patterns

Key name and path segment match the existing routing style; no issues found.

frontend/src/routes/AppRoutes.tsx (1)

8-8: MyFav route wiring under /favourites looks correct

Importing MyFav and mounting it at ROUTES.FAVOURITES inside the Layout matches how the other main pages are registered; routing setup looks good.

Also applies to: 20-20

backend/app/routes/images.py (1)

29-38: isFavourite field wiring in ImageData looks consistent.

Adding isFavourite: bool to ImageData and mapping it via image.get('isFavourite', False) in get_all_images keeps the API strongly typed while remaining backwards compatible with older rows that may lack the column.

Also applies to: 60-69

frontend/src/pages/Home/MyFav.tsx (1)

26-48: Data fetching and feedback wiring look solid.

Using usePictoQuery with enabled: !isSearchActive and feeding isLoading/isSuccess into useMutationFeedback is a clean way to reuse your feedback hook for queries. The setImages dispatch on successful fetch is straightforward.

frontend/src/components/Media/MediaViewControls.tsx (1)

4-14: LGTM! Prop renames verified across all call sites.

The prop name updates from American to British spelling (onToggleFavoriteonToggleFavourite, isFavoriteisFavourite) have been consistently applied throughout the codebase. Verification confirms:

  • MediaViewControls.tsx component definition uses new prop names
  • MediaView.tsx (parent component) has been updated to pass the new prop names
  • No remaining references to old prop names exist in the codebase

@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@rahulharpal1603 rahulharpal1603 merged commit aeb3924 into AOSSIE-Org:main Nov 15, 2025
7 of 8 checks passed
@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@DecodeX15
Copy link
Contributor

@rahulharpal1603 sir as the contri in this pr is major mine so can u please assign this to me

@rahulharpal1603
Copy link
Contributor Author

@rahulharpal1603 sir as the contri in this pr is major mine so can u please assign this to me

Done.

@DecodeX15
Copy link
Contributor

Thank you, Sir.
I appreciate you taking the time to review and accept my PR.
I look forward to contributing more in the future.

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

Labels

backend bug Something isn't working frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants