Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Public Recipe Browser #2525

Merged

Conversation

michael-genson
Copy link
Collaborator

@michael-genson michael-genson commented Aug 27, 2023

What type of PR is this?

(REQUIRED)

  • feature

What this PR does / why we need it:

(REQUIRED)

This PR adds a public recipe browser, similar to the one in 0.5.x. With this PR, we are ALMOST at feature parity with the old version of Mealie. Cookbooks are still not implemented JK we're at full parity now that cookbooks are implemented.

Here's what it looks like:
image

Looks identical to the logged-in one, minus the sidebar (this has been re-added, see below) and stuff in the header (just like the existing public recipe page). This PR aims to accomplish the following goals:

  1. Make as minimal changes to existing components/composables/backend as possible, reusing as much as we can
  2. Make the changes we do need as DRY as possible (without stupid amounts of refactoring)

To accomplish the recipe browser, several changes were made to both the backend and the frontend.

Backend

The backend changes were pretty minimal. I refactored the existing explore recipe route to inherit from a new BasePublicExploreController, which basically just injects the group into the controller using the slug from the path (and if it's missing or the group is missing/private, throw a 404):

class BasePublicExploreController(BasePublicController):
    group: GroupInDB = Depends(get_public_group)

...

async def get_public_group(group_slug: str = fastapi.Path(...), session=Depends(generate_session)) -> GroupInDB:
    repos = get_repositories(session)
    group = repos.groups.get_by_slug_or_id(group_slug)

    if not group or group.preferences.private_group:
        raise HTTPException(404, "group not found")
    else:
        return group

The rest of the work was pretty much copying the normal logged-in user get and get_all routes into the new public explore controller. I'm not a fan of how I had to do some copy-pasting, but I couldn't for the life of me get separate routes to work (with different FastAPI routers) without the copy paste. It's pretty minimal, at least.

Also, plenty of tests to make sure things are public/private as expected.

Frontend

After this PR I feel like I actually understand JS and TS now. I probably don't, but hey, let me have this. I had to make changes to the API clients, the organizer stores, and the rendered components/pages.

API Clients

I created a new set of base clients that are a smaller subset of the existing ones:

  • BaseCRUDAPIReadOnly is the CRUD API, but only the get and getAll methods (which makes it more of an R than a CRUD, but oh well)
  • New public APIs (PublicRecipeApi, PublicFoodsApi, etc) to call the new backend explore APIs
  • ExploreApi implements the new public APIs
  • PublicExploreApi extends PublicApi which requires a groupSlug and grants access to the ExploreApi

Importantly, the ExploreApi matches the signature of the existing UserApi, which comes into play with the new...

Organizer Stores

The organizer stores (e.g. useFoodStore, useTagStore, ...) use the user API, and use the useStoreActions constructor. I created public replacements for these (usePublicExploreApi, usePublicStoreActions, ...) which only implement a subset of these (the get and getAll). Since the signatures otherwise match, they work as drop-in replacements for the user API, which allows magic like this:

const tags = loggedIn.value ? useTagStore() : usePublicTagStore(props.groupSlug);

Similarly, I modified useLazyRecipes:

export const useLazyRecipes = function (publicGroupSlug: string | null = null) {
  const api = publicGroupSlug ? usePublicExploreApi(publicGroupSlug).explore : useUserApi();
  ...

Since all of the modified components/pages only use get and getAll APIs, the drop-in works without any other changes.

Pages/Components

Lastly, I had to update how the main page and some of the components work. The main page just wraps the new RecipeExplorerPage component (which is pretty much cut-paste of the home page, but dropped into a component). This is similar to how the RecipePage component works.

I tweaked a few components to reference the correct URLs (e.g. the App Bar needs to bring you to the explore home, not the logged-in home) and to prevent going to logged-in-user-only pages (e.g. I disabled clicking on categories and tags when not logged-in). I also removed most of the context menu items (and hid the context menu from the main explore page, since it's empty):
image
image

That's pretty much it. Otherwise, since we're using mostly drop-in replacements, everything else still works, like searches:
image

Oh, and I added the group explore link to the user profile page:
image
image

Unless your group is set to private:
image

Which issue(s) this PR fixes:

(REQUIRED)

Resolves #1326

Special notes for your reviewer:

(fill-in or delete this section)

My hands hurt

Testing

(fill-in or delete this section)

Pytest for backend, lots of manual frontend testing. I don't know if we want to add tests for the new APIs/stores on the frontend, but the private ones don't have tests.

Release Notes

(REQUIRED)

added public recipe browser

@michael-genson michael-genson marked this pull request as draft August 27, 2023 20:38
@michael-genson
Copy link
Collaborator Author

michael-genson commented Aug 27, 2023

I'm just going to do the cookbook stuff now ¯\_(ツ)_/¯ I also found a few small bugs

@michael-genson michael-genson marked this pull request as ready for review August 27, 2023 22:55
@michael-genson
Copy link
Collaborator Author

I created a new layout called "explore" which keeps the app sidebar, but removes the auth middleware (so you can stay logged-out). The sidebar is currently limited to only cookbooks, but that can be changed as soon as we make explore-friendly tag/category/tool pages:
image
image

To get this to work I had to move around some of the layout components (I created a "DefaultLayout" component and moved the header, sidebar, etc. into the "LayoutParts" directory). I also updated the cookbook store, added an explore cookbook API, etc. (same as everything else above)

I noticed during testing that we're getting an infinite loop of requests on these cookbook pages, but I confirmed this happens in nightly too so it's not a new bug:
image

It's probably related to #2195

@gravelfreeman
Copy link

Well this is an awesome work and hope it'll be added in the next version of Mealie.

Just to make sure I understand correctly, there's currently no way of sharing a cookbook even though the interface says so?

I can only get public links for single recipes atm.

@michael-genson
Copy link
Collaborator Author

there's currently no way of sharing a cookbook even though the interface says so?

With this change cookbooks will have public links (I didn't add a "copy link" button, but you can navigate to them through the public browser). Basically anything accessible via the new general explore page doesn't require a login

@hay-kot hay-kot merged commit 2c5e5a8 into mealie-recipes:mealie-next Sep 14, 2023
6 checks passed
@michael-genson michael-genson deleted the feat/public-recipe-browser branch September 14, 2023 14:31
@phellarv
Copy link

I'm running nightly, and I cannot see this change?
What build should I run to enable public sharing?

@michael-genson
Copy link
Collaborator Author

Are you using the old docker hub image? Check out the new docs with the GitHub image:
https://nightly.mealie.io/documentation/getting-started/installation/sqlite/

@ocangelo
Copy link

was this never included in a release?
are there other options to let users see recipes from a different group?

@michael-genson
Copy link
Collaborator Author

@ocangelo
Copy link

ocangelo commented Feb 28, 2024

i have seen that but i am still having problems.

  • I have my default group "Home" and a new "Test Group" with the same settings (Unchecked Private Group, checked "Allow users outside of your group to see your recipes").
  • users can only register through invites
  • let's say i have a "home user" in home (admin) and a "test user" in "Test Group" (normal user)
  • I have created "home recipe" in "Home" and "test recipe" in "Test Group", both public
  • if i access the website as a "non registered user" i can see "home recipe" (and cookbooks and everything from the "home" default group) but i cannot see "test recipe" or anything else i add to "Test Group"
  • if i login as "home user" i can only see "home recipe" (and other "Home Group" things)
  • if i login as "test user" i can only see "test recipe" (and other "Test Group" things)

what am i missing?

@michael-genson
Copy link
Collaborator Author

That is expected behavior, recipes are not shared between groups. Groups are fully isolated instances.

To view the recipes of another group, you need to navigate to that group's URL. Every group has a unique URL.

@arcreigh
Copy link

That is expected behavior, recipes are not shared between groups. Groups are fully isolated instances.

To view the recipes of another group, you need to navigate to that group's URL. Every group has a unique URL.

It would be nice to have a page within mealie to quickly browse between groups given users have permission to view those groups.

I for one use groups to separate my recipes from my friends. But I would still like to be able to go to my friends recipes and comment on them / be able to show that I used their recipe in my timeline.

It'd be nice to use the timeline as an internal social network to mealie to see what we are all cooking together.

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.

[v1.0.0b] [Task] - Public Group Exploration
6 participants