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

add a primary asset cover image to library pages #224

Merged
merged 4 commits into from
Oct 15, 2024

Conversation

Southclaws
Copy link
Owner

@Southclaws Southclaws commented Oct 14, 2024

This adds a separate asset relation to library pages: primary image, or "cover image" which acts as a hero section header for library pages.

This also introduces the concept of assets having version history. The need for this was because when a cover image is cropped, the original is still needed in order to facilitate future edits to the crop.

image

from discord:

this, it's a fixed height but obviously dynamic width, so you can drop an image in and move it around a bit so it's positioned nicely

hard part: because the width is dynamic, the cropped coordinates are invalid for that use-case

img has object-fit/object-position, but the coordinate space is different because it's dependent screen space coordinates not image space coordinates

so I think the only solution is to store two images, one is the original and the other is the cropped version (which can be deleted/replaced when the original is edited)

when you enter edit mode, it loads the original image and when it's rendered for reading, it loads the cropped image

So now I'm wondering if it makes sense to add a recursive backreference to Assets for change versioning :thinkthonk:

Copy link

vercel bot commented Oct 14, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
storyden ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 15, 2024 8:30pm
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
storyden-homepage ⬜️ Ignored (Inspect) Visit Preview Oct 15, 2024 8:30pm

Copy link

coderabbitai bot commented Oct 14, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

The changes in this pull request introduce a new field, PrimaryImage, to the Node struct across various files, enhancing the data structure to include a reference to a primary image. This involves updates to methods for creating, updating, and querying nodes, including the addition of new methods and modifications to existing ones. The entity-relationship diagram and migration schema have also been updated to reflect these changes. Additionally, new components and hooks for managing asset uploads and cover images have been added, improving the user interface for library pages and media assets.

Changes

File Path Change Summary
app/resources/library/mapping.go, app/resources/library/node.go, app/services/library/node_mutate/node.go, app/transports/http/bindings/nodes.go, internal/ent/node.go, internal/ent/node_create.go, internal/ent/node_update.go, internal/ent/schema/node.go Added PrimaryImage field to Node struct and updated related methods to handle this new field. Introduced QueryPrimaryImage method and updated the entity-relationship diagram to include primary_asset_id.
app/resources/library/node_querier/node_querier.go, app/resources/library/node_search/search.go Modified Get and Search methods to include WithPrimaryImage() for enhanced query capabilities.
app/resources/library/node_writer/node_writer.go Introduced WithPrimaryImage function for setting the primary asset ID in ent.NodeMutation.
internal/ent/er.html, internal/ent/migrate/schema.go Updated ERD and migration schema to include primary_asset_id field with foreign key constraints.
web/src/api/openapi-schema/nodeCommonProps.ts, web/src/api/openapi-schema/nodeInitialProps.ts, web/src/api/openapi-schema/nodeMutableProps.ts Added primary_image and primary_image_asset_id properties to NodeCommonProps, NodeInitialProps, and NodeMutableProps interfaces.
web/src/components/asset/AssetUploadAction.tsx, web/src/components/library/LibraryPageCoverImageControl/LibraryPageCoverImageControl.tsx, web/src/components/library/LibraryPageCoverImageControl/useLibraryPageCoverImageControl.ts Introduced components and hooks for managing asset uploads and cover images.
web/src/screens/library/LibraryPageScreen/LibraryPageScreen.tsx, web/src/screens/library/LibraryPageScreen/useLibraryPageScreen.ts Integrated new cover image handling functionality into LibraryPageScreen and updated associated hook for image cropping.
web/src/screens/media/MediaEditModal.tsx, web/src/screens/media/MediaEditScreen.tsx Added components for editing media assets and managing media uploads.
web/styled-system/recipes/file-upload.d.ts, web/styled-system/recipes/file-upload.mjs Modified FileUploadRecipe interface and updated fileUploadSlotNames array to include additional keys.

Possibly related PRs

🐇 In the library where images gleam,
A PrimaryImage now reigns supreme.
With queries enhanced, and uploads in play,
Our nodes shine brighter, come what may!
Hops of joy for the changes we see,
A vibrant new world, oh, how it will be! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 21

🧹 Outside diff range and nitpick comments (34)
web/src/screens/media/MediaEditModal.tsx (2)

1-1: Correct the import path casing for consistency.

The import path for ModalDrawer has inconsistent casing. It should be ModalDrawer instead of Modaldrawer to match typical React component naming conventions.

Apply this change:

-import { ModalDrawer } from "src/components/site/Modaldrawer/Modaldrawer";
+import { ModalDrawer } from "src/components/site/ModalDrawer/ModalDrawer";

6-14: Approve component structure with a minor optimization.

The MediaEditModal component is well-structured and correctly implements the modal functionality. Props are appropriately passed down to child components.

Consider removing the unnecessary fragment (<>) as there's only one child element:

 export function MediaEditModal(props: UseDisclosureProps & Props) {
   return (
-    <>
       <ModalDrawer isOpen={props.isOpen} onClose={props.onClose} title="Upload">
         <MediaEditScreen {...props} />
       </ModalDrawer>
-    </>
   );
 }
web/src/screens/media/MediaEditScreen.tsx (1)

1-5: Remove unused import.

The FileUploadFileAcceptDetails import from "@ark-ui/react" is not used in the current implementation. Consider removing it to keep the imports clean and avoid potential confusion.

-import { FileUploadFileAcceptDetails } from "@ark-ui/react";

 import { Asset } from "@/api/openapi-schema";
 import { ImageEditor } from "@/components/site/ImageEditor/ImageEditor";
 import { LStack } from "@/styled-system/jsx";
web/src/components/library/LibraryPageCoverImageControl/useLibraryPageCoverImageControl.ts (1)

1-43: Overall, the useLibraryPageCoverImageControl hook is well-implemented and aligns with the PR objectives.

The hook effectively manages cover images for library pages, providing clear and concise functionality for uploading and removing images. It follows React best practices, includes proper error handling, and ensures data consistency through revalidation. The code is clean, readable, and maintainable.

One minor suggestion for improvement:

Consider adding JSDoc comments to the hook and its returned functions to provide better documentation for consumers of this hook.

web/src/components/library/LibraryPageCoverImageControl/LibraryPageCoverImageControl.tsx (3)

11-18: Component structure looks good, consider addressing the TODO.

The component structure follows React best practices, utilizing hooks and conditional rendering effectively. The separation of logic into a custom hook is commendable.

However, there's a TODO comment about grouping options in the editing toolbar. Consider creating an issue to track this enhancement for future implementation.

Would you like me to create a GitHub issue to track the TODO for grouping editing toolbar options?


19-37: Functionality aligns with PR objectives, consider adding a loading state.

The component successfully implements the required functionality for managing cover images, allowing both removal and upload/update operations. The conditional rendering based on the existence of a cover image is well-implemented.

To enhance user experience, consider adding a loading state during image upload or removal operations. This could prevent users from triggering multiple actions while an operation is in progress.

Here's a suggestion to add a loading state:

 export function LibraryPageCoverImageControl(props: Props) {
   const {
     hasCoverImage,
+    isLoading,
     handlers: { handleUploadCoverImage, handleRemoveCoverImage },
   } = useLibraryPageCoverImageControl(props);

   return (
     <>
       {hasCoverImage && (
         <Button
           type="button"
           size="xs"
           variant="outline"
           onClick={handleRemoveCoverImage}
+          disabled={isLoading}
         >
-          <ImageMinusIcon />
+          {isLoading ? <Spinner size="sm" /> : <ImageMinusIcon />}
           remove cover
         </Button>
       )}

       <AssetUploadAction
         operation={hasCoverImage ? "update" : "add"}
         onFinish={handleUploadCoverImage}
         accept={["image/png", "image/jpeg", "image/gif"]}
+        disabled={isLoading}
       />
     </>
   );
 }

Note: You'll need to implement the isLoading state in the useLibraryPageCoverImageControl hook and add a Spinner component to your UI library.


19-37: Enhance accessibility with ARIA labels.

While the component uses standard UI components which likely have built-in accessibility features, consider adding ARIA labels to improve the experience for screen reader users.

Here's a suggestion to enhance accessibility:

 {hasCoverImage && (
   <Button
     type="button"
     size="xs"
     variant="outline"
     onClick={handleRemoveCoverImage}
+    aria-label="Remove cover image"
   >
     <ImageMinusIcon />
     remove cover
   </Button>
 )}

 <AssetUploadAction
   operation={hasCoverImage ? "update" : "add"}
   onFinish={handleUploadCoverImage}
   accept={["image/png", "image/jpeg", "image/gif"]}
+  aria-label={hasCoverImage ? "Update cover image" : "Add cover image"}
 />
web/src/api/openapi-schema/nodeMutableProps.ts (1)

30-30: LGTM: New property added correctly. Consider updating interface documentation.

The new primary_image_asset_id property is correctly added to the NodeMutableProps interface. It's optional and uses the Identifier type as expected.

Consider updating the interface documentation comment to mention this new property, especially since it's a significant addition related to the primary image feature.

internal/ent/schema/node.go (2)

27-27: LGTM! Consider adding a comment for clarity.

The addition of the primary_asset_id field is well-implemented. It's consistent with other ID fields in the schema and correctly set as optional and nillable.

Consider adding a comment to explain the purpose of this field, similar to other fields in the schema. For example:

-		field.String("primary_asset_id").GoType(xid.ID{}).Optional().Nillable(),
+		field.String("primary_asset_id").GoType(xid.ID{}).Optional().Nillable().
+			Comment("The ID of the primary asset (cover image) associated with this node"),

54-57: LGTM! Consider adding a comment for consistency.

The addition of the primary_image edge is well-implemented. It correctly corresponds to the new primary_asset_id field and is appropriately set as Unique.

For consistency with other edges in the schema, consider adding a comment to explain the purpose of this edge. For example:

 		edge.To("primary_image", Asset.Type).
 			Field("primary_asset_id").
-			Unique(),
+			Unique().
+			Comment("The primary asset (cover image) associated with this node"),
web/src/recipes/file-upload.ts (1)

68-73: Consider adding borderRadius to itemPreviewImage

The itemPreviewImage style could be enhanced by adding a borderRadius property. This would ensure that the preview image has rounded corners, which is often desirable for a polished look and consistency with other UI elements.

Consider adding the following property to the itemPreviewImage style:

 itemPreviewImage: {
   aspectRatio: "1",
   height: "10",
   objectFit: "scale-down",
   width: "10",
+  borderRadius: "md",
 },
app/resources/library/mapping.go (1)

66-79: Approved with a minor suggestion

The modified return statement correctly includes the new PrimaryImage field in the Node struct initialization. The overall structure and indentation are consistent with the rest of the code.

However, to improve readability and maintain consistency, consider grouping related fields together. For example, you might want to place PrimaryImage next to Assets since they are both related to visual content.

Here's a suggested reordering of the fields:

return &Node{
    Mark:         NewMark(c.ID, c.Slug),
    CreatedAt:    c.CreatedAt,
    UpdatedAt:    c.UpdatedAt,
    Name:         c.Name,
    Description:  opt.NewPtr(c.Description),
    Content:      richContent,
    Assets:       assets,
    PrimaryImage: primaryImage,
    WebLink:      link,
    Owner:        *pro,
    Parent:       parent,
    Nodes:        nodes,
    Visibility:   visibility,
    Metadata:     c.Metadata,
}, nil
internal/ent/er.html (2)

262-262: LGTM: Relationship correctly implements primary image association

The new relationship between Node and Asset correctly implements the association for the primary image. The relationship type (}o--o|) accurately represents that a Node can have zero or one primary image Asset.

Consider enhancing the relationship description for clarity:

-Node }o--o| Asset : "primary_image"
+Node }o--o| Asset : "primary_image (cover)"

This small change would make it immediately clear that this relationship represents the cover image mentioned in the PR objectives.


165-165: Consider handling of original and cropped cover images

The changes to the Node entity and its relationship with Asset effectively implement the primary cover image feature for library pages. The data model modifications are minimal and focused, which is good for maintainability.

However, the PR objectives mention a need to handle both original and cropped versions of the cover image. The current data model doesn't explicitly address this requirement. Consider the following options:

  1. Use the existing Asset entity's metadata field to store information about the cropped version.
  2. Create a separate CroppedAsset entity that references the original Asset.
  3. Add fields to the Asset entity to store cropping information.

Each approach has its trade-offs in terms of complexity and query performance. Please clarify how you intend to handle the original and cropped versions of the cover image in the data model.

Also applies to: 262-262

app/services/library/node_mutate/node.go (1)

Missing changes in Create and Update methods

The code diff does not show the expected modifications to the Create and Update methods. Please ensure that all intended changes, especially related to the new PrimaryImage field, are included in the diff.

🔗 Analysis chain

Line range hint 1-1: Request for information: Changes to Create and Update methods

The AI summary mentioned modifications to the Create and Update methods, but these changes are not visible in the provided code diff. Could you please provide the specific changes made to these methods? This information is crucial for a comprehensive review of the implementation of the new PrimaryImage field.

To help identify the changes, you can run the following command:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find changes in Create and Update methods

rg -U 'func \(s \*service\) (Create|Update)' app/services/library/node_mutate/node.go -A 20 -B 5

Length of output: 1478

app/transports/http/bindings/nodes.go (3)

90-91: LGTM: Primary image handling added

The addition of PrimaryImage handling is consistent with the PR objectives. Good use of opt.Map and opt.NewPtr for optional value handling.

Consider adding a comment explaining the purpose of this new field for better code readability.


235-236: LGTM: Primary image handling added to node update

The addition of PrimaryImage handling in the NodeUpdate function is consistent with the changes in NodeCreate. This allows for proper modification of the primary image during updates.

For consistency, consider grouping the PrimaryImage field with other similar fields in the node_mutate.Partial struct initialization.

Also applies to: 244-244


350-360: LGTM: Primary image added to node serialization

The addition of the PrimaryImage field to both serialiseNode and serialiseNodeWithItems functions ensures that the primary image is included in the serialized output of nodes. The use of opt.Map and .Ptr() is consistent with the handling of other optional fields.

Consider grouping similar fields together in the struct initialization for better code organization. For example, you could place PrimaryImage next to Assets or Content.

Also applies to: 372-382

internal/ent/mutation.go (5)

14494-14495: LGTM with a minor suggestion.

The addition of primary_image and clearedprimary_image fields aligns with the PR objective. However, consider using camelCase for field names to maintain consistency with Go naming conventions.

Consider renaming:

-	primary_image        *xid.ID
-	clearedprimary_image bool
+	primaryImage        *xid.ID
+	clearedPrimaryImage bool

14995-15042: LGTM with suggestions for improvement.

The new methods for managing the primary asset ID are well-implemented and follow the existing patterns in the codebase. However, there are a few areas for improvement:

  1. Naming Convention: Some method names use snake_case instead of the Go-standard camelCase.
  2. Error Handling: The OldPrimaryAssetID method could benefit from more specific error types.

Consider the following improvements:

  1. Rename methods to follow Go conventions:
-func (m *NodeMutation) SetPrimaryAssetID(x xid.ID) {
+func (m *NodeMutation) SetPrimaryAssetID(x xid.ID) {
-func (m *NodeMutation) PrimaryAssetID() (r xid.ID, exists bool) {
+func (m *NodeMutation) PrimaryAssetID() (r xid.ID, exists bool) {
-func (m *NodeMutation) OldPrimaryAssetID(ctx context.Context) (v *xid.ID, err error) {
+func (m *NodeMutation) OldPrimaryAssetID(ctx context.Context) (v *xid.ID, err error) {
  1. Consider using custom error types for more specific error handling in OldPrimaryAssetID:
var (
    ErrNotUpdateOperation = errors.New("operation is not UpdateOne")
    ErrMissingIDField     = errors.New("missing ID field in the mutation")
)

func (m *NodeMutation) OldPrimaryAssetID(ctx context.Context) (v *xid.ID, err error) {
    if !m.op.Is(OpUpdateOne) {
        return v, ErrNotUpdateOperation
    }
    if m.id == nil || m.oldValue == nil {
        return v, ErrMissingIDField
    }
    // ... rest of the method
}

15312-15350: LGTM with minor naming convention suggestions.

The new methods for managing the "primary_image" edge are well-implemented and follow the existing patterns in the codebase. The comment explaining the purpose of PrimaryImageIDs is particularly helpful.

Consider renaming some methods to follow Go's camelCase convention:

-func (m *NodeMutation) SetPrimaryImageID(id xid.ID) {
+func (m *NodeMutation) SetPrimaryImageID(id xid.ID) {
-func (m *NodeMutation) ClearPrimaryImage() {
+func (m *NodeMutation) ClearPrimaryImage() {
-func (m *NodeMutation) PrimaryImageCleared() bool {
+func (m *NodeMutation) PrimaryImageCleared() bool {
-func (m *NodeMutation) PrimaryImageID() (id xid.ID, exists bool) {
+func (m *NodeMutation) PrimaryImageID() (id xid.ID, exists bool) {
-func (m *NodeMutation) PrimaryImageIDs() (ids []xid.ID) {
+func (m *NodeMutation) PrimaryImageIDs() (ids []xid.ID) {
-func (m *NodeMutation) ResetPrimaryImage() {
+func (m *NodeMutation) ResetPrimaryImage() {

16133-16135: LGTM with a minor suggestion.

The new condition to check if the primary image edge has been cleared is implemented correctly. It follows the existing pattern of checking a boolean flag and appending the edge to the edges slice if cleared.

Consider renaming the flag to follow Go's camelCase convention:

-	if m.clearedprimary_image {
-		edges = append(edges, node.EdgePrimaryImage)
-	}
+	if m.clearedPrimaryImage {
+		edges = append(edges, node.EdgePrimaryImage)
+	}

16164-16165: LGTM with a minor suggestion.

The new case for the primary image edge in the EdgeCleared method is implemented correctly. It follows the existing pattern of returning the corresponding cleared flag.

Consider renaming the flag to follow Go's camelCase convention:

-	case node.EdgePrimaryImage:
-		return m.clearedprimary_image
+	case node.EdgePrimaryImage:
+		return m.clearedPrimaryImage
app/transports/http/openapi/server_gen.go (1)

1524-1526: LGTM: Addition of PrimaryImageAssetId field with a minor suggestion

The addition of the PrimaryImageAssetId field is consistent with the PR's objective. The use of *AssetID allows for optional primary image IDs, which is appropriate.

Consider using camelCase for the field name to be consistent with Go naming conventions:

- PrimaryImageAssetId *AssetID `json:"primary_image_asset_id,omitempty"`
+ PrimaryImageAssetID *AssetID `json:"primary_image_asset_id,omitempty"`
web/src/components/ui/file-upload.tsx (2)

83-86: Consider the necessity of re-exporting Context and HiddenInput

In lines 83-86, you are re-exporting FileUploadContext as Context and FileUploadHiddenInput as HiddenInput from "@ark-ui/react/file-upload". Ensure that re-exporting these components aligns with your module's public API and that they are intended for external use. If these are internal components, it might be better to avoid exporting them to keep the API surface minimal.


15-81: Consider adding JSDoc comments to exported components for better documentation

The exported components RootProvider, Root, Dropzone, ItemDeleteTrigger, ItemGroup, ItemName, ItemPreviewImage, ItemPreview, Item, ItemSizeText, Label, and Trigger are key parts of your UI library. Adding JSDoc comments to these components would improve code readability and provide better documentation for other developers using them.

web/src/lib/library/library.ts (1)

170-172: Ensure deep clone when updating primary_image

When creating newNode, you are using a shallow copy of data with { ...data, primary_image: asset }. If data contains nested objects, mutations to these nested objects elsewhere could affect this cached data unintentionally. Consider performing a deep clone to prevent unintended side effects.

Apply the following change to use a deep cloning method:

-    const newNode = { ...data, primary_image: asset };
+    const newNode = { ...structuredClone(data), primary_image: asset };

Alternatively, if the environment does not support structuredClone, you can use JSON.parse(JSON.stringify(data)) or a library like lodash.cloneDeep.

web/src/screens/library/LibraryPageScreen/LibraryPageScreen.tsx (4)

104-107: Address the TODO: Implement custom icons or emojis for pages

The TODO comment suggests adding custom icons or emojis for pages. Implementing this feature could enhance the user experience by allowing more personalization.

Would you like assistance in developing this feature or opening a GitHub issue to track its progress?


109-112: Address the TODO: Enable import from other sources

There's a TODO comment indicating the need to implement importing from other sources. Adding this functionality could improve the usability of the library by allowing users to bring in content from external platforms.

Would you like help in implementing this feature or creating a GitHub issue to track it?


123-123: Address the TODO: Remove black background when image is empty

The TODO comment highlights the need to remove the black background when no image is present in the FixedCropper. Implementing this would improve the visual appeal when the cover image is not set.

Would you like assistance in resolving this issue or opening a GitHub issue to track it?


88-91: Simplify code by removing unnecessary fragment

The fragment (<> </>) wrapping the <EditAction> component is unnecessary since there is only one child element. Removing it can simplify the code.

Apply this diff to remove the unnecessary fragment:

                 <>
                     <EditAction onClick={handleEditMode}>Edit</EditAction>
-                </>
web/src/screens/library/LibraryPageScreen/useLibraryPageScreen.ts (1)

234-236: Address the TODO: Delete the original asset when updating the cover image

The TODO indicates that the original cover image asset should be deleted when a new one is uploaded. Implementing this will prevent accumulation of unused assets and ensure efficient storage management.

Would you like assistance in implementing the deletion of the original asset, or should I open a new GitHub issue to track this task?

internal/ent/node_update.go (2)

166-185: Maintain Consistent Documentation and Ordering for New Methods

The newly added methods for managing the "primary_asset_id" field are correctly implemented. To maintain consistency with the rest of the codebase:

  • Ensure that documentation comments follow the same style as existing methods.
  • Consider placing these methods adjacent to other similar field methods for better organization.

1101-1120: Ensure Consistency in NodeUpdateOne Method Documentation

The methods for handling "primary_asset_id" in NodeUpdateOne are implemented correctly. For consistency:

  • Align the documentation comments with the existing style.
  • Place the methods logically within the code to match the structure of similar methods.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d74dd87 and db2fcdb.

⛔ Files ignored due to path filters (4)
  • api/openapi.yaml is excluded by !**/*.yaml
  • go.sum is excluded by !**/*.sum, !**/*.sum
  • web/package.json is excluded by !**/*.json
  • web/yarn.lock is excluded by !**/yarn.lock, !**/*.lock, !**/*.lock
📒 Files selected for processing (35)
  • app/resources/library/mapping.go (1 hunks)
  • app/resources/library/node.go (1 hunks)
  • app/resources/library/node_querier/node_querier.go (1 hunks)
  • app/resources/library/node_search/search.go (1 hunks)
  • app/resources/library/node_writer/node_writer.go (1 hunks)
  • app/services/library/node_mutate/node.go (2 hunks)
  • app/transports/http/bindings/nodes.go (4 hunks)
  • app/transports/http/openapi/server_gen.go (6 hunks)
  • internal/ent/client.go (1 hunks)
  • internal/ent/er.html (2 hunks)
  • internal/ent/migrate/schema.go (3 hunks)
  • internal/ent/mutation.go (20 hunks)
  • internal/ent/node.go (14 hunks)
  • internal/ent/node/node.go (7 hunks)
  • internal/ent/node/where.go (3 hunks)
  • internal/ent/node_create.go (6 hunks)
  • internal/ent/node_query.go (8 hunks)
  • internal/ent/node_update.go (8 hunks)
  • internal/ent/schema/node.go (2 hunks)
  • web/panda.config.ts (2 hunks)
  • web/src/api/openapi-schema/nodeCommonProps.ts (2 hunks)
  • web/src/api/openapi-schema/nodeInitialProps.ts (2 hunks)
  • web/src/api/openapi-schema/nodeMutableProps.ts (2 hunks)
  • web/src/components/asset/AssetUploadAction.tsx (1 hunks)
  • web/src/components/library/LibraryPageCoverImageControl/LibraryPageCoverImageControl.tsx (1 hunks)
  • web/src/components/library/LibraryPageCoverImageControl/useLibraryPageCoverImageControl.ts (1 hunks)
  • web/src/components/ui/file-upload.tsx (1 hunks)
  • web/src/lib/library/library.ts (6 hunks)
  • web/src/recipes/file-upload.ts (1 hunks)
  • web/src/screens/library/LibraryPageScreen/LibraryPageScreen.tsx (3 hunks)
  • web/src/screens/library/LibraryPageScreen/useLibraryPageScreen.ts (6 hunks)
  • web/src/screens/media/MediaEditModal.tsx (1 hunks)
  • web/src/screens/media/MediaEditScreen.tsx (1 hunks)
  • web/styled-system/recipes/file-upload.d.ts (1 hunks)
  • web/styled-system/recipes/file-upload.mjs (1 hunks)
🔥 Files not summarized due to errors (2)
  • app/transports/http/openapi/server_gen.go: Error: Server error: no LLM provider could handle the message
  • internal/ent/mutation.go: Error: Server error: no LLM provider could handle the message
🧰 Additional context used
🪛 Biome
web/src/components/asset/AssetUploadAction.tsx

[error] 66-66: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (89)
web/src/screens/media/MediaEditModal.tsx (1)

1-14: Overall assessment: Well-implemented modal component for media editing.

This new MediaEditModal component aligns well with the PR objective of adding a primary asset cover image to library pages. It provides a clean interface for media editing within a modal context. The component is well-structured, uses appropriate props, and follows React best practices.

A few minor optimizations have been suggested, but overall, this implementation looks good and should effectively serve its purpose in the larger context of the PR.

web/src/screens/media/MediaEditScreen.tsx (2)

7-10: Props type definition looks good.

The Props type is well-defined with appropriate types for initialValue and onFinish. The optional initialValue allows the component to be used for both creating new assets and editing existing ones.


16-28: Component structure looks good.

The MediaEditScreen component is well-structured. It correctly handles the upload process by calling the onFinish prop and renders the ImageEditor component with the necessary props. The use of LStack for layout is appropriate.

web/src/components/library/LibraryPageCoverImageControl/useLibraryPageCoverImageControl.ts (6)

1-3: Imports look good and are well-organized.

The necessary dependencies are imported, and there are no unused imports. The organization is logical, separating internal functions, types, and custom hooks.


5-7: Props type definition is clear and appropriate.

The Props type is well-defined, exporting a single node property of type Node. This aligns with the PR objectives and provides a clear interface for the hook.


9-12: Hook setup and state management are well-implemented.

The hook is correctly defined and uses useLibraryMutation to obtain necessary functions. The hasCoverImage state is derived efficiently from the node's primary_image property.


14-23: handleUploadCoverImage function is well-structured and handles errors appropriately.

The function effectively manages the cover image upload process, using handle for error management and including a cleanup step to revalidate data. This implementation aligns well with the PR objectives.


25-34: handleRemoveCoverImage function is consistent with handleUploadCoverImage and effectively removes the cover image.

The function maintains a consistent structure with handleUploadCoverImage, properly handles errors, and includes a cleanup step. It correctly removes the cover image by passing undefined to updateNodeCoverImage.


36-42: Return statement is well-structured and provides all necessary data and functions.

The hook returns a clear object structure with hasCoverImage and an object of handler functions. This return value aligns well with the PR objectives and allows for easy consumption of the hook's functionality.

web/src/components/library/LibraryPageCoverImageControl/LibraryPageCoverImageControl.tsx (1)

1-9: LGTM: Imports are well-organized.

The imports are properly structured, with external imports followed by internal imports. This organization enhances code readability and maintainability.

web/src/api/openapi-schema/nodeInitialProps.ts (3)

12-12: LGTM: Import statement for Identifier type.

The new import statement for the Identifier type is correctly added and follows the existing import pattern in the file.


Line range hint 1-31: Verify changes in the source of truth for this auto-generated file.

This file appears to be auto-generated, as indicated by the comment at the top. While the changes look good, it's crucial to ensure that these modifications are made in the source of truth (e.g., API schema, OpenAPI specification) that generates this file, rather than editing it directly.

Please confirm that the corresponding changes have been made in the source that generates this file. This ensures that the changes persist across future generations of this file.


27-27: LGTM: New property for primary image asset.

The new primary_image_asset_id property is correctly added to the NodeInitialProps interface. It's optional and uses the Identifier type, which aligns with the PR objectives.

Let's verify the usage of this new property in the codebase:

✅ Verification successful

Verified: primary_image_asset_id is correctly integrated across the codebase.

  • The primary_image_asset_id property is properly added and utilized in:
    • web/src/lib/library/library.ts
    • web/src/api/openapi-schema/nodeMutableProps.ts
    • web/src/api/openapi-schema/nodeInitialProps.ts
    • web/src/api/openapi-schema/eventMutableProps.ts
    • web/src/api/openapi-schema/eventInitialProps.ts
    • web/src/api/openapi-schema/nodeCreateBody.ts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of the new primary_image_asset_id property

# Test: Search for usages of primary_image_asset_id
rg --type typescript -A 5 'primary_image_asset_id'

# Test: Check if there are any places where NodeInitialProps is used that might need updating
rg --type typescript -A 5 'NodeInitialProps'

Length of output: 165


Script:

#!/bin/bash
# Description: Retry verifying the usage of the new primary_image_asset_id property with correct type flag

# Test: Search for usages of primary_image_asset_id
rg --type ts -A 5 'primary_image_asset_id'

# Test: Check if there are any places where NodeInitialProps is used that might need updating
rg --type ts -A 5 'NodeInitialProps'

Length of output: 2553

web/src/api/openapi-schema/nodeMutableProps.ts (2)

12-12: LGTM: New import statement is correct and necessary.

The new import statement for the Identifier type is correctly added and is necessary for the new property in the NodeMutableProps interface.


Line range hint 1-34: Verify changes in auto-generated file.

This file appears to be auto-generated based on the comment at the top. While the changes are correct and necessary for the new feature, it's important to ensure that these modifications are made through the proper generation process rather than manual edits.

Please confirm that the changes to this file are the result of updates to the OpenAPI schema or the generation process, rather than direct manual edits.

web/styled-system/recipes/file-upload.d.ts (1)

19-19: ⚠️ Potential issue

Remove duplicate keys in the Record type

The return type of the function in the FileUploadRecipe interface contains repeated keys in the Record type. This repetition is unnecessary and may lead to confusion or potential issues in type checking.

Consider simplifying the return type by removing the duplicate keys. Here's a suggested fix:

-(props?: FileUploadVariantProps): Pretty<Record<"root" | "dropzone" | "item" | "itemDeleteTrigger" | "itemGroup" | "itemName" | "itemPreview" | "itemPreviewImage" | "itemSizeText" | "label" | "trigger" | "root" | "dropzone" | "item" | "itemDeleteTrigger" | "itemGroup" | "itemName" | "itemPreview" | "itemPreviewImage" | "itemSizeText" | "label" | "trigger" | "root" | "dropzone" | "item" | "itemDeleteTrigger" | "itemGroup" | "itemName" | "itemPreview" | "itemPreviewImage" | "itemSizeText" | "label" | "trigger", string>>
+(props?: FileUploadVariantProps): Pretty<Record<"root" | "dropzone" | "item" | "itemDeleteTrigger" | "itemGroup" | "itemName" | "itemPreview" | "itemPreviewImage" | "itemSizeText" | "label" | "trigger", string>>

This change maintains the intended functionality while improving code clarity and reducing potential confusion.

To ensure this change doesn't affect other parts of the codebase, we can run the following script:

This will help us verify if there are any dependencies on the specific structure of the return type that might be affected by this change.

✅ Verification successful

Adjust the search command to target TypeScript files explicitly

The previous search using rg --type typescript failed because the typescript file type was not recognized. Let's search for FileUploadRecipe within .ts and .tsx files instead.


Duplicate keys in Record type can be safely removed

The duplicate keys in the Record type within web/styled-system/recipes/file-upload.d.ts are only present in this file and do not affect other parts of the codebase. Removing the duplicates will improve code clarity without introducing any issues.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for usage of FileUploadRecipe interface

# Test: Search for FileUploadRecipe usage
rg --type typescript "FileUploadRecipe" -C 5

Length of output: 79


Script:

#!/bin/bash
# Description: Check for usage of FileUploadRecipe interface in TypeScript files

# Test: Search for FileUploadRecipe usage in .ts and .tsx files
rg "FileUploadRecipe" --glob "*.ts" --glob "*.tsx" -C 5

Length of output: 1954

web/src/api/openapi-schema/nodeCommonProps.ts (1)

10-10: LGTM! The changes align with the PR objectives.

The addition of the primary_image property to the NodeCommonProps interface correctly implements the feature for adding a primary asset cover image to library pages. The property is appropriately typed as Asset and marked as optional.

However, please note that this file appears to be auto-generated. Ensure that any changes to this file are made through the appropriate generation process rather than direct edits.

Let's verify the impact of this change on the rest of the codebase:

Also applies to: 35-35

✅ Verification successful

Verified! The addition of primary_image is properly integrated across the codebase.

All instances where NodeCommonProps is used have been appropriately updated to handle the new primary_image property. The optional nature of the property is respected, ensuring that existing functionality remains unaffected.

No issues were found related to this change.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for usage of NodeCommonProps and potential areas that might need updates

# Search for files using NodeCommonProps
echo "Files using NodeCommonProps:"
rg --type typescript "NodeCommonProps"

# Search for places where node properties are accessed, which might need to handle the new primary_image
echo "\nPotential places to update for primary_image handling:"
rg --type typescript "node\.(assets|content|description|link|meta|name|owner|parent|relevance_score|slug|visibility)"

Length of output: 418


Script:

#!/bin/bash
# Description: Corrected script to check for usage of NodeCommonProps and potential areas that might need updates

# Search for files using NodeCommonProps
echo "Files using NodeCommonProps:"
rg "NodeCommonProps" --glob "*.ts" --glob "*.tsx"

# Search for places where node properties are accessed, which might need to handle the new primary_image
echo "\nPotential places to update for primary_image handling:"
rg "node\.(assets|content|description|link|meta|name|owner|parent|relevance_score|slug|visibility)" --glob "*.ts" --glob "*.tsx"

Length of output: 5905


Script:

#!/bin/bash
# Description: Check for usage of the new 'primary_image' property across the codebase

# Search for all occurrences of 'primary_image'
echo "Files using 'primary_image':"
rg "primary_image" --glob "*.ts" --glob "*.tsx"

Length of output: 1534

app/resources/library/node_search/search.go (1)

73-73: LGTM! Consider verifying query performance.

The addition of WithPrimaryImage() aligns well with the PR objective of adding a primary image to library pages. This change will fetch the primary image data along with the node data, which is efficient.

To ensure this addition doesn't negatively impact query performance, especially for large datasets, consider running the following verification:

If these tests reveal any issues or missing optimizations, consider addressing them before merging.

web/src/recipes/file-upload.ts (3)

1-2: LGTM: Imports are appropriate

The imports for fileUploadAnatomy and defineSlotRecipe are correctly specified and relevant to the file's purpose.


4-7: LGTM: Component definition is well-structured

The fileUpload component is correctly defined using defineSlotRecipe. The use of fileUploadAnatomy.keys() for slots ensures completeness, and the className "fileUpload" is appropriate.


8-74: LGTM: Comprehensive and well-structured styles

The base styles for the file upload component are thorough and well-organized. The use of semantic color tokens, layout properties, and typography settings creates a cohesive and maintainable style structure. The grid layout for the item slot is particularly well-defined, and the addition of an animation for items is a nice touch for user experience.

app/resources/library/node.go (1)

42-42: LGTM! Verify handling in related methods.

The addition of the PrimaryImage field to the Node struct aligns well with the PR objective of adding a primary asset cover image to library pages. The use of opt.Optional[asset.Asset] is appropriate, allowing the field to be optional.

To ensure comprehensive implementation, please verify that related methods (e.g., creation, updating, and querying of nodes) handle this new field appropriately. Run the following script to check for potential places where updates might be needed:

✅ Verification successful

Further Verification Needed: Search for PrimaryImage Handling

To ensure that the new PrimaryImage field is properly handled across the codebase, please run the following scripts:


PrimaryImage Field Integration Verified

The new PrimaryImage field has been successfully integrated across the codebase. Key observations include:

  1. Proper implementation in data models (Event, Link, Node entities).
  2. CRUD operations (Create, Read, Update, Delete) for the field are in place.
  3. Query methods include the field for eager loading and filtering.
  4. API bindings correctly handle serialization and deserialization.
  5. Consistent implementation across different parts of the codebase.

The addition of the PrimaryImage field appears to be comprehensive and well-integrated, with no apparent issues or missing implementations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find potential places where the new PrimaryImage field might need to be handled

# Search for Node struct usage in method signatures
echo "Potential methods that might need updates:"
rg --type go -e "func.*\(.*Node.*\)" -g '!app/resources/library/node.go'

# Search for Node struct field access
echo "\nPotential places accessing Node fields:"
rg --type go -e "Node\.\w+" -g '!app/resources/library/node.go'

Length of output: 175790


Script:

#!/bin/bash
# Description: Find all usages and handling of the PrimaryImage field

# Search for direct references to PrimaryImage
echo "Searching for 'PrimaryImage' usages in the codebase:"
rg --type go -e "PrimaryImage"

# Search for methods that set or get PrimaryImage
echo "\nSearching for methods related to 'PrimaryImage':"
rg --type go -e "SetPrimaryImage|GetPrimaryImage|UpdatePrimaryImage|PrimaryImageOrErr"

# Check for serialization/deserialization involving PrimaryImage
echo "\nSearching for serialization/deserialization of 'PrimaryImage':"
rg --type go -e "PrimaryImage\s*[:=]"

Length of output: 44447

app/resources/library/mapping.go (1)

56-58: LGTM: Proper handling of optional PrimaryImage

The new code block correctly maps the optional PrimaryImage edge to an asset.Asset. The use of opt.Map is appropriate for handling this optional field, and the conversion from ent.Asset to asset.Asset using asset.FromModel is consistent with the rest of the codebase.

app/resources/library/node_querier/node_querier.go (1)

56-56: LGTM! Consider verifying query performance.

The addition of WithPrimaryImage() to the query chain aligns well with the PR objective of adding a primary asset cover image to library pages. This enhancement will allow the Get method to retrieve the primary image associated with the node.

To ensure this change doesn't negatively impact query performance, especially for nodes without a primary image, consider running performance tests. You can use the following script to analyze the query execution time:

Don't forget to update any relevant documentation or comments to reflect this new functionality, if necessary.

app/resources/library/node_writer/node_writer.go (1)

76-80: LGTM: New WithPrimaryImage function implemented correctly

The new WithPrimaryImage function is well-implemented and follows the existing pattern of option functions in this file. It correctly sets the primary asset ID using the SetPrimaryAssetID method of the ent.NodeMutation.

internal/ent/er.html (1)

165-165: LGTM: New field aligns with PR objectives

The addition of the primary_asset_id field to the Node entity is a good implementation of the PR's main objective. This field will allow library pages to have a designated primary image, which can serve as the cover image or hero section header.

app/services/library/node_mutate/node.go (2)

47-47: LGTM: Addition of PrimaryImage field

The new PrimaryImage field of type opt.Optional[asset.AssetID] is a good addition to the Partial struct. It aligns with the PR objective of introducing a cover image for library pages and allows for optional updates.


65-65: LGTM: Update to Opts method

The addition of p.PrimaryImage.Call(func(value xid.ID) { opts = append(opts, node_writer.WithPrimaryImage(value)) }) is correct and consistent with the handling of other fields in the Opts method. It ensures that the PrimaryImage option is added only when a value is present.

web/panda.config.ts (2)

14-14: New file upload functionality added

The import of fileUpload from @/recipes/file-upload indicates the introduction of new file upload functionality. This aligns well with the PR objective of adding a primary asset cover image to library pages.


249-249: File upload recipe integrated into theme configuration

The fileUpload recipe has been added to the slotRecipes section of the theme configuration. This ensures that the new file upload component will have consistent styling with the rest of the application.

To ensure the fileUpload recipe is properly implemented, let's verify its existence and structure:

✅ Verification successful

File upload recipe is properly implemented and integrated into the theme configuration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the file-upload recipe exists and examine its content
cat src/recipes/file-upload.ts

Length of output: 91


Script:

#!/bin/bash
# Search for files related to 'file-upload' in the repository
fd 'file-upload' --type f

Length of output: 179


Script:

#!/bin/bash
# Search for occurrences of 'fileUpload' in the codebase
rg 'fileUpload' .

Length of output: 4571


Script:

#!/bin/bash
# Display the contents of the correct file-upload recipe
cat web/src/recipes/file-upload.ts

Length of output: 1668

app/transports/http/bindings/nodes.go (2)

97-97: LGTM: PrimaryImage field added to node creation

The addition of the PrimaryImage field to the node_mutate.Partial struct is consistent and necessary for including the primary image in the node creation process.


Line range hint 1-424: Overall assessment: Primary image implementation looks good

The changes in this file consistently implement the addition of a primary image field across node creation, update, and serialization functions. The modifications align well with the PR objectives and maintain the existing code style and patterns. Good job on maintaining consistency throughout the file.

To ensure the changes are comprehensive, let's verify if there are any other node-related functions that might need to include the PrimaryImage field:

This will help us confirm that all relevant functions have been updated to handle the new PrimaryImage field.

✅ Verification successful

To ensure that the PrimaryImage field is consistently handled across all node-related functions, let's verify its usage within each identified function.


Verification Completed: PrimaryImage Field is Consistently Implemented

The PrimaryImage field is properly managed across all node-related functions in app/transports/http/bindings/nodes.go. All relevant functions handle PrimaryImage appropriately, ensuring consistency and alignment with the PR objectives.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other node-related functions that might need to include PrimaryImage
rg --type go -i 'func.*node' app/transports/http/bindings/nodes.go

Length of output: 1872


Script:

#!/bin/bash
# Search for usages of PrimaryImage within node-related functions in nodes.go
rg --type go 'PrimaryImage' app/transports/http/bindings/nodes.go

Length of output: 436

internal/ent/node/where.go (3)

104-107: LGTM: New PrimaryAssetID function is correctly implemented.

The new PrimaryAssetID function follows the established pattern for equality check predicates in this file. It's consistent with the existing code style and correctly implements the predicate for the "primary_asset_id" field.


674-752: LGTM: Comprehensive predicate functions added for primary_asset_id.

The new predicate functions for primary_asset_id are well-implemented and provide a comprehensive set of query operations. They follow the established patterns in the file and cover equality, inequality, range checks, containment, prefix/suffix matching, null checks, and case-insensitive comparisons. This addition enhances the querying capabilities for the Node entity.


933-954: LGTM: Edge predicates for primary_image are correctly implemented.

The new edge predicates HasPrimaryImage and HasPrimaryImageWith for the "primary_image" relationship are well-implemented. They follow the established patterns for edge predicates in this file, correctly setting up the M2O relationship and allowing for additional predicates on the related Asset entity. This addition enables efficient querying of Node entities based on their primary image relationship.

internal/ent/client.go (1)

3539-3553: New method QueryPrimaryImage added to NodeClient

The addition of the QueryPrimaryImage method to the NodeClient struct is consistent with the PR objectives and follows the established pattern for edge query methods in this generated file. This new method will allow querying the primary image associated with a Node entity, which is crucial for implementing the cover image feature for library pages.

internal/ent/mutation.go (16)

15629-15629: LGTM: Field count updated correctly.

The increase in the field count from 12 to 13 correctly reflects the addition of the primary image field.


15657-15659: LGTM: Primary asset ID field correctly added.

The addition of the primary asset ID field to the fields list is implemented correctly, following the existing pattern of checking for non-nil values before appending.


15695-15696: LGTM: Primary asset ID case added correctly.

The new case for the primary asset ID field in the switch statement is implemented correctly, following the existing pattern of returning the corresponding method result.


15730-15731: LGTM: Old primary asset ID case added correctly.

The new case for retrieving the old primary asset ID in the switch statement is implemented correctly, following the existing pattern of returning the corresponding method result with context.


15810-15816: LGTM: Primary asset ID setter case added correctly.

The new case for setting the primary asset ID field is implemented correctly. It includes proper type assertion and error handling, following the existing pattern used for other fields.


15880-15882: LGTM: Primary asset ID field clearing check added correctly.

The new condition to check if the primary asset ID field has been cleared is implemented correctly. It follows the existing pattern of using m.FieldCleared() and appending the field to the fields slice if cleared.


15915-15917: LGTM: Primary asset ID field clearing case added correctly.

The new case for clearing the primary asset ID field is implemented correctly. It follows the existing pattern of calling the corresponding clear method and returning nil.


15959-15961: LGTM: Primary asset ID field reset case added correctly.

The new case for resetting the primary asset ID field is implemented correctly. It follows the existing pattern of calling the corresponding reset method and returning nil.


15977-15977: LGTM: Edges slice capacity updated correctly.

The increase in the initial capacity of the edges slice from 8 to 9 correctly accounts for the addition of the new primary image edge.


15987-15989: LGTM: Primary image edge check added correctly.

The new condition to check if the primary image has been set is implemented correctly. It follows the existing pattern of checking for non-nil values and appending the edge to the edges slice if set.


16026-16029: LGTM: Primary image edge case added correctly in EdgeIDs method.

The new case for the primary image edge in the EdgeIDs method is implemented correctly. It follows the existing pattern for singular edges, checking for a non-nil value and returning it as a slice of ent.Value.


16064-16064: LGTM: Removed edges slice capacity updated correctly.

The increase in the initial capacity of the edges slice from 8 to 9 in the RemovedEdges() method correctly accounts for the addition of the new primary image edge.


16123-16123: LGTM: Cleared edges slice capacity updated correctly.

The increase in the initial capacity of the edges slice from 8 to 9 in the ClearedEdges() method correctly accounts for the addition of the new primary image edge.


16190-16192: LGTM: Primary image edge clearing case added correctly.

The new case for clearing the primary image edge is implemented correctly. It follows the existing pattern of calling the corresponding clear method and returning nil.


16213-16215: LGTM: Primary image edge reset case added correctly.

The new case for resetting the primary image edge is implemented correctly. It follows the existing pattern of calling the corresponding reset method and returning nil.


Line range hint 1-16215: Overall assessment: LGTM with minor suggestions.

The changes to add the primary image field and edge to the Node entity are well-implemented and consistent with the existing patterns in the codebase. All necessary methods and cases have been added or updated correctly.

Key points:

  1. The primary image field and associated methods are correctly implemented.
  2. Edge-related methods are updated to include the new primary image edge.
  3. Some minor naming convention issues were identified, mainly related to using snake_case instead of camelCase for some variable and method names.

These changes align well with the PR objective of adding a primary asset cover image to library pages. The implementation appears to be thorough and should integrate smoothly with the existing codebase.

app/transports/http/openapi/server_gen.go (4)

1451-1452: LGTM: Addition of PrimaryImage field

The addition of the PrimaryImage field of type *Asset is consistent with the PR's objective of adding a primary asset cover image to library pages. The use of a pointer and the omitempty JSON tag is appropriate for optional primary images.


1491-1492: Clarify the need for duplicate PrimaryImage field

This segment is identical to the previous addition of the PrimaryImage field. While this might be intentional for different structs or contexts, it would be helpful to clarify why this duplication is necessary. Could you provide more context on where these fields are being added and if there's a possibility for refactoring to avoid duplication?

#!/bin/bash
# Search for struct definitions containing the PrimaryImage field
rg --type go -A 10 -B 5 'type \w+ struct \{(?:\n.*?)+PrimaryImage.*?\n'

1566-1568: Clarify the need for duplicate PrimaryImageAssetId field

This segment is identical to the previous addition of the PrimaryImageAssetId field. As with the earlier duplication of PrimaryImage, it would be helpful to understand why this duplication is necessary. Could you provide more context on where these fields are being added and if there's a possibility for refactoring to avoid duplication?

#!/bin/bash
# Search for struct definitions containing the PrimaryImageAssetId field
rg --type go -A 10 -B 5 'type \w+ struct \{(?:\n.*?)+PrimaryImageAssetId.*?\n'

27732-27919: 🛠️ Refactor suggestion

Clarify the purpose of the large string block and consider alternatives

This large block of strings appears to be part of a GetSwagger function, possibly containing encoded or compressed swagger specification data. While this might be necessary, it raises some concerns:

  1. The purpose and necessity of this change are not immediately clear.
  2. Such a large block of inline strings can make the code harder to read and maintain.

Could you provide more context on why this change is needed?

Consider the following alternatives to improve code readability and maintainability:

  1. Store the swagger specification in a separate file and read it at runtime.
  2. If the data must be embedded, use a tool like go-bindata to generate Go code from the swagger file.
  3. If the current approach must be kept, add comments explaining the purpose and format of this data.
web/src/lib/library/library.ts (2)

236-236: Include updateNodeCoverImage in returned mutations

Adding updateNodeCoverImage to the returned object makes the function available for use elsewhere in the application, aligning with the intended functionality of updating a node's cover image.


177-179: Verify handling of undefined primary_image_asset_id in API

When asset is undefined, primary_image_asset_id will also be undefined. Ensure that the nodeUpdate API correctly interprets this to remove the cover image without causing errors. This is important for cases where a user wants to unset the cover image.

Run the following script to confirm that passing undefined successfully removes the cover image:

Check the response to ensure that the primary_image field is now null and no errors are returned.

internal/ent/node.go (7)

43-44: Adding PrimaryAssetID field to Node struct

The addition of the PrimaryAssetID *xid.ID field correctly introduces a reference to the primary asset within the Node struct, allowing nodes to associate with a primary image asset as intended.


65-66: Adding PrimaryImage edge to NodeEdges struct

The new PrimaryImage *Asset field in the NodeEdges struct properly defines the edge for the primary image relationship, enabling efficient querying and loading of the primary image associated with a node.


81-81: Updating loadedTypes array size

Increasing the size of the loadedTypes array to [10]bool correctly accommodates the new PrimaryImage edge, ensuring proper tracking of loaded edge types during eager loading.


115-125: Implementing PrimaryImageOrErr method

The PrimaryImageOrErr method is correctly implemented to retrieve the PrimaryImage edge or return appropriate errors if it's not loaded or not found. The use of e.loadedTypes[3] matches the index of the PrimaryImage edge, ensuring accurate error handling.


275-281: Handling PrimaryAssetID in assignValues method

The assignValues method correctly handles the assignment of the PrimaryAssetID field. It checks for the appropriate type using sql.NullScanner, verifies validity, and assigns the value properly, ensuring type safety and accurate data mapping from the database rows.


330-334: Adding QueryPrimaryImage method

The QueryPrimaryImage method appropriately allows querying the PrimaryImage edge from a Node entity. This addition enables retrieval of the primary image associated with a node, aligning with the PR objective of adding a cover image to library pages.


421-425: Including PrimaryAssetID in String method output

The inclusion of PrimaryAssetID in the String() method ensures that the string representation of the Node entity reflects the primary asset ID when it's not nil. This enhancement aids in debugging and logging by providing complete entity information.

internal/ent/node/node.go (7)

37-38: Addition of FieldPrimaryAssetID

The FieldPrimaryAssetID constant is correctly added to represent the primary_asset_id field in the database.


51-52: Addition of EdgePrimaryImage

The EdgePrimaryImage constant is properly defined to denote the primary_image edge name in mutations.


82-88: Definition of PrimaryImage relation constants

The constants PrimaryImageTable, PrimaryImageInverseTable, and PrimaryImageColumn are correctly defined, establishing the relationship with the assets table. This setup avoids circular dependencies with the "asset" package.


137-137: Including FieldPrimaryAssetID in Columns

The addition of FieldPrimaryAssetID to the Columns slice ensures that the new field is included in SQL queries, which is essential for proper database operations involving this field.


262-266: Adding ByPrimaryAssetID OrderOption

The ByPrimaryAssetID function is correctly implemented, allowing results to be ordered by the primary_asset_id field. This enhances query capabilities for sorting based on the new field.


305-311: Adding ByPrimaryImageField OrderOption

The ByPrimaryImageField function is properly added, enabling ordering by a specified field of the primary_image edge. This provides flexibility in sorting queries based on related asset attributes.


409-415: Defining newPrimaryImageStep

The newPrimaryImageStep function is correctly defined to establish the SQL graph step for the primary_image relationship in queries. This step is crucial for traversing the relationship between nodes and their primary images.

internal/ent/migrate/schema.go (3)

543-543: Addition of 'primary_asset_id' column is correct

The new column primary_asset_id is properly defined as a nullable string with a size of 20. This aligns with the existing schema conventions and allows nodes to reference a primary asset.


569-574: Foreign key constraint for 'primary_asset_id' is appropriately set

The foreign key nodes_assets_primary_image correctly establishes a relationship between the nodes table and the assets table. It references AssetsColumns[0] (which corresponds to the id column in assets) from NodesColumns[13] (the newly added primary_asset_id column). The OnDelete: schema.SetNull action ensures that if a referenced asset is deleted, the primary_asset_id in nodes will be set to NULL, which is appropriate for maintaining data integrity.


1014-1014: Initialization of foreign key reference is correctly implemented

In the init function, the assignment to NodesTable.ForeignKeys[3].RefTable = AssetsTable correctly sets the reference table for the new foreign key. This ensures that the schema migration will properly recognize the relationship between nodes and assets.

internal/ent/node_query.go (7)

36-36: LGTM!

The addition of withPrimaryImage field is consistent with existing patterns.


495-495: LGTM!

The Clone method correctly includes cloning of withPrimaryImage.


542-552: LGTM!

The WithPrimaryImage method is implemented consistently with existing eager-loading methods.


Line range hint 697-707: LGTM!

The loadedTypes array is correctly updated with the new edge withPrimaryImage.


750-755: LGTM!

The code correctly loads the PrimaryImage edge when eager-loading is requested.


888-919: LGTM!

The loadPrimaryImage method correctly handles the loading of the PrimaryImage edge, including null checks for PrimaryAssetID.


1258-1260: LGTM!

The code correctly ensures that PrimaryAssetID is selected when withPrimaryImage is requested.

internal/ent/node_create.go (5)

134-138: Method addition looks good

The SetPrimaryAssetID method is correctly implemented and follows the standard pattern for setting fields in the NodeCreate builder.


140-146: Nillable method implementation is consistent

The SetNillablePrimaryAssetID method is properly implemented, allowing for nullable assignment of PrimaryAssetID.


807-823: NodeUpsert methods for PrimaryAssetID are correctly implemented

The methods SetPrimaryAssetID, UpdatePrimaryAssetID, and ClearPrimaryAssetID in the NodeUpsert struct are properly implemented, allowing for setting and updating the PrimaryAssetID field during upsert operations.


1064-1084: NodeUpsertOne methods for PrimaryAssetID are properly added

The addition of SetPrimaryAssetID, UpdatePrimaryAssetID, and ClearPrimaryAssetID methods in NodeUpsertOne is correctly implemented, enabling individual upsert operations to manage the PrimaryAssetID field.


1499-1519: NodeUpsertBulk methods for PrimaryAssetID are correctly implemented

The methods SetPrimaryAssetID, UpdatePrimaryAssetID, and ClearPrimaryAssetID in the NodeUpsertBulk struct are properly implemented, facilitating bulk upsert operations for the PrimaryAssetID field.

internal/ent/node_update.go (2)

399-404: Verify the Necessity of Clearing the "primary_image" Edge

The ClearPrimaryImage() method allows the "primary_image" edge to be unset. Please confirm:

  • This behavior aligns with application requirements.
  • The system can handle nodes without a primary image without issues.

If nodes must always have a primary image, consider removing this method or adding checks to prevent unintended clearing.


1334-1339: Confirm Clearing Behavior of "primary_image" Edge in NodeUpdateOne

The ClearPrimaryImage() method allows the removal of the "primary_image" edge. Please verify:

  • This operation is intended and handled appropriately in the application logic.
  • There are no adverse effects when a node lacks a primary image.

Comment on lines 12 to 14
export function useMediaEditScreen(props: Props) {
//
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement or remove empty hook.

The useMediaEditScreen hook is currently empty. If it's intended for future use, consider adding a TODO comment explaining its purpose. If it's not needed, remove it to avoid confusion.

If you decide to keep it for future implementation, you could add a TODO comment like this:

export function useMediaEditScreen(props: Props) {
  // TODO: Implement media edit screen logic
}

Alternatively, if it's not needed, you can remove it entirely:

-export function useMediaEditScreen(props: Props) {
-  //
-}

}

export function MediaEditScreen(props: Props) {
useMediaEditScreen(props);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Reconsider the use of useMediaEditScreen hook.

The useMediaEditScreen hook is called, but its return value is not used. This could lead to unnecessary computations or side effects. Consider removing the hook call if it's not needed, or use its return value if it provides essential functionality.

If the hook is not needed, you can remove it like this:

-useMediaEditScreen(props);

If it is needed, make sure to use its return value:

const hookResult = useMediaEditScreen(props);
// Use hookResult in your component

@@ -24,6 +24,7 @@ func (Node) Fields() []ent.Field {
field.String("content").Optional().Nillable(),
field.String("parent_node_id").GoType(xid.ID{}).Optional(),
field.String("account_id").GoType(xid.ID{}),
field.String("primary_asset_id").GoType(xid.ID{}).Optional().Nillable(),
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Potential issues with handling primary_asset_id during Asset deletion.

The search did not find any OnDelete operations handling the primary_asset_id references in Nodes. This could lead to dangling references if an Asset is deleted without updating or removing the corresponding primary_asset_id in Nodes. It's recommended to implement cascade delete or nullification strategies to maintain data consistency.

🔗 Analysis chain

Consider implications on querying and data consistency.

The addition of the primary_asset_id field and primary_image edge enhances the Node schema to support a cover image, aligning well with the PR objectives. However, consider the following points:

  1. Querying: Ensure that queries fetching Node data are updated to include the primary image when necessary, to avoid N+1 query problems.

  2. Data Consistency: Implement checks to ensure that the primary_asset_id always corresponds to an existing Asset, perhaps using ent's hooks or validators.

  3. Cascade Behavior: Consider what should happen to this reference if the associated Asset is deleted. You might want to add a policy to handle this scenario.

  4. Migration: Ensure there's a migration plan for existing Node entries that don't have a primary image set.

To verify the usage and potential impact, you can run the following script:

Also applies to: 54-57

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for existing Node queries and potential places that need updating

# Search for Node queries that might need updating
echo "Searching for Node queries that might need updating:"
rg --type go 'Node\s*\{[^}]*\}' -g '!internal/ent/schema/*'

# Search for potential places where data consistency checks might be needed
echo "\nSearching for potential places where data consistency checks might be needed:"
rg --type go 'Create.*Node' -g '!internal/ent/schema/*'

# Search for existing delete operations on Asset that might affect Nodes
echo "\nSearching for existing delete operations on Asset that might affect Nodes:"
rg --type go 'Delete.*Asset' -g '!internal/ent/schema/*'

Length of output: 46088


Script:

#!/bin/bash
# Description: Verify inclusion of 'primary_asset_id' in Node queries

# Check if 'primary_asset_id' is being selected in Node queries
echo "Checking if 'primary_asset_id' is included in Node queries:"
rg --type go 'primary_asset_id' -g '!internal/ent/schema/*' 'Node'

# Description: Verify validators or hooks for 'primary_asset_id' in Node creation and updates
echo "\nChecking for validators or hooks enforcing 'primary_asset_id':"
rg --type go 'SetPrimaryAssetID' -g '!internal/ent/schema/*'

# Description: Ensure Asset deletion operations handle 'primary_asset_id' in Nodes
echo "\nChecking if Asset deletion handles 'primary_asset_id' references in Nodes:"
rg --type go 'OnDelete' -g '!internal/ent/schema/*' 'primary_asset_id'

Length of output: 4305


Script:

#!/bin/bash
# Description: Verify inclusion of 'primary_asset_id' in Node queries

# Corrected search for 'primary_asset_id' within Node-related queries
echo "Checking if 'primary_asset_id' is included in Node queries:"
rg --type go 'Node\s*\{[^}]*primary_asset_id[^}]*\}' -g '!internal/ent/schema/*'

# Description: Verify validators or hooks for 'primary_asset_id' in Node creation and updates
echo "\nChecking for validators or hooks enforcing 'primary_asset_id':"
rg --type go 'SetPrimaryAssetID' -g '!internal/ent/schema/*'

# Description: Ensure Asset deletion operations handle 'primary_asset_id' references in Nodes
echo "\nChecking if Asset deletion handles 'primary_asset_id' references in Nodes:"
rg --type go 'OnDelete.*primary_asset_id' -g '!internal/ent/schema/*'

Length of output: 4224

Comment on lines +92 to +135
[
"trigger",
"fileUpload__trigger"
],
[
"root",
"fileUpload__root"
],
[
"dropzone",
"fileUpload__dropzone"
],
[
"item",
"fileUpload__item"
],
[
"itemDeleteTrigger",
"fileUpload__itemDeleteTrigger"
],
[
"itemGroup",
"fileUpload__itemGroup"
],
[
"itemName",
"fileUpload__itemName"
],
[
"itemPreview",
"fileUpload__itemPreview"
],
[
"itemPreviewImage",
"fileUpload__itemPreviewImage"
],
[
"itemSizeText",
"fileUpload__itemSizeText"
],
[
"label",
"fileUpload__label"
],
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove duplicate slot definitions

The added slot definitions from lines 92 to 135 are exact duplicates of existing entries in the fileUploadSlotNames array. This duplication is unnecessary and could lead to confusion, maintenance issues, and potential performance impacts.

Please remove these duplicate entries. The fileUploadSlotNames array should contain only one instance of each slot definition. Apply this diff to resolve the issue:

-  [
-    "trigger",
-    "fileUpload__trigger"
-  ],
-  [
-    "root",
-    "fileUpload__root"
-  ],
-  [
-    "dropzone",
-    "fileUpload__dropzone"
-  ],
-  [
-    "item",
-    "fileUpload__item"
-  ],
-  [
-    "itemDeleteTrigger",
-    "fileUpload__itemDeleteTrigger"
-  ],
-  [
-    "itemGroup",
-    "fileUpload__itemGroup"
-  ],
-  [
-    "itemName",
-    "fileUpload__itemName"
-  ],
-  [
-    "itemPreview",
-    "fileUpload__itemPreview"
-  ],
-  [
-    "itemPreviewImage",
-    "fileUpload__itemPreviewImage"
-  ],
-  [
-    "itemSizeText",
-    "fileUpload__itemSizeText"
-  ],
-  [
-    "label",
-    "fileUpload__label"
-  ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[
"trigger",
"fileUpload__trigger"
],
[
"root",
"fileUpload__root"
],
[
"dropzone",
"fileUpload__dropzone"
],
[
"item",
"fileUpload__item"
],
[
"itemDeleteTrigger",
"fileUpload__itemDeleteTrigger"
],
[
"itemGroup",
"fileUpload__itemGroup"
],
[
"itemName",
"fileUpload__itemName"
],
[
"itemPreview",
"fileUpload__itemPreview"
],
[
"itemPreviewImage",
"fileUpload__itemPreviewImage"
],
[
"itemSizeText",
"fileUpload__itemSizeText"
],
[
"label",
"fileUpload__label"
],

Comment on lines +76 to +80
func WithPrimaryImage(id asset.AssetID) Option {
return func(nm *ent.NodeMutation) {
nm.SetPrimaryAssetID(id)
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding a function to remove the primary image

While the current implementation allows setting a primary image, it might be useful to provide a way to remove it as well. Consider adding a WithoutPrimaryImage function that clears the primary asset ID.

Here's a suggested implementation:

func WithoutPrimaryImage() Option {
	return func(nm *ent.NodeMutation) {
		nm.ClearPrimaryAsset()
	}
}

This assumes that the ent.NodeMutation has a ClearPrimaryAsset method. If it doesn't, you might need to implement it or use a different approach to clear the primary asset.

Comment on lines +248 to +253
func (nc *NodeCreate) SetNillablePrimaryImageID(id *xid.ID) *NodeCreate {
if id != nil {
nc = nc.SetPrimaryImageID(*id)
}
return nc
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove unnecessary reassignment in SetNillablePrimaryImageID

In the SetNillablePrimaryImageID method, reassigning nc with nc = nc.SetPrimaryImageID(*id) is unnecessary since SetPrimaryImageID modifies nc and returns it. This reassignment could be omitted for consistency and clarity.

Apply this fix to remove the unnecessary reassignment:

 func (nc *NodeCreate) SetNillablePrimaryImageID(id *xid.ID) *NodeCreate {
 	if id != nil {
-		nc = nc.SetPrimaryImageID(*id)
+		nc.SetPrimaryImageID(*id)
 	}
 	return nc
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (nc *NodeCreate) SetNillablePrimaryImageID(id *xid.ID) *NodeCreate {
if id != nil {
nc = nc.SetPrimaryImageID(*id)
}
return nc
}
func (nc *NodeCreate) SetNillablePrimaryImageID(id *xid.ID) *NodeCreate {
if id != nil {
nc.SetPrimaryImageID(*id)
}
return nc
}

Comment on lines +277 to +295
// SetPrimaryImageID sets the "primary_image" edge to the Asset entity by ID.
func (nu *NodeUpdate) SetPrimaryImageID(id xid.ID) *NodeUpdate {
nu.mutation.SetPrimaryImageID(id)
return nu
}

// SetNillablePrimaryImageID sets the "primary_image" edge to the Asset entity by ID if the given value is not nil.
func (nu *NodeUpdate) SetNillablePrimaryImageID(id *xid.ID) *NodeUpdate {
if id != nil {
nu = nu.SetPrimaryImageID(*id)
}
return nu
}

// SetPrimaryImage sets the "primary_image" edge to the Asset entity.
func (nu *NodeUpdate) SetPrimaryImage(a *Asset) *NodeUpdate {
return nu.SetPrimaryImageID(a.ID)
}

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Add Validation for the "primary_image" Edge

While the methods for setting the "primary_image" edge are correctly implemented, it's advisable to add validation to ensure:

  • The Asset being assigned exists and is valid.
  • The Asset meets any criteria required to be a primary image (e.g., correct file type or status).

This can prevent runtime errors and maintain data integrity.

Comment on lines +1212 to +1230
// SetPrimaryImageID sets the "primary_image" edge to the Asset entity by ID.
func (nuo *NodeUpdateOne) SetPrimaryImageID(id xid.ID) *NodeUpdateOne {
nuo.mutation.SetPrimaryImageID(id)
return nuo
}

// SetNillablePrimaryImageID sets the "primary_image" edge to the Asset entity by ID if the given value is not nil.
func (nuo *NodeUpdateOne) SetNillablePrimaryImageID(id *xid.ID) *NodeUpdateOne {
if id != nil {
nuo = nuo.SetPrimaryImageID(*id)
}
return nuo
}

// SetPrimaryImage sets the "primary_image" edge to the Asset entity.
func (nuo *NodeUpdateOne) SetPrimaryImage(a *Asset) *NodeUpdateOne {
return nuo.SetPrimaryImageID(a.ID)
}

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider Validation for "primary_image" Methods in NodeUpdateOne

Similar to the NodeUpdate struct, it's advisable to:

  • Add validation to ensure the Asset assigned as the primary image is valid and exists.
  • Prevent possible runtime errors due to invalid references.

Comment on lines +1666 to +1694
if nuo.mutation.PrimaryImageCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: node.PrimaryImageTable,
Columns: []string{node.PrimaryImageColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(asset.FieldID, field.TypeString),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := nuo.mutation.PrimaryImageIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: node.PrimaryImageTable,
Columns: []string{node.PrimaryImageColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(asset.FieldID, field.TypeString),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve Error Handling in sqlSave for NodeUpdateOne

In the sqlSave method for NodeUpdateOne, consider enhancing error handling when updating the "primary_image" edge:

  • Check for the existence of the Asset ID provided.
  • Handle scenarios where the edge assignment could fail due to referential integrity constraints.

An example modification:

 if nodes := nuo.mutation.PrimaryImageIDs(); len(nodes) > 0 {
     edge := &sqlgraph.EdgeSpec{
         Rel:     sqlgraph.M2O,
         Inverse: false,
         Table:   node.PrimaryImageTable,
         Columns: []string{node.PrimaryImageColumn},
         Bidi:    false,
         Target: &sqlgraph.EdgeTarget{
             IDSpec: sqlgraph.NewFieldSpec(asset.FieldID, field.TypeString),
         },
     }
     for _, k := range nodes {
         edge.Target.Nodes = append(edge.Target.Nodes, k)
+        // Verify Asset existence
+        exists, err := AssetExists(ctx, nuo.driver, k)
+        if err != nil {
+            return nil, err
+        }
+        if !exists {
+            return nil, fmt.Errorf("ent: Asset with ID %v does not exist", k)
+        }
     }
     _spec.Edges.Add = append(_spec.Edges.Add, edge)
 }

Committable suggestion was skipped due to low confidence.

Comment on lines +701 to +729
if nu.mutation.PrimaryImageCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: node.PrimaryImageTable,
Columns: []string{node.PrimaryImageColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(asset.FieldID, field.TypeString),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := nu.mutation.PrimaryImageIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: node.PrimaryImageTable,
Columns: []string{node.PrimaryImageColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(asset.FieldID, field.TypeString),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance Error Handling for "primary_image" Edge Updates in sqlSave

In the sqlSave method, you've added logic to handle the "primary_image" edge. To improve robustness:

  • Add error handling for cases where the provided Asset ID does not correspond to an existing entity.
  • Ensure that setting or clearing the edge maintains database integrity.

Consider updating the code as follows:

 if nodes := nu.mutation.PrimaryImageIDs(); len(nodes) > 0 {
     edge := &sqlgraph.EdgeSpec{
         Rel:     sqlgraph.M2O,
         Inverse: false,
         Table:   node.PrimaryImageTable,
         Columns: []string{node.PrimaryImageColumn},
         Bidi:    false,
         Target: &sqlgraph.EdgeTarget{
             IDSpec: sqlgraph.NewFieldSpec(asset.FieldID, field.TypeString),
         },
     }
     for _, k := range nodes {
         edge.Target.Nodes = append(edge.Target.Nodes, k)
     }
     _spec.Edges.Add = append(_spec.Edges.Add, edge)
+    // Add error handling for non-existent Asset IDs
+    exists, err := AssetExists(ctx, nu.driver, k)
+    if err != nil {
+        return 0, err
+    }
+    if !exists {
+        return 0, fmt.Errorf("ent: Asset with ID %v does not exist", k)
+    }
 }

Committable suggestion was skipped due to low confidence.

This also introduces the concept of assets having version history. The need for this was because when a cover image is cropped, the original is still needed in order to facilitate future edits to the crop.
Copy link

@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: 20

🧹 Outside diff range and nitpick comments (28)
web/src/api/openapi-schema/asset.ts (1)

17-17: LGTM! Consider implementation details for recursive structure.

The addition of the optional parent property of type Asset to the Asset interface is a good approach to implement the recursive backreference mentioned in the PR objectives. This change enables a hierarchical organization of assets, which can be beneficial for managing relationships between assets, such as the original and cropped versions of images mentioned in the PR summary.

However, when implementing this recursive structure, consider the following:

  1. Ensure that circular references are prevented or handled appropriately to avoid infinite loops during serialization or traversal.
  2. Implement proper depth limiting when querying or displaying asset hierarchies to prevent performance issues with deeply nested structures.
  3. Update any existing code that works with Asset objects to handle this new optional property correctly.
web/src/api/openapi-schema/parentAssetIDQueryParameter.ts (1)

11-19: LGTM: Well-documented type definition

The ParentAssetIDQueryParameter type is clearly defined, and the accompanying documentation provides comprehensive information about its purpose and usage scenarios. This aligns well with the PR objectives of introducing a new asset relation for library pages.

Consider adding a brief example of how this parameter might be used in a request, to further clarify its application for developers.

internal/ent/schema/asset.go (3)

31-31: LGTM! Consider adding a comment for clarity.

The addition of the parent_asset_id field is well-implemented. It's correctly defined as optional and nillable, allowing for assets without a parent. The use of xid.ID{} as the Go type is consistent with other ID fields in the schema.

Consider adding a brief comment above the field to explain its purpose:

+       // ID of the parent asset, if this asset is a derivative or version of another asset
        field.String("parent_asset_id").GoType(xid.ID{}).Optional().Nillable(),

58-62: LGTM! Consider adding inverse edges for completeness.

The new edge is well-implemented, establishing a clear parent-child relationship between assets. The "Unique" constraint correctly ensures that an asset can have only one parent.

For improved querying capabilities, consider adding an inverse edge from parent to children:

        edge.To("assets", Asset.Type).
            From("parent").
            Unique().
-           Field("parent_asset_id"),
+           Field("parent_asset_id").
+           Inverse("children"),

This would allow easy querying of an asset's children, enhancing the API's flexibility.


31-31: Consider implications of asset hierarchies

The introduction of parent-child relationships for assets is a significant change that may have broader implications for asset management and querying.

  1. Recursive Querying: Consider implementing methods or hooks that allow for efficient recursive querying of asset hierarchies. This could be useful for scenarios where you need to traverse the entire asset tree.

  2. Depth Limitation: Evaluate whether there should be a limit on the depth of asset hierarchies to prevent potential performance issues with deeply nested structures.

  3. Circular References: Implement safeguards to prevent circular references in the asset hierarchy (e.g., asset A being a parent of asset B, which is a parent of asset A).

  4. Cascade Operations: Consider how operations like deletion should behave in the context of asset hierarchies. Should deleting a parent asset also delete all its children?

These considerations align with the PR objective of discussing recursive backreferencing for Assets. Implementing these features now or planning for them in the future could enhance the robustness of the asset management system.

Also applies to: 58-62

web/src/api/openapi-schema/assetUploadParams.ts (2)

34-42: LGTM: New property added with clear documentation

The new parent_asset_id property is well-defined and documented. It aligns with the PR objectives and maintains backward compatibility by being optional. The comment provides clear guidance on its usage.

Consider adding a brief example in the comment to illustrate how this property might be used in practice. This could help developers quickly understand its application. For instance:

/**
 * ...existing comment...
 * 
 * Example:
 * {
 *   filename: "updated_image.jpg",
 *   parent_asset_id: "asset_123"
 * }
 */

Line range hint 1-42: Overall assessment: Well-implemented feature addition

The changes in this file successfully introduce the new asset relation feature for library pages. The implementation is backward-compatible, well-documented, and follows existing code patterns. These modifications align well with the PR objectives, particularly in supporting the handling of original and cropped versions of images.

As the project evolves, consider creating a separate type for version-related properties if more properties are added in the future. This could improve code organization and make it easier to manage version-specific logic.

app/resources/asset/db.go (1)

42-61: Good implementation, but consider adding validation and documentation

The AddVersion method is well-structured and consistent with the existing Add method. It correctly sets the parent asset ID, which aligns with the PR objective of adding versioning to assets. The error handling is robust, using the fault package for wrapping errors with context and tags.

However, there are a few areas for improvement:

  1. Consider adding validation to ensure the parent asset exists before creating a new version.
  2. Implement permission checking to ensure the account has the right to add a version to the parent asset.
  3. Add a comment to explain the purpose of the AddVersion method and how it differs from Add.

Here's a suggested improvement:

+// AddVersion creates a new version of an existing asset.
+// It links the new asset to its parent using the parent's AssetID.
 func (d *database) AddVersion(ctx context.Context,
 	accountID xid.ID,
 	parent AssetID,
 	filename Filename,
 	size int,
 ) (*Asset, error) {
+	// Verify that the parent asset exists
+	_, err := d.GetByID(ctx, parent)
+	if err != nil {
+		return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.InvalidArgument))
+	}
+
+	// TODO: Implement permission checking to ensure the account can add a version to this asset
+
 	asset, err := d.db.Asset.
 		Create().
 		SetID(filename.GetID()).
 		SetParentAssetID(parent).
 		SetFilename(filename.name).
 		SetSize(size).
 		SetAccountID(xid.ID(accountID)).
 		Save(ctx)
 	if err != nil {
 		return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.Internal))
 	}

 	return FromModel(asset), nil
 }

This improvement adds a check for the parent asset's existence and a placeholder for permission checking. It also includes a comment explaining the method's purpose.

web/src/components/ui/file-upload.tsx (2)

15-22: New RootProvider component with strong typing

The introduction of the RootProvider component with its complex type definition enhances type safety and provides a clear contract for component props. This is a positive addition to the file.

For improved readability, consider using type aliases for the complex type definitions. For example:

type RootProviderPropsType = Assign<
  Assign<HTMLStyledProps<"div">, FileUpload.RootProviderBaseProps>,
  FileUploadVariantProps
>;

export const RootProvider = withProvider<HTMLDivElement, RootProviderPropsType>(
  FileUpload.RootProvider,
  "root"
);

This approach can make the code more maintainable and easier to understand at a glance.


24-31: Consistent typing approach for Root component

The update to the Root component aligns it with the typing approach used in RootProvider. This consistency is commendable and improves the overall structure of the file.

As suggested for RootProvider, consider using a type alias here as well:

type RootPropsType = Assign<
  Assign<HTMLStyledProps<"div">, FileUpload.RootBaseProps>,
  FileUploadVariantProps
>;

export const Root = withProvider<HTMLDivElement, RootPropsType>(
  FileUpload.Root,
  "root"
);

This will further enhance readability and maintainability.

app/transports/http/bindings/assets.go (1)

51-52: LGTM! Consider adding a comment for clarity.

The addition of parentID aligns well with the PR objective of introducing a new asset relation. The use of opt.NewPtrMap is a good practice for handling optional values.

Consider adding a brief comment explaining the purpose of this new variable:

+// Extract the optional parent asset ID from the request parameters
 parentID := opt.NewPtrMap(request.Params.ParentAssetId, deserialiseAssetID)
web/src/components/asset/AssetUploadAction.tsx (1)

96-147: Rendering logic looks good, minor improvement for getMIMEs function.

The component's rendering logic is clear and follows React best practices. The getMIMEs function handles different input types correctly. However, there's room for a small improvement:

In the getMIMEs function, consider using type narrowing to simplify the logic:

function getMIMEs(
  accept: Record<string, string[]> | string | string[] | undefined
): string[] {
  if (!accept) return [];
  if (typeof accept === "string") return [accept];
  if (Array.isArray(accept)) return accept;
  return Object.keys(accept);
}

This change makes the function more concise and easier to read.

The rendering logic and overall structure of the component are well-implemented.

web/src/screens/library/LibraryPageScreen/LibraryPageScreen.tsx (2)

30-38: Improved data fetching and error handling

The use of useNodeGet hook with fallback data and the implementation of error handling with UnreadyBanner are excellent improvements. They enhance data management and user experience.

Consider adding a loading state to provide feedback to users while data is being fetched:

if (isLoading) {
  return <LoadingSpinner />;
}
if (!data) {
  return <UnreadyBanner error={error} />;
}

81-169: Improved UI for editing and cover image management

The simplification of edit actions and the implementation of cover image cropping are well-executed and align with the PR objectives. The conditional rendering effectively handles different states of the component.

Consider extracting the FixedCropper logic into a separate component to improve readability and maintainability:

const CoverImageCropper = ({ url, initialCoordinates, onCrop }) => {
  // ... cropper logic
};

// Usage
{editing && primaryAssetEditingURL && (
  <CoverImageCropper
    url={primaryAssetEditingURL}
    initialCoordinates={initialCoverCoordinates}
    onCrop={handleCoverAdjustment}
  />
)}
internal/ent/er.html (2)

244-244: LGTM: New relationships added

The new relationships Node }o--o| Asset : "primary_image" and Asset |o--o{ Asset : "assets/parent" correctly represent the primary image feature and the parent-child relationship between assets. These additions are consistent with the PR objectives and the new fields added to the entities.

Consider renaming the "assets/parent" relationship to "children/parent" for clarity. This would make it more intuitive that an asset can have multiple children, while still being a child of another asset.

Also applies to: 264-264


43-43: Summary: ERD updated to support cover images and asset relationships

The changes to the ERD successfully implement the structure needed for the cover image feature and introduce a more flexible asset relationship model. These modifications include:

  1. Adding a parent-child relationship for assets
  2. Introducing a primary asset for nodes (potentially all node types)
  3. Establishing the necessary relationships between nodes, assets, and other assets

These changes not only fulfill the PR objectives but also provide a foundation for potential future enhancements to asset management within the system. The implementation maintains consistency with the existing ERD structure while introducing new capabilities.

Consider documenting these new relationships and their potential uses in the project's architecture documentation. This will help other developers understand the full capabilities of the new asset structure and how it can be leveraged in different parts of the application.

Also applies to: 166-166, 244-244, 264-264

app/services/library/node_mutate/node.go (1)

48-48: LGTM: Addition of PrimaryImage field.

The new PrimaryImage field of type deletable.Value[asset.AssetID] in the Partial struct aligns with the PR objective of adding a primary asset cover image to library pages. This allows for setting, unsetting, or removing the primary image for a node.

Consider adding a comment to explain the purpose and behavior of the PrimaryImage field, especially regarding its deletable.Value nature.

web/src/lib/library/library.ts (1)

40-61: LGTM: Well-defined CoverImageArgs type with clear documentation.

The CoverImageArgs type is well-structured and provides clear options for handling cover images with or without cropping. The comments are helpful in explaining the purpose of each field.

Consider adding a brief comment above the type definition to explain its overall purpose and usage context.

internal/ent/migrate/schema.go (2)

123-123: LGTM! Consider adding an index for parent_asset_id.

The addition of the parent_asset_id column and its corresponding foreign key constraint is well-implemented. This change allows for a hierarchical structure of assets, which aligns with the PR objectives. The SetNull action on delete is a good choice for maintaining data integrity.

Consider adding an index on the parent_asset_id column to improve query performance when fetching child assets:

 {Name: "parent_asset_id", Type: field.TypeString, Nullable: true, Size: 20},
+// Add this line
+{Name: "parent_asset_id_index", Unique: false, Columns: []*schema.Column{AssetsColumns[7]}},

Also applies to: 137-142


550-550: LGTM! Consider adding an index for primary_asset_id.

The addition of the primary_asset_id column and its corresponding foreign key constraint is well-implemented. This change allows nodes to have a primary asset (cover image), which directly aligns with the PR objectives. The SetNull action on delete is a good choice for maintaining data integrity.

Consider adding an index on the primary_asset_id column to improve query performance when fetching nodes with their primary assets:

 {Name: "primary_asset_id", Type: field.TypeString, Nullable: true, Size: 20},
+// Add this line
+{Name: "primary_asset_id_index", Unique: false, Columns: []*schema.Column{NodesColumns[13]}},

Also applies to: 576-581

internal/ent/node_create.go (1)

241-258: New methods for PrimaryImage edge look good, with a minor suggestion

The new methods SetPrimaryImageID, SetNillablePrimaryImageID, and SetPrimaryImage are correctly implemented and follow the existing patterns in the codebase. They provide a way to set the "primary_image" edge to the Asset entity, which aligns with the PR objective.

However, there's a small inconsistency in the SetNillablePrimaryImageID method:

In the SetNillablePrimaryImageID method, consider removing the unnecessary assignment:

 func (nc *NodeCreate) SetNillablePrimaryImageID(id *xid.ID) *NodeCreate {
 	if id != nil {
-		nc = nc.SetPrimaryImageID(*id)
+		nc.SetPrimaryImageID(*id)
 	}
 	return nc
 }

This change would make it consistent with other similar methods in the file.

internal/ent/mutation.go (4)

4131-4178: LGTM: Comprehensive methods for parent asset ID management.

The new methods for handling the parent asset ID are well-implemented and provide a complete set of operations. They follow the established pattern in the codebase.

One minor suggestion:

Consider adding a comment to explain the purpose of the OldParentAssetID method, as it's not immediately obvious why it's needed.

+// OldParentAssetID returns the old value of the parent_asset_id field before any changes in this mutation.
+// This is used for optimistic concurrency control in update operations.
 func (m *AssetMutation) OldParentAssetID(ctx context.Context) (v *xid.ID, err error) {
     // ... (existing implementation)
 }

4382-4474: LGTM: Comprehensive methods for parent-child asset relationship management.

The new methods for handling the parent asset relationship and assets relationship are well-implemented and provide a complete set of operations. They follow the established pattern in the codebase.

One minor suggestion:

Consider adding a brief comment to explain the purpose of the ParentIDs method, as it's not immediately clear why it returns a slice when there's only one parent.

+// ParentIDs returns the parent IDs in the mutation.
+// Note: This method is used internally by the framework and should not be used directly.
 func (m *AssetMutation) ParentIDs() (ids []xid.ID) {
     // ... (existing implementation)
 }

15210-15257: LGTM: Comprehensive methods for primary asset ID management.

The new methods for handling the primary asset ID are well-implemented and provide a complete set of operations. They follow the established pattern in the codebase and include proper error handling.

One minor suggestion:

Consider adding a comment to explain the purpose of the OldPrimaryAssetID method, as it's not immediately obvious why it's needed.

+// OldPrimaryAssetID returns the old value of the primary_asset_id field before any changes in this mutation.
+// This is used for optimistic concurrency control in update operations.
 func (m *NodeMutation) OldPrimaryAssetID(ctx context.Context) (v *xid.ID, err error) {
     // ... (existing implementation)
 }

15527-15565: LGTM: Comprehensive methods for primary image relationship management.

The new methods for handling the primary image relationship are well-implemented and provide a complete set of operations. They follow the established pattern in the codebase.

One minor suggestion:

Consider adding a brief comment to explain the purpose of the PrimaryImageIDs method, as it's not immediately clear why it returns a slice when there's only one primary image.

+// PrimaryImageIDs returns the primary image IDs in the mutation.
+// Note: This method is used internally by the framework and should not be used directly.
 func (m *NodeMutation) PrimaryImageIDs() (ids []xid.ID) {
     // ... (existing implementation)
 }
internal/deletable/deletable.go (2)

8-12: Clarify the documentation for Value[T] struct

The current documentation may cause confusion because both the "deleted" and "left untouched" states are associated with "no value." Consider rephrasing to differentiate these states more clearly. For example:

  • Deleted: No value, delete = true
  • Updated: Holds a new value
  • Unchanged: Neither deleted nor updated (no value)

13-16: Consider renaming the delete field to avoid confusion with built-in functions

Using delete as a field name might lead to confusion since delete is a built-in function in Go for deleting map entries. Consider renaming the field to shouldDelete or markForDeletion for better clarity.

app/services/asset/asset_upload/uploader.go (1)

51-53: Add documentation comments for exported struct fields

The exported struct Options has fields ContentFill and the newly added ParentID without documentation comments. According to Go conventions, exported fields should have comments for better readability and maintainability.

Consider adding comments like:

type Options struct {
    // ContentFill specifies how the content should be filled.
    ContentFill opt.Optional[asset.ContentFillCommand]
    // ParentID is the optional ID of the parent asset for versioning.
    ParentID    opt.Optional[asset.AssetID]
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between db2fcdb and ed2a469.

⛔ Files ignored due to path filters (6)
  • api/config.yaml is excluded by !**/*.yaml
  • api/openapi.yaml is excluded by !**/*.yaml
  • go.mod is excluded by !**/*.mod
  • go.sum is excluded by !**/*.sum, !**/*.sum
  • web/package.json is excluded by !**/*.json
  • web/yarn.lock is excluded by !**/yarn.lock, !**/*.lock, !**/*.lock
📒 Files selected for processing (53)
  • app/resources/asset/asset.go (3 hunks)
  • app/resources/asset/db.go (1 hunks)
  • app/resources/library/mapping.go (1 hunks)
  • app/resources/library/node.go (1 hunks)
  • app/resources/library/node_querier/node_querier.go (1 hunks)
  • app/resources/library/node_search/search.go (1 hunks)
  • app/resources/library/node_writer/node_writer.go (1 hunks)
  • app/services/asset/asset_upload/uploader.go (2 hunks)
  • app/services/library/node_mutate/node.go (3 hunks)
  • app/transports/http/bindings/assets.go (2 hunks)
  • app/transports/http/bindings/nodes.go (5 hunks)
  • app/transports/http/openapi/server_gen.go (13 hunks)
  • internal/deletable/deletable.go (1 hunks)
  • internal/ent/asset.go (7 hunks)
  • internal/ent/asset/asset.go (7 hunks)
  • internal/ent/asset/where.go (3 hunks)
  • internal/ent/asset_create.go (6 hunks)
  • internal/ent/asset_query.go (8 hunks)
  • internal/ent/asset_update.go (8 hunks)
  • internal/ent/client.go (2 hunks)
  • internal/ent/er.html (4 hunks)
  • internal/ent/migrate/schema.go (6 hunks)
  • internal/ent/mutation.go (41 hunks)
  • internal/ent/node.go (14 hunks)
  • internal/ent/node/node.go (7 hunks)
  • internal/ent/node/where.go (3 hunks)
  • internal/ent/node_create.go (6 hunks)
  • internal/ent/node_query.go (8 hunks)
  • internal/ent/node_update.go (8 hunks)
  • internal/ent/schema/asset.go (2 hunks)
  • internal/ent/schema/node.go (2 hunks)
  • web/panda.config.ts (2 hunks)
  • web/src/api/openapi-schema/asset.ts (1 hunks)
  • web/src/api/openapi-schema/assetUploadParams.ts (2 hunks)
  • web/src/api/openapi-schema/index.ts (1 hunks)
  • web/src/api/openapi-schema/nodeCommonProps.ts (2 hunks)
  • web/src/api/openapi-schema/nodeInitialProps.ts (2 hunks)
  • web/src/api/openapi-schema/nodeMutableProps.ts (2 hunks)
  • web/src/api/openapi-schema/nullableIdentifier.ts (1 hunks)
  • web/src/api/openapi-schema/parentAssetIDQueryParameter.ts (1 hunks)
  • web/src/components/asset/AssetUploadAction.tsx (1 hunks)
  • web/src/components/library/LibraryPageCoverImageControl/LibraryPageCoverImageControl.tsx (1 hunks)
  • web/src/components/library/LibraryPageCoverImageControl/useLibraryPageCoverImageControl.ts (1 hunks)
  • web/src/components/ui/file-upload.tsx (1 hunks)
  • web/src/lib/library/library.ts (7 hunks)
  • web/src/lib/library/metadata.ts (1 hunks)
  • web/src/recipes/file-upload.ts (1 hunks)
  • web/src/screens/library/LibraryPageScreen/LibraryPageScreen.tsx (3 hunks)
  • web/src/screens/library/LibraryPageScreen/useLibraryPageScreen.ts (7 hunks)
  • web/src/screens/media/MediaEditModal.tsx (1 hunks)
  • web/src/screens/media/MediaEditScreen.tsx (1 hunks)
  • web/styled-system/recipes/file-upload.d.ts (1 hunks)
  • web/styled-system/recipes/file-upload.mjs (1 hunks)
🔥 Files not summarized due to errors (2)
  • app/transports/http/openapi/server_gen.go: Error: Server error: no LLM provider could handle the message
  • internal/ent/mutation.go: Error: Server error: no LLM provider could handle the message
🚧 Files skipped from review as they are similar to previous changes (17)
  • app/resources/library/mapping.go
  • app/resources/library/node.go
  • app/resources/library/node_querier/node_querier.go
  • app/resources/library/node_search/search.go
  • app/transports/http/bindings/nodes.go
  • internal/ent/schema/node.go
  • web/panda.config.ts
  • web/src/api/openapi-schema/nodeCommonProps.ts
  • web/src/api/openapi-schema/nodeInitialProps.ts
  • web/src/api/openapi-schema/nodeMutableProps.ts
  • web/src/components/library/LibraryPageCoverImageControl/LibraryPageCoverImageControl.tsx
  • web/src/components/library/LibraryPageCoverImageControl/useLibraryPageCoverImageControl.ts
  • web/src/recipes/file-upload.ts
  • web/src/screens/media/MediaEditModal.tsx
  • web/src/screens/media/MediaEditScreen.tsx
  • web/styled-system/recipes/file-upload.d.ts
  • web/styled-system/recipes/file-upload.mjs
🧰 Additional context used
🪛 Biome
web/src/components/asset/AssetUploadAction.tsx

[error] 68-68: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (167)
web/src/lib/library/metadata.ts (2)

1-3: LGTM: Imports are correct and necessary.

The imports for Zod and the Metadata type are appropriate for the functionality of this file.


11-14: LGTM: NodeMetadataSchema and type are well-defined.

The NodeMetadataSchema is correctly structured with an optional coverImage property. This aligns well with the PR objective of adding a cover image to library pages while maintaining flexibility for nodes without cover images.

web/src/api/openapi-schema/nullableIdentifier.ts (2)

1-9: LGTM: Well-structured generated file header

The generated comment header provides crucial information about the file's origin and the API it pertains to. It clearly indicates that the file shouldn't be manually edited and provides valuable insights into the API's versioning strategy. This information is essential for developers working with the Storyden API.


11-15: LGTM: Clear and well-documented type definition

The NullableIdentifier type is well-defined and properly documented. The use of a union type (string | null) accurately represents a nullable string identifier. The JSDoc comment clearly explains the type's purpose and includes the @nullable tag, which is consistent with the type definition.

web/src/api/openapi-schema/asset.ts (1)

Line range hint 1-24: Verify API schema consistency for auto-generated file.

This file appears to be auto-generated based on the OpenAPI schema. To ensure the integrity of the API contract:

  1. Verify that the changes in this file accurately reflect the intended modifications to the API schema.
  2. Confirm that the auto-generation process is working correctly and that no manual changes were made to this file.
  3. If there are any discrepancies, update the source OpenAPI schema and regenerate the TypeScript definitions.

To check for any manual modifications to this auto-generated file, you can run the following command:

If this command returns any output, it indicates that there might be uncommitted changes to the file, which should be investigated as it's supposed to be auto-generated.

✅ Verification successful

No Uncommitted Changes Detected in asset.ts.

The auto-generated file web/src/api/openapi-schema/asset.ts has no uncommitted changes, confirming its consistency with the OpenAPI schema and ensuring the auto-generation process is functioning correctly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any uncommitted changes in the auto-generated file

git status --porcelain web/src/api/openapi-schema/asset.ts

Length of output: 60

web/src/api/openapi-schema/parentAssetIDQueryParameter.ts (1)

1-9: LGTM: File header provides crucial context

The file header effectively communicates that this is an auto-generated file and provides important information about the Storyden API's versioning strategy. This context is valuable for developers working with the API.

web/src/api/openapi-schema/assetUploadParams.ts (1)

13-13: LGTM: New import added correctly

The new import for ParentAssetIDQueryParameter is correctly added and follows the existing import pattern in the file. This import is necessary for the new property added to the AssetUploadParams type.

web/src/components/ui/file-upload.tsx (4)

1-13: Improved modularity and type safety in imports

The new import structure and the introduction of createStyleContext demonstrate a move towards a more modular and type-safe approach. This change enhances maintainability and provides better IntelliSense support.


33-81: Consistent and type-safe approach for FileUpload components

The redefinition of multiple FileUpload components (Dropzone, ItemDeleteTrigger, ItemGroup, etc.) using withContext and specific HTML element types is a significant improvement. This approach:

  1. Enhances type safety by explicitly defining the HTML element type for each component.
  2. Improves consistency across the component definitions.
  3. Facilitates easier styling and context sharing among components.

The consistent structure makes the code more maintainable and easier to understand. Great job on implementing this standardized approach across all components!


83-86: Improved module exports with clear naming

The addition of exports for FileUploadContext as Context and FileUploadHiddenInput as HiddenInput enhances the flexibility of this module. The clear naming convention improves the overall usability and readability of the code when these components are imported in other parts of the application.


58-61: ⚠️ Potential issue

Fix inconsistent HTML element type for ItemPreview

The ItemPreview component still has an inconsistency between its HTML element type and props type. The first type parameter is HTMLImageElement, but the props use HTMLStyledProps<"div">. This mismatch could lead to incorrect usage.

To resolve this, you should align the element type with the props type. If ItemPreview is intended to be a div element, update the type as follows:

export const ItemPreview = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<"div">, FileUpload.ItemPreviewBaseProps>
>(FileUpload.ItemPreview, "itemPreview");

Alternatively, if it's meant to be an img element, update the props type accordingly.

app/transports/http/bindings/assets.go (1)

59-59: LGTM! Consistent with the new parent asset feature.

The addition of the ParentID field to the asset_upload.Options struct is consistent with the new parent asset feature. This change allows the upload function to associate an asset with its parent, supporting the new asset relation mentioned in the PR objectives.

web/src/components/asset/AssetUploadAction.tsx (3)

1-22: Imports and type definitions look good!

The imports are appropriate for the component's functionality, and the type definitions for AssetUploadActionProps and Props are clear and well-structured.


24-29: Component setup looks good, but verify getMIMEs function.

The component setup follows React best practices and demonstrates good separation of concerns. However, ensure that the getMIMEs function (called on line 29) is properly implemented and tested.

#!/bin/bash
# Description: Verify the implementation of the getMIMEs function
# Expected: The function should be defined and handle different input types correctly

# Test: Search for the getMIMEs function definition
rg --type typescript 'function getMIMEs'

# Test: Check if the function handles different input types (string, array, record)
rg --type typescript 'if \(typeof accept === "string"\)' -A 10

31-47: Improve error handling and investigate potential bug.

The handleFile function seems to handle file uploads correctly, but consider the following improvements:

  1. Enhance error handling by providing more specific error messages, especially for the case when no file is provided.
  2. Investigate the potential Zag bug mentioned in the comment on line 33. If it's a known issue, consider adding a link to the issue tracker or providing more context.

Consider updating the error logging on line 36 to provide more context:

- console.error("handleFile: no file was provided", files);
+ console.error("handleFile: expected at least one file, but none were provided", { files });
app/resources/library/node_writer/node_writer.go (3)

76-80: LGTM: WithPrimaryImage function implementation

The WithPrimaryImage function is well-implemented and aligns with the PR objectives. It correctly uses the Option pattern and sets the primary asset ID as expected.


82-86: LGTM: WithPrimaryImageRemoved function implementation

The WithPrimaryImageRemoved function is well-implemented and addresses the suggestion from the past review comment. It correctly uses the Option pattern and clears the primary asset ID as expected.


76-86: Excellent addition of primary image management functions

The new WithPrimaryImage and WithPrimaryImageRemoved functions are well-implemented and significantly enhance the node management capabilities. These additions align perfectly with the PR objectives of adding a primary asset cover image to library pages. They also address the past review comment suggestion, demonstrating responsiveness to feedback.

These functions will allow for more flexible and robust handling of primary images in the library pages, improving the overall user experience.

web/src/screens/library/LibraryPageScreen/LibraryPageScreen.tsx (3)

3-4: Improved component structure and necessary imports added

The changes to the import statements and the introduction of the LibraryPage component are well-structured and align with the PR objectives. The separation of concerns between LibraryPageScreen and LibraryPage enhances code organization and maintainability.

Also applies to: 11-11, 13-13, 17-17, 20-23, 30-38


Line range hint 40-58: Well-structured component props and state management

The addition of new props related to the cover image functionality and the use of the useLibraryPageScreen custom hook demonstrate good practices in React component design. This approach effectively separates concerns and manages complex state logic outside the component.


154-166: ⚠️ Potential issue

Fix prop types for Next.js Image component

The use of the Next.js Image component for displaying the cover image is appropriate. However, the width and height props should be numbers, not strings.

Please update the Image component props as follows:

<Image
  className={css({
    width: "full",
    height: "full",
    borderRadius: "lg",
    objectFit: "cover",
    objectPosition: "center",
  })}
  src={primaryAssetURL}
  alt=""
-  width="1920"
-  height="1080"
+  width={1920}
+  height={1080}
/>
internal/ent/er.html (2)

43-43: LGTM: New parent-child relationship for assets

The addition of parent_asset_id to the Asset entity enables a hierarchical structure for assets. This change aligns with the PR objectives and allows for more complex asset relationships, which could be useful for managing different versions or variations of an asset.


166-166: LGTM: Primary asset for nodes added

The addition of primary_asset_id to the Node entity allows nodes to have a primary asset, which aligns with the PR objective of adding a cover image to library pages. This implementation suggests that the feature is available for all nodes, not just library pages.

Could you clarify if this feature is intended to be available for all node types or specifically for library pages? If it's for all nodes, consider updating the PR description to reflect this broader scope.

app/services/library/node_mutate/node.go (3)

29-29: LGTM: New import for deletable package.

The addition of the deletable package import is consistent with the new PrimaryImage field in the Partial struct. This import is necessary for using the deletable.Value type.


66-70: LGTM: Proper handling of PrimaryImage in Opts method.

The new logic for handling the PrimaryImage field in the Opts method is well-implemented. It correctly uses the Call method of deletable.Value to handle all possible states (set, unset, removed) of the primary image. The implementation is consistent with how other fields are processed in this method.


Line range hint 102-270: Verify: Implicit handling of PrimaryImage in Create and Update methods.

The Create and Update methods don't show explicit changes for handling the new PrimaryImage field. This suggests that the processing of PrimaryImage is abstracted away in the Opts method or the node_writer package, which is a good practice for maintaining separation of concerns.

To ensure proper handling of the PrimaryImage field, please verify:

  1. The node_writer package correctly processes the WithPrimaryImage and WithPrimaryImageRemoved options.
  2. The Create and Update methods in the node_writer package handle these new options appropriately.

You can use the following script to check the node_writer package:

web/src/lib/library/library.ts (7)

3-3: LGTM: New imports and types for cover image functionality.

The added imports and types are appropriate for implementing the cover image feature. They provide the necessary tools and type definitions for handling assets and node metadata.

Also applies to: 9-9, 17-19, 33-34


288-288: LGTM: Added removeNodeCoverImage to the returned object

The addition of removeNodeCoverImage to the returned object from useLibraryMutation is consistent with the implementation of the new function. This makes the new functionality available to consumers of this hook.


Line range hint 1-314: Overall assessment: Good implementation with room for improvement

The changes to library.ts successfully implement the cover image functionality, introducing new types, functions, and modifying existing ones to support this feature. The code is generally well-structured and follows good practices.

Main points for improvement:

  1. Enhance error handling, particularly in functions like removeNodeCoverImage and updateNode.
  2. Improve type safety in areas like the mergePrimaryImageAsset function and the updateNode function's use of type assertions.
  3. Ensure consistency in the structure and behavior of mutation functions.
  4. Address the potential issue with query parameter handling in nodeListAllKeyFn.
  5. Clarify the behavior for edge cases, such as when oldNode.primary_image is undefined in mergePrimaryImageAsset.

These improvements will enhance the robustness and maintainability of the code. Consider implementing the suggested changes and adding comprehensive unit tests to verify the behavior of the new and modified functions.


Line range hint 135-190: Improvements needed in updateNode function

The updateNode function has been updated to handle cover image updates, which is great. However, there are a few areas that need attention:

  1. The nodeMutator function uses the spread operator to merge properties, which could lead to unexpected results if newNode contains properties not present in the original data.

  2. The TODO comment on line 187 indicates that the original node metadata should be spread, but it's not being done.

  3. The type assertion satisfies NodeWithChildren on line 151 might hide type errors if the merged object doesn't actually satisfy the NodeWithChildren type.

To address these issues, consider the following improvements:

  1. Use a more explicit merging strategy for nodeProps:
const nodeProps = Object.fromEntries(
  Object.entries(newNode).filter(([key]) => key in data && key !== 'parent')
);
  1. Implement the spreading of the original node metadata:
const newMeta = cover && !cover.isReplacement
  ? {
      ...data.meta,
      coverImage: cover.config,
    }
  : undefined;
  1. Replace the type assertion with a type guard:
const updated: Partial<NodeWithChildren> = {
  ...data,
  ...nodeProps,
  ...withNewCover,
};

if (isNodeWithChildren(updated)) {
  return updated;
} else {
  console.error('Updated node does not satisfy NodeWithChildren type');
  return data;
}

Where isNodeWithChildren is a type guard function you'd need to implement.

To verify these changes, we can add some unit tests:

#!/bin/bash
# Create a test file for updateNode function

cat << EOF > test_updateNode.ts
import { updateNode } from './library';
import { NodeWithChildren, CoverImageArgs } from './types';

// Mock data
const mockNode: NodeWithChildren = {
  // ... (fill with mock data)
};

const mockCover: CoverImageArgs = {
  // ... (fill with mock data)
};

// Test cases
console.log(updateNode('test-slug', { name: 'New Name' }));
console.log(updateNode('test-slug', { name: 'New Name' }, mockCover));
console.log(updateNode('test-slug', { invalidProp: 'Should not be included' } as any));

EOF

# Run the test file
npx ts-node test_updateNode.ts

This test script will help verify that the updateNode function correctly handles various scenarios, including invalid properties and cover image updates.


211-232: 🛠️ Refactor suggestion

Improve error handling and consistency in removeNodeCoverImage

The removeNodeCoverImage function correctly implements the removal of cover images with optimistic updates. However, there are a few areas for improvement:

  1. Error handling is missing for the nodeUpdate call.
  2. The function's structure differs slightly from other mutation functions in the file.

Consider the following improvements:

  1. Add error handling:
try {
  await nodeUpdate(slug, {
    primary_image_asset_id: null,
  });
} catch (error) {
  console.error("Failed to remove node cover image:", error);
  // Revert the optimistic update
  await mutate(nodeKeyFn);
}
  1. For consistency with other functions, consider moving the mutate call after the nodeUpdate:
const mutator: MutatorCallback<NodeGetOKResponse> = (data) => {
  if (!data) return;
  return { ...data, primary_image: undefined };
};

try {
  await nodeUpdate(slug, {
    primary_image_asset_id: null,
  });
  await mutate(nodeKeyFn, mutator, { revalidate: false });
} catch (error) {
  console.error("Failed to remove node cover image:", error);
  // Optionally, show an error message to the user
}

These changes will improve error handling and make the function more consistent with the rest of the codebase.

To verify these changes, we can add a unit test:

#!/bin/bash
# Create a test file for removeNodeCoverImage function

cat << EOF > test_removeNodeCoverImage.ts
import { removeNodeCoverImage } from './library';
import { NodeWithChildren } from './types';

// Mock SWR's mutate function
const mockMutate = jest.fn();
jest.mock('swr', () => ({
  ...jest.requireActual('swr'),
  useSWRConfig: () => ({ mutate: mockMutate }),
}));

// Mock nodeUpdate function
const mockNodeUpdate = jest.fn();
jest.mock('@/api/openapi-client/nodes', () => ({
  ...jest.requireActual('@/api/openapi-client/nodes'),
  nodeUpdate: mockNodeUpdate,
}));

describe('removeNodeCoverImage', () => {
  it('should remove cover image and update node', async () => {
    const slug = 'test-slug';
    await removeNodeCoverImage(slug);

    expect(mockMutate).toHaveBeenCalled();
    expect(mockNodeUpdate).toHaveBeenCalledWith(slug, { primary_image_asset_id: null });
  });

  it('should handle errors and revert optimistic update', async () => {
    const slug = 'test-slug';
    mockNodeUpdate.mockRejectedValueOnce(new Error('Update failed'));

    await removeNodeCoverImage(slug);

    expect(mockMutate).toHaveBeenCalledTimes(2); // Once for optimistic update, once for revert
    expect(console.error).toHaveBeenCalled();
  });
});

EOF

# Run the test file
npx jest test_removeNodeCoverImage.ts

This test script will help verify that the removeNodeCoverImage function correctly handles both successful and error scenarios.


72-78: ⚠️ Potential issue

Address potential issue with query parameters in nodeListAllKeyFn

The updated function improves readability by using template literals. However, the comment on line 76 suggests a potential issue when handling keys with query parameters.

To address this, consider implementing a more robust key matching logic. Here's a possible solution:

const nodeListAllKeyFn = (key: Arguments) => {
  if (!Array.isArray(key) || typeof key[0] !== 'string') return false;
  
  const [baseUrl] = key[0].split('?');
  return baseUrl.startsWith(nodeListKey) && !baseUrl.startsWith(`${nodeListKey}/`);
};

This approach splits the key by the query parameter separator and only checks the base URL part, which should handle cases with query parameters correctly.

To verify this solution, we can run the following test:

This test script will help verify that the proposed solution correctly handles various scenarios, including those with query parameters.


295-314: 🛠️ Refactor suggestion

Improve type safety and clarity in mergePrimaryImageAsset

The mergePrimaryImageAsset function is a good addition for handling optimistic updates of the primary image asset. However, there are a few areas where it could be improved:

  1. The function's return type could be more specific.
  2. The logic for determining the parentAsset could be clearer.
  3. There's no handling for the case where oldNode.primary_image is undefined.

Consider the following improvements:

  1. Specify a more precise return type:
function mergePrimaryImageAsset(
  oldNode: Node,
  coverConfig: CoverImageArgs
): Pick<Node, "primary_image" | "meta"> {
  // ... function body ...
}
  1. Clarify the parentAsset logic:
const parentAsset = oldNode.primary_image?.parent ?? oldNode.primary_image ?? null;
  1. Handle the case where oldNode.primary_image is undefined:
if (coverConfig.isReplacement || !oldNode.primary_image) {
  return {
    primary_image: coverConfig.asset,
    meta: { ...oldNode.meta, coverImage: coverConfig.isReplacement ? null : coverConfig.config },
  };
}

Could you clarify the intended behavior when oldNode.primary_image is undefined? Should it be treated the same as a replacement, or should it be handled differently?

To verify these changes, we can add some unit tests:

This test script will help verify that the mergePrimaryImageAsset function correctly handles various scenarios, including replacements, non-replacements, and cases where the original node has no primary image.

internal/ent/node.go (7)

43-44: LGTM: New field for primary asset added

The addition of the PrimaryAssetID field as a pointer to xid.ID is consistent with other ID fields in the Node struct and allows for optional primary asset association.


65-66: LGTM: New edge for primary image added

The addition of the PrimaryImage edge in the NodeEdges struct and the corresponding increase in the loadedTypes array size are appropriate changes to support the new primary asset feature.

Also applies to: 81-81


187-188: LGTM: Scan value handling for primary asset ID added

The addition of the case for node.FieldPrimaryAssetID in the scanValues method is implemented correctly. It uses sql.NullScanner with a new xid.ID, which is consistent with how other ID fields are handled in this method.


275-281: LGTM: Assignment handling for primary asset ID added

The addition of the case for node.FieldPrimaryAssetID in the assignValues method is implemented correctly. It properly checks for the correct type, handles the nullable nature of the field, and is consistent with how other nullable ID fields are handled in this method.


330-333: LGTM: New query method for primary image added

The QueryPrimaryImage method is implemented correctly, following the same pattern as other query methods in the Node struct. It properly returns an AssetQuery object, allowing for querying the primary image associated with a node.


Line range hint 1-437: Summary: Primary asset feature successfully implemented

The changes in this file successfully implement the primary asset feature for the Node entity. The additions include:

  1. A new PrimaryAssetID field in the Node struct
  2. A new PrimaryImage edge in the NodeEdges struct
  3. A new PrimaryImageOrErr method for error handling
  4. Updates to scanValues and assignValues methods to handle the new field
  5. A new QueryPrimaryImage method for querying the primary image

These changes are consistent with the existing code structure and patterns, and they effectively support the PR objective of adding a primary asset cover image to library pages.


115-124: LGTM: New method for primary image edge added

The PrimaryImageOrErr method is implemented correctly, following the same pattern as other OrErr methods in the struct. It properly handles cases where the edge is loaded or not loaded.

Please verify that the index 3 used for loadedTypes[3] is correct for the PrimaryImage edge. You can run the following command to check the order of edges in the NodeEdges struct:

✅ Verification successful

Verified: Index for PrimaryImage edge is correct

The loadedTypes[3] correctly corresponds to the PrimaryImage edge in the NodeEdges struct.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
rg --type go "type NodeEdges struct {" -A 20 internal/ent/node.go

Length of output: 177


Script:

#!/bin/bash
rg --type go "type NodeEdges struct \{" -A 20 internal/ent/node.go

Length of output: 1103

internal/ent/node/node.go (6)

37-38: LGTM: New constants for primary image relationship

The addition of these constants is well-structured and consistent with the existing codebase. They provide the necessary definitions for the new primary image relationship.

Also applies to: 51-52, 82-88


137-137: LGTM: Added FieldPrimaryAssetID to Columns slice

The addition of FieldPrimaryAssetID to the Columns slice is correct and necessary for proper SQL operations involving the new field.


262-265: LGTM: New ordering function for primary_asset_id

The ByPrimaryAssetID function is correctly implemented, following the established pattern for ordering functions in this file.


305-310: LGTM: New ordering function for primary_image field

The ByPrimaryImageField function is correctly implemented, consistent with other similar functions in the file. It properly utilizes the newPrimaryImageStep() function for ordering.


409-415: LGTM: New SQL graph step function for primary image

The newPrimaryImageStep function is correctly implemented, following the established pattern for SQL graph step functions. It properly defines the Many-to-One relationship for the primary image.


Line range hint 1-458: Summary: Successful implementation of primary image relationship

The changes in this file successfully implement the primary image relationship for the Node entity. New constants, fields, and functions have been added consistently with the existing code structure. These additions enable querying and ordering by the primary image, which aligns with the PR objective of adding a primary asset cover image to library pages.

However, to ensure completeness:

  1. Verify that corresponding changes have been made in related files (e.g., schema definitions, migrations).
  2. Confirm that the new relationship is properly handled in the business logic layer.
  3. Ensure that appropriate tests have been added or updated to cover this new functionality.

To verify the completeness of the implementation, please run the following script:

internal/ent/node_query.go (4)

36-36: LGTM: Addition of withPrimaryImage field

The addition of the withPrimaryImage field to the NodeQuery struct is consistent with the PR objective of introducing a primary image relation for nodes. This field will be used for eager loading of the primary image asset.


542-551: LGTM: Implementation of WithPrimaryImage method

The WithPrimaryImage method is correctly implemented. It follows the established pattern for other relationships in the NodeQuery struct and properly sets up the withPrimaryImage field with the provided options. This method will allow users to specify options for eager loading of primary images when constructing a query.


697-701: LGTM: Updates to sqlAll method for primary image handling

The changes to the sqlAll method correctly integrate the new primary image relationship into the existing query execution logic. The loadedTypes array is properly updated to include the new relationship, and the condition to load the primary image is added following the established pattern for other relationships. These changes ensure that the primary image will be eagerly loaded when requested.

Also applies to: 750-755


1258-1260: LGTM: Update to querySpec method for primary image handling

The changes to the querySpec method correctly add handling for the withPrimaryImage field. The new condition ensures that the PrimaryAssetID column is included in the query when the primary image relationship is being eagerly loaded. This implementation follows the established pattern for other relationships and is consistent with the overall design.

internal/ent/node_create.go (4)

134-146: New methods for PrimaryAssetID look good

The new methods SetPrimaryAssetID and SetNillablePrimaryAssetID are correctly implemented and follow the existing patterns in the codebase. They provide a way to set the "primary_asset_id" field, which aligns with the PR objective of adding a primary asset cover image to library pages.


807-823: New upsert methods for PrimaryAssetID look good

The new methods SetPrimaryAssetID, UpdatePrimaryAssetID, and ClearPrimaryAssetID in the NodeUpsert struct are correctly implemented. They follow the existing patterns in the codebase and provide the necessary functionality for handling the "primary_asset_id" field during upsert operations. This addition aligns with the PR objective of adding support for a primary asset cover image.


1064-1083: New single-row upsert methods for PrimaryAssetID look good

The new methods SetPrimaryAssetID, UpdatePrimaryAssetID, and ClearPrimaryAssetID in the NodeUpsertOne struct are correctly implemented. They follow the existing patterns in the codebase and provide the necessary functionality for handling the "primary_asset_id" field during single-row upsert operations. This addition complements the bulk upsert methods and ensures consistent handling of the primary asset field across different operation types.


1499-1518: New bulk upsert methods for PrimaryAssetID look good and complete the implementation

The new methods SetPrimaryAssetID, UpdatePrimaryAssetID, and ClearPrimaryAssetID in the NodeUpsertBulk struct are correctly implemented. They follow the existing patterns in the codebase and provide the necessary functionality for handling the "primary_asset_id" field during bulk upsert operations.

Overall, these additions to the node_create.go file comprehensively implement the support for a primary asset (cover image) in the Node entity. The new methods cover all necessary operations: create, update, upsert (both single and bulk), and clear. This implementation aligns well with the PR objective of adding a primary asset cover image to library pages.

The consistent implementation across different operation types (create, single upsert, bulk upsert) ensures that the primary asset can be handled correctly in various scenarios, providing a robust solution for managing cover images in the library pages.

internal/ent/mutation.go (22)

3765-3769: LGTM: New fields for parent-child asset relationship.

The new fields parent, clearedparent, assets, removedassets, and clearedassets are correctly implemented to support the parent-child relationship for assets. This aligns with the PR objective of enhancing asset management.


4564-4564: LGTM: Updated Fields method.

The Fields method has been correctly updated to include 7 fields, which aligns with the new fields added to the struct.


4583-4585: LGTM: ParentAssetID added to Fields method.

The ParentAssetID field has been correctly added to the list of fields returned by the Fields method. This is consistent with the new parent-child relationship implementation for assets.


4606-4607: LGTM: ParentAssetID case added to switch statement.

The asset.FieldParentAssetID case has been correctly added to the switch statement. This is consistent with the new parent-child relationship implementation for assets.


4629-4630: LGTM: ParentAssetID case added to switch statement.

The asset.FieldParentAssetID case has been correctly added to the switch statement for retrieving old field values. This is consistent with the new parent-child relationship implementation for assets.


4682-4688: LGTM: ParentAssetID case added to switch statement for setting field values.

The asset.FieldParentAssetID case has been correctly added to the switch statement for setting field values. The implementation includes proper type checking before setting the value, which is a good practice for maintaining data integrity.


4737-4739: LGTM: ParentAssetID added to ClearedFields method.

The ParentAssetID field has been correctly added to the list of cleared fields in the ClearedFields method. This is consistent with the new parent-child relationship implementation for assets.


4757-4759: LGTM: ParentAssetID case added to switch statement for clearing nullable fields.

The asset.FieldParentAssetID case has been correctly added to the switch statement for clearing nullable fields. This is consistent with the new parent-child relationship implementation for assets.


4786-4795: LGTM: ParentAssetID case added to reset switch and AddedEdges method updated.

The asset.FieldParentAssetID case has been correctly added to the switch statement for resetting fields. Additionally, the AddedEdges method has been updated to include 7 edges, which is consistent with the new relationships added to the asset struct.


4808-4813: LGTM: Parent and Assets edges added to AddedEdges method.

The Parent and Assets edges have been correctly added to the list of added edges in the AddedEdges method. This is consistent with the new parent-child relationship implementation for assets.


4846-4855: LGTM: Parent and Assets edge cases added to edge IDs switch statement.

The asset.EdgeParent and asset.EdgeAssets cases have been correctly added to the switch statement for edge IDs. The implementation includes proper null checks before returning the IDs, which is a good practice for preventing null pointer exceptions.


4868-4868: LGTM: RemovedEdges method updated.

The RemovedEdges method has been correctly updated to include 7 edges, which is consistent with the new relationships added to the asset struct.


4878-4880: LGTM: Assets edge added to RemovedEdges method.

The Assets edge has been correctly added to the list of removed edges in the RemovedEdges method. This is consistent with the new parent-child relationship implementation for assets.


4909-4914: LGTM: Assets edge case added to removed edge IDs switch statement.

The asset.EdgeAssets case has been correctly added to the switch statement for removed edge IDs. This is consistent with the new parent-child relationship implementation for assets.


4927-4927: LGTM: ClearedEdges method updated.

The ClearedEdges method has been correctly updated to include 7 edges, which is consistent with the new relationships added to the asset struct.


4940-4945: LGTM: Parent and Assets edges added to ClearedEdges method.

The Parent and Assets edges have been correctly added to the list of cleared edges in the ClearedEdges method. This is consistent with the new parent-child relationship implementation for assets.


4964-4967: LGTM: Parent and Assets edge cases added to edge clearing status switch statement.

The asset.EdgeParent and asset.EdgeAssets cases have been correctly added to the switch statement for edge clearing status. This is consistent with the new parent-child relationship implementation for assets.


4981-4983: LGTM: Parent edge case added to clear unique edges switch statement.

The asset.EdgeParent case has been correctly added to the switch statement for clearing unique edges. This is consistent with the new parent-child relationship implementation for assets.


5004-5009: LGTM: Parent and Assets edge cases added to reset edges switch statement.

The asset.EdgeParent and asset.EdgeAssets cases have been correctly added to the switch statement for resetting edges. This is consistent with the new parent-child relationship implementation for assets.


14709-14710: LGTM: New fields for primary image added.

The new fields primary_image and clearedprimary_image have been correctly added to the struct. This aligns with the PR objective of adding a primary asset cover image to library pages.


Line range hint 15844-16430: LGTM: Consistent updates for primary image field and relationship.

The various methods and switch statements have been correctly updated to include the new primary image field and relationship. These changes are consistent throughout the file and follow the established patterns in the codebase. Proper null checks and error handling are included where necessary.

The updates include:

  1. Addition of primary image field to the Fields method
  2. Updates to AddedEdges, RemovedEdges, and ClearedEdges methods
  3. New cases in switch statements for handling different operations on the primary image field and edge

These changes collectively support the PR objective of adding a primary asset cover image to library pages.


Line range hint 1-16430: Overall assessment: Well-implemented changes for asset relationships and primary images.

The changes in this file consistently implement two main features:

  1. A parent-child relationship for assets
  2. A primary image for nodes

These implementations align well with the PR objective of adding a primary asset cover image to library pages. The code quality is good, with proper error handling and consistent implementation throughout the file. The new methods and fields follow the established patterns in the codebase, which will make it easier for developers familiar with the project to understand and work with these new features.

A few minor suggestions were made for improving documentation, but overall, the changes are well-structured and ready for integration.

app/transports/http/openapi/server_gen.go (13)

Line range hint 23-27: LGTM: Addition of nullable package import

The addition of the "github.com/oapi-codegen/nullable" import is appropriate and aligns with the need to handle nullable fields in OpenAPI specifications.


460-463: LGTM: Addition of Parent field to Asset struct

The addition of the Parent *Asset field with the omitempty JSON tag is well-implemented. This change introduces a self-referential structure for Assets, allowing for a parent-child relationship. This aligns with the PR objective of adding a recursive backreference to Assets for change versioning.


1453-1456: LGTM: Addition of Parent and PrimaryImage fields to Node struct

The addition of Parent *Node and PrimaryImage *Asset fields with omitempty JSON tags is well-implemented. These changes introduce a self-referential structure for Nodes and allow for a primary image association, directly aligning with the PR objective of adding a primary image (cover image) to library pages.


1493-1496: Please clarify the purpose of this duplicate code

This code segment is identical to the previous one (lines 1453-1456). Could you please clarify if this duplication is intentional? If it's defining the same fields for different structs or interfaces, it might be beneficial to add a comment explaining the purpose of the duplication.


1526-1530: LGTM: Addition of PrimaryImageAssetId field

The addition of the PrimaryImageAssetId *AssetID field with the omitempty JSON tag is well-implemented. This change is consistent with the addition of the PrimaryImage field in the Node struct and is appropriate for update operations. The use of AssetID instead of the full Asset struct is a good choice for this context.


1568-1572: LGTM: Addition of nullable PrimaryImageAssetId field

The addition of the PrimaryImageAssetId nullable.Nullable[NullableIdentifier] field with the omitempty JSON tag is well-implemented. This change is consistent with the previous additions related to primary images and provides enhanced flexibility in handling the primary image asset ID. The use of nullable.Nullable allows for explicit representation of null values, which can be beneficial in certain API scenarios.


1690-1694: LGTM: Addition of NullableIdentifier type

The addition of the NullableIdentifier type as an alias for string is a good implementation. This new type, likely used in conjunction with the nullable.Nullable type, provides a clear way to represent identifiers that can be null. The implementation as a string alias is simple and effective.


2672-2676: LGTM: Addition of ParentAssetIDQuery type

The addition of the ParentAssetIDQuery type as an alias for string is a good implementation. This new type is likely used for querying parent asset IDs, supporting the new parent-child relationship for assets introduced earlier. The implementation as a string alias is simple and consistent with other ID types in the codebase.


3060-3068: LGTM: Well-documented addition of ParentAssetId field

The addition of the ParentAssetId *ParentAssetIDQuery field is well-implemented and thoroughly documented. The detailed comment provides clear guidance on how to use this field for uploading new versions of existing assets, supporting the new parent-child relationship for assets. The use of a pointer and omitempty tags in both form and JSON tags is appropriate for an optional field.


6161-6176: LGTM: Proper handling of new ParentAssetId parameter

The addition of logic to handle the new ParentAssetId parameter is well-implemented. The code follows the existing patterns for parameter handling, including proper nil checks and error handling. The query parameter building process is consistent with other parameters in the function.


18168-18176: LGTM: Proper binding of new ParentAssetId query parameter

The addition of logic to bind the new ParentAssetId query parameter is well-implemented. The code follows the existing patterns for parameter binding, using the appropriate binding function for query parameters. The error handling and error message formatting are consistent with other parameter bindings in the function.


27628-27944: LGTM: Updated Swagger specification

The Swagger specification has been updated, which is crucial for maintaining accurate API documentation. This update likely reflects all the changes made to the API in this PR.

However, as the content is encoded, it's challenging to verify the exact changes in this review. It would be beneficial to manually review the decoded Swagger spec to ensure all API changes are accurately represented.


1626-1628: ⚠️ Potential issue

LGTM: PrimaryImage addition, but there's a typo nearby

The addition of the PrimaryImage *Asset field is consistent with the PR's objective and earlier changes.

However, there's a typo in the nearby Recomentations field. It should be corrected:

- Recomentations DatagraphItemList `json:"recomentations"`
+ Recommendations DatagraphItemList `json:"recommendations"`

Likely invalid or redundant comment.

app/resources/asset/asset.go (3)

22-28: Addition of AddVersion method is well-structured.

The new AddVersion method in the Repository interface extends the asset functionality appropriately and follows the existing code conventions.


61-61: Confirm the assignment of the Parent field in Asset struct.

Assigning Parent: parent, in the FromModel function ensures the Parent field is correctly initialized. Verify that parent accurately reflects the presence or absence of a parent asset.


46-46: ⚠️ Potential issue

Manage recursive serialization due to the Parent field.

Introducing Parent opt.Optional[Asset] to the Asset struct creates a recursive type. Ensure that serialization (e.g., JSON marshaling) handles the Parent field properly to prevent infinite recursion or stack overflow errors.

Run the following script to identify instances where Asset is serialized without handling the Parent field:

internal/deletable/deletable.go (2)

49-51: Function Skip[T any] looks good

The implementation correctly provides an opt-out mechanism for cases where the inbound type is not a nullable type. It effectively handles the optional value without introducing unnecessary complexity.


53-55: Method Get() is properly implemented

The Get method accurately returns the optional value and the delete flag, allowing callers to determine the state of the Value[T] instance.

app/services/asset/asset_upload/uploader.go (1)

52-52: LGTM! Addition of 'ParentID' field to 'Options' struct

The inclusion of ParentID as an optional field allows the uploader to handle versioning, aligning with the PR objective to support asset versioning.

web/src/screens/library/LibraryPageScreen/useLibraryPageScreen.ts (12)

10-10: Avoid importing from internal modules; use the public API to import AbstractCropperRef.

Importing directly from internal paths like 'react-advanced-cropper/dist/components/AbstractCropper' can lead to fragility in the codebase, as internal structures can change without notice. Instead, if AbstractCropperRef is exported from the main module, you should import it directly from 'react-advanced-cropper' or use the recommended public API as per the library's documentation.


37-43: LGTM!

The CoverImageFormSchema is correctly defined using Zod to validate the cover image data. It allows either a CoverImageSchema object or an object with an asset_id string field.


54-54: LGTM!

The FormSchema is updated to include an optional coverImage field of type CoverImageFormSchema. This allows the form to handle cover image data when submitted.


74-75: LGTM!

The cropperRef is correctly defined using useRef with a type of FixedCropperRef. This ref will be used to access the cropper instance and its methods.


113-119: LGTM!

The primaryAssetEditingURL is correctly derived using the getAssetURL function. It uses the parent asset path if available, otherwise falls back to the primary_image path. This ensures that the original image is used for editing.


121-121: LGTM!

The primaryAssetURL is correctly derived using the getAssetURL function with the primary_image path. This URL represents the current cover image being displayed.


123-123: LGTM!

The initialCoverCoordinates are correctly parsed from the node's metadata using the parseNodeMetadata function. This provides the initial crop coordinates for the cover image.


149-209: LGTM!

The uploadCroppedCover function is implemented correctly:

  • It checks if the cropperRef is available and throws an error if not.
  • It retrieves the canvas from the cropper and throws an error if not available.
  • It gets the crop coordinates from the cropper and throws an error if not available.
  • It converts the canvas to a Blob using toBlob method.
  • It determines the parent_asset_id based on whether the current primary image is a copy or not.
  • It uploads the cropped image using assetUpload function with the appropriate parent_asset_id.
  • It returns an object with the replacement status, crop coordinates, and the uploaded asset.

214-214: LGTM!

The handleSubmit function is updated to call the uploadCroppedCover function and await its result before updating the node. This ensures that the cropped cover image is uploaded and associated with the node.


303-306: LGTM!

The useLibraryPageScreen hook correctly returns the necessary properties:

  • cropperRef: The ref to access the cropper instance.
  • primaryAssetURL: The URL of the current cover image being displayed.
  • primaryAssetEditingURL: The URL of the original image used for editing.
  • initialCoverCoordinates: The initial crop coordinates for the cover image.

156-157: Use Error objects when rejecting promises for better error handling.

When rejecting a Promise, it's advisable to use an Error object instead of a string. This provides better stack traces and aids in debugging.

Apply this diff to improve error handling:

-      reject("An unexpected error occurred with the image editor.");
+      reject(new Error("An unexpected error occurred with the image editor."));

170-172: Use Error objects when rejecting promises for better error handling.

When rejecting a Promise, it's advisable to use an Error object instead of a string. This provides better stack traces and aids in debugging.

Apply this diff to improve error handling:

-          reject("An unexpected error occurred with the image editor.");
+          reject(new Error("An unexpected error occurred with the image editor."));
internal/ent/asset.go (10)

35-36: Addition of 'ParentAssetID' field to Asset struct

The new field ParentAssetID *xid.ID correctly introduces a parent-child relationship in the Asset struct, allowing assets to reference a parent asset. This change is well-implemented and follows the existing coding patterns.


53-56: Inclusion of 'Parent' and 'Assets' in AssetEdges

Adding Parent and Assets to AssetEdges enables traversal of parent and child assets within the entity graph. This enhances the navigability and relational integrity of assets.


61-61: Verify 'loadedTypes' array indices after adding new edges

The loadedTypes array size has increased to [7]bool to accommodate the new edges. Please verify that all indices used in edge loading and checking (e.g., in ParentOrErr, AssetsOrErr, EventOrErr) correctly correspond to their respective edges to prevent any misalignment issues.


102-111: Confirm correct index usage in 'ParentOrErr' method

In the ParentOrErr method, the index e.loadedTypes[4] is used to check if the 'parent' edge has been loaded. Ensure that this index correctly corresponds to the 'parent' edge in the updated loadedTypes array after adding new edges.


113-120: Confirm correct index usage in 'AssetsOrErr' method

The AssetsOrErr method uses the index e.loadedTypes[5] to verify if the 'assets' edge is loaded. Please confirm that this index aligns with the 'assets' edge in the loadedTypes array to ensure proper edge loading.


125-127: Ensure updated index in 'EventOrErr' method corresponds to 'event' edge

The EventOrErr method now references e.loadedTypes[6] for the 'event' edge. Verify that this index matches the 'event' edge in the updated loadedTypes array to maintain accurate edge loading behavior.


136-137: Proper handling of 'ParentAssetID' in 'scanValues'

The addition of asset.FieldParentAssetID in the scanValues function ensures that the ParentAssetID field is correctly scanned from SQL rows. The use of sql.NullScanner accommodates potential null values, which is appropriate for an optional field.


207-213: Correct assignment of 'ParentAssetID' in 'assignValues'

In the assignValues method, the assignment logic for ParentAssetID properly checks for null values and assigns the scanned xid.ID when valid. This implementation handles the optional nature of ParentAssetID effectively.


247-250: Addition of 'QueryParent' method enhances parent asset retrieval

The QueryParent method allows for querying the 'parent' edge of an Asset entity. This addition is consistent with existing query methods and facilitates easy retrieval of a parent asset.


252-255: Introduction of 'QueryAssets' method for accessing child assets

The QueryAssets method enables querying the 'assets' edge to retrieve child assets associated with a parent asset. This method is implemented following the established patterns in the codebase.

internal/ent/asset/asset.go (10)

30-31: Addition of FieldParentAssetID constant

The constant FieldParentAssetID is correctly defined to represent the parent_asset_id field in the database.


40-43: Addition of EdgeParent and EdgeAssets constants

The constants EdgeParent and EdgeAssets are appropriately added to represent the parent and assets edges in mutations.


70-77: Definition of parent and assets table and column constants

The constants ParentTable, ParentColumn, AssetsTable, and AssetsColumn are correctly defined for the self-referential relationship in the assets table.


96-96: Including FieldParentAssetID in Columns slice

The FieldParentAssetID is properly added to the Columns slice for validation and query purposes.


167-170: Addition of ByParentAssetID ordering function

The function ByParentAssetID allows ordering results by the parent_asset_id field. The implementation is correct and follows existing patterns.


221-226: Addition of ByParentField ordering function

The function ByParentField enables ordering the results by a specified field of the parent asset. It is correctly implemented using newParentStep().


228-233: Addition of ByAssetsCount ordering function

The function ByAssetsCount facilitates ordering by the count of child assets. The implementation is accurate.


235-240: Addition of ByAssets ordering function

The function ByAssets enables ordering results based on terms related to the assets edge. The function is correctly implemented and aligns with existing code.


283-289: Definition of newParentStep function

The newParentStep function defines the SQL graph step for the parent edge. The self-referential many-to-one edge is correctly established.


290-296: Definition of newAssetsStep function

The newAssetsStep function defines the SQL graph step for the assets edge. The self-referential one-to-many edge is accurately set up.

internal/ent/asset/where.go (12)

84-87: Addition of ParentAssetID predicate function

The function ParentAssetID has been correctly added, mirroring the existing pattern for equality predicates on fields. This enhances usability and maintains consistency across the codebase.


354-358: Implementation of ParentAssetIDEQ equality predicate

The ParentAssetIDEQ function properly implements the equality predicate for the parent_asset_id field, allowing precise queries where parent_asset_id equals a specific value.


359-363: Implementation of ParentAssetIDNEQ inequality predicate

The ParentAssetIDNEQ function correctly provides the not-equal predicate for the parent_asset_id field, enabling queries for assets where parent_asset_id does not equal a specific value.


364-368: Addition of ParentAssetIDIn predicate

The ParentAssetIDIn function is appropriately implemented, allowing querying assets whose parent_asset_id is in a set of specified IDs.


369-373: Addition of ParentAssetIDNotIn predicate

The ParentAssetIDNotIn function correctly implements the not-in predicate for the parent_asset_id field, enabling the selection of assets whose parent_asset_id is not within a specified set.


374-382: Implementation of comparison predicates for ParentAssetID

The functions ParentAssetIDGT, ParentAssetIDGTE, ParentAssetIDLT, and ParentAssetIDLTE are correctly added, providing greater-than, greater-than-or-equal, less-than, and less-than-or-equal predicates for the parent_asset_id field. These predicates enhance querying capabilities, although their practical use with ID fields may be limited.


394-432: Inclusion of string predicates for ParentAssetID field

The functions ParentAssetIDContains, ParentAssetIDHasPrefix, ParentAssetIDHasSuffix, ParentAssetIDEqualFold, and ParentAssetIDContainsFold are added for the parent_asset_id field, providing string-based predicates. While IDs are typically unique identifiers, including these predicates can offer additional flexibility for querying, especially if the IDs have meaningful prefixes or patterns.


412-421: Null checking predicates for ParentAssetID

The functions ParentAssetIDIsNil and ParentAssetIDNotNil are correctly implemented, allowing queries to check for the presence or absence of a parent_asset_id. This is essential when dealing with optional relationships or identifying root assets without parents.


526-535: Correct addition of HasParent edge predicate

The HasParent function is properly implemented to apply the has-edge predicate on the "parent" edge, enabling queries that select assets which have a parent asset.


537-547: Implementation of HasParentWith edge predicate

The HasParentWith function correctly applies the has-edge predicate with additional conditions, allowing for more refined queries involving the "parent" edge based on specified predicates.


549-558: Addition of HasAssets edge predicate

The HasAssets function is properly added, enabling queries to select assets that have associated child assets, supporting hierarchical data retrieval.


560-570: Implementation of HasAssetsWith edge predicate

The HasAssetsWith function is correctly implemented, allowing for queries on assets with specific child assets that meet given conditions. This enhances the ability to perform complex queries within the asset hierarchy.

internal/ent/asset_create.go (10)

84-88: Implementation of SetParentAssetID method is correct

The SetParentAssetID method is correctly implemented and follows the existing patterns in the codebase.


90-96: Implementation of SetNillableParentAssetID method is correct

The SetNillableParentAssetID method properly handles nil values and is consistent with the codebase conventions.


168-172: Implementation of SetParentID method is correct

The SetParentID method correctly sets the "parent" edge to the Asset entity by ID.


174-180: Implementation of SetNillableParentID method is correct

The SetNillableParentID method appropriately handles nil values when setting the "parent" edge.


182-185: Implementation of SetParent method is correct

The SetParent method correctly sets the "parent" edge to the Asset entity.


187-191: Implementation of AddAssetIDs method is correct

The AddAssetIDs method correctly adds the "assets" edge to the Asset entity by IDs.


193-200: Implementation of AddAssets method is correct

The AddAssets method properly adds the "assets" edges to the Asset entity.


585-602: Implementation of AssetUpsert methods for parent_asset_id is correct

The SetParentAssetID, UpdateParentAssetID, and ClearParentAssetID methods in the AssetUpsert struct are correctly implemented and follow the existing patterns.


738-758: Implementation of AssetUpsertOne methods for parent_asset_id is correct

The methods SetParentAssetID, UpdateParentAssetID, and ClearParentAssetID in AssetUpsertOne are correctly implemented and align with the patterns used in other upsert operations.


1061-1081: Implementation of AssetUpsertBulk methods for parent_asset_id is correct

The bulk upsert methods SetParentAssetID, UpdateParentAssetID, and ClearParentAssetID are correctly implemented for handling the parent_asset_id field.

internal/ent/asset_query.go (11)

36-37: Adding fields for new relationships

The addition of withParent and withAssets fields to the AssetQuery struct correctly enables querying parent and child assets.


164-184: Implementation of QueryParent method

The QueryParent method is properly implemented to facilitate querying the parent asset associated with the current asset, following consistent patterns used in other query methods.


186-206: Implementation of QueryAssets method

The QueryAssets method is correctly added to enable querying child assets linked to the current asset, maintaining consistency with existing query methods.


426-427: Including new fields in Clone method

By adding withParent and withAssets to the Clone method, the query builder ensures that these configurations are duplicated correctly during cloning.


480-489: Adding WithParent method for eager loading

The WithParent method is appropriately implemented to allow eager loading of the parent asset, consistent with the existing edge-loading methods.


491-500: Adding WithAssets method for eager loading

The WithAssets method is correctly added to enable eager loading of child assets, following the established pattern for edge-loading methods.


591-597: Updating loadedTypes array

The loadedTypes array now includes withParent and withAssets, correctly tracking the loading state of the new relationships.


649-661: Handling eager loading for new edges

The code effectively handles the eager loading of withParent and withAssets, ensuring related assets are loaded when these edges are queried.


884-915: Implementing loadParent method

The loadParent function is correctly implemented to load parent assets and assign them to their respective nodes, with appropriate error handling for missing foreign keys.


916-948: Implementing loadAssets method

The loadAssets function properly loads child assets and assigns them to their parent assets, efficiently handling the relationships and potential nil references.


1012-1014: Including ParentAssetID in query specifications

By adding ParentAssetID to the node columns when withParent is used, the code ensures that necessary fields are included in the query for loading parent assets.

internal/ent/node/where.go (3)

104-107: Addition of PrimaryAssetID predicate function is consistent and correct

The PrimaryAssetID function correctly applies an equality check on the primary_asset_id field, mirroring the pattern used for other fields in the codebase.


674-753: New PrimaryAssetID predicate functions are properly implemented

The suite of predicate functions for PrimaryAssetID—including PrimaryAssetIDEQ, PrimaryAssetIDNEQ, PrimaryAssetIDIn, and others—are correctly added and follow the established conventions. The conversion of xid.ID to string in functions like PrimaryAssetIDContains is consistent with similar implementations elsewhere in the code.


933-954: HasPrimaryImage edge predicates are correctly defined

The HasPrimaryImage and HasPrimaryImageWith functions properly implement the predicates for the primary_image edge. The use of sqlgraph.NewStep and the edge configuration aligns with existing edge predicates, ensuring consistent behavior in querying relationships.

internal/ent/asset_update.go (1)

187-193: ⚠️ Potential issue

Redundant assignment in SetNillableParentID method

In the SetNillableParentID method, the assignment au = au.SetParentID(*id) is redundant since SetParentID already returns au. You can remove the assignment and simply call au.SetParentID(*id).

Apply this diff to remove the redundant assignment:

 func (au *AssetUpdate) SetNillableParentID(id *xid.ID) *AssetUpdate {
 	if id != nil {
-		au = au.SetParentID(*id)
+		au.SetParentID(*id)
 	}
 	return au
 }

Likely invalid or redundant comment.

internal/ent/client.go (3)

1332-1346: Excellent addition of the QueryParent method to the AssetClient!

This new method allows querying the parent asset of a given Asset, enabling traversal of the parent-child relationship between assets. It utilizes the sqlgraph package to define the necessary database join step.


1348-1362: Great work on adding the QueryAssets method to the AssetClient!

This method enables querying the child assets associated with a given Asset, facilitating the retrieval of related assets in a one-to-many relationship. It leverages the sqlgraph package to define the appropriate database join step.


3571-3585: Fantastic addition of the QueryPrimaryImage method to the NodeClient!

This new method allows querying the primary image associated with a given Node, enabling easy retrieval of the node's main visual representation. It utilizes the sqlgraph package to define the necessary database join step between the node and asset tables.

Comment on lines +5 to +9
export const CoverImageSchema = z.object({
top: z.number(),
left: z.number(),
});
export type CoverImage = z.infer<typeof CoverImageSchema>;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding constraints to 'top' and 'left' values.

The current schema allows any number for 'top' and 'left'. Consider adding constraints to ensure these values are within a valid range (e.g., percentages between 0 and 100).

Example:

export const CoverImageSchema = z.object({
  top: z.number().min(0).max(100),
  left: z.number().min(0).max(100),
});

This would prevent invalid positions and improve data integrity.

Comment on lines +16 to +25
export function parseNodeMetadata(
metadata: Metadata | undefined,
): NodeMetadata {
const parsed = NodeMetadataSchema.safeParse(metadata);
if (parsed.success) {
return parsed.data;
}

return {};
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding error logging and improving error handling.

While the function safely handles parsing failures by returning an empty object, this approach might hide important information about why the parsing failed. Consider the following improvements:

  1. Add error logging when parsing fails to help with debugging.
  2. Return a more informative result, such as a union type that includes both successful and error cases.

Example:

import { logger } from "@/utils/logger"; // Assume we have a logger utility

export function parseNodeMetadata(
  metadata: Metadata | undefined
): { success: true; data: NodeMetadata } | { success: false; error: z.ZodError } {
  const parsed = NodeMetadataSchema.safeParse(metadata);
  if (parsed.success) {
    return { success: true, data: parsed.data };
  }

  logger.warn("Failed to parse node metadata", parsed.error);
  return { success: false, error: parsed.error };
}

This approach provides more context when parsing fails, aiding in debugging and allowing callers to handle errors more gracefully.

Comment on lines +1 to +86
"use client";

import type { Assign } from "@ark-ui/react";
import { FileUpload } from "@ark-ui/react/file-upload";

import {
type FileUploadVariantProps,
fileUpload,
} from "@/styled-system/recipes";
import type { ComponentProps, HTMLStyledProps } from "@/styled-system/types";
import { createStyleContext } from "@/utils/create-style-context";

const { withProvider, withContext } = createStyleContext(fileUpload);

export type RootProviderProps = ComponentProps<typeof RootProvider>;
export const RootProvider = withProvider<
HTMLDivElement,
Assign<
Assign<HTMLStyledProps<"div">, FileUpload.RootProviderBaseProps>,
FileUploadVariantProps
>
>(FileUpload.RootProvider, "root");

export type RootProps = ComponentProps<typeof Root>;
export const Root = withProvider<
HTMLDivElement,
Assign<
Assign<HTMLStyledProps<"div">, FileUpload.RootBaseProps>,
FileUploadVariantProps
>
>(FileUpload.Root, "root");

export const Dropzone = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<"div">, FileUpload.DropzoneBaseProps>
>(FileUpload.Dropzone, "dropzone");

export const ItemDeleteTrigger = withContext<
HTMLButtonElement,
Assign<HTMLStyledProps<"button">, FileUpload.ItemDeleteTriggerBaseProps>
>(FileUpload.ItemDeleteTrigger, "itemDeleteTrigger");

export const ItemGroup = withContext<
HTMLUListElement,
Assign<HTMLStyledProps<"ul">, FileUpload.ItemGroupBaseProps>
>(FileUpload.ItemGroup, "itemGroup");

export const ItemName = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<"div">, FileUpload.ItemNameBaseProps>
>(FileUpload.ItemName, "itemName");

export const ItemPreviewImage = withContext<
HTMLImageElement,
Assign<HTMLStyledProps<"img">, FileUpload.ItemPreviewImageBaseProps>
>(FileUpload.ItemPreviewImage, "itemPreviewImage");

export const ItemPreview = withContext<
HTMLImageElement,
Assign<HTMLStyledProps<"div">, FileUpload.ItemPreviewBaseProps>
>(FileUpload.ItemPreview, "itemPreview");

export const Item = withContext<
HTMLLIElement,
Assign<HTMLStyledProps<"li">, FileUpload.ItemBaseProps>
>(FileUpload.Item, "item");

export const ItemSizeText = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<"div">, FileUpload.ItemSizeTextBaseProps>
>(FileUpload.ItemSizeText, "itemSizeText");

export const Label = withContext<
HTMLLabelElement,
Assign<HTMLStyledProps<"label">, FileUpload.LabelBaseProps>
>(FileUpload.Label, "label");

export const Trigger = withContext<
HTMLButtonElement,
Assign<HTMLStyledProps<"button">, FileUpload.TriggerBaseProps>
>(FileUpload.Trigger, "trigger");

export {
FileUploadContext as Context,
FileUploadHiddenInput as HiddenInput,
} from "@ark-ui/react/file-upload";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Overall improvements with one remaining issue

This refactoring significantly enhances the file-upload.tsx component:

  1. Improved type safety and consistency across all components.
  2. Standardized styling approach using withContext and withProvider.
  3. Clear and flexible exports for better usability.
  4. Enhanced modularity and maintainability.

These changes demonstrate a well-thought-out architecture and will likely improve developer experience when working with this component.

However, there's one remaining issue to address:

  • The ItemPreview component still has an inconsistency between its HTML element type and props type (lines 58-61).

Once this issue is resolved, the refactoring will be complete and highly effective.

@@ -99,6 +102,7 @@ func serialiseAsset(a asset.Asset) openapi.Asset {
Id: a.ID.String(),
Filename: a.Name.String(),
Path: path,
Parent: opt.Map(a.Parent, serialiseAsset).Ptr(),
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Potential Circular References Detected in Serialization

During verification, multiple structs were found with Parent fields that reference the same struct type. This could lead to infinite recursion during serialization if not properly handled.

  • internal/ent/node.go:62: Parent *Node
  • internal/ent/asset.go:54: Parent *Asset
  • app/transports/http/openapi/server_gen.go:461: Parent *Asset
  • app/transports/http/openapi/server_gen.go:1453: Parent *Node
  • app/transports/http/openapi/server_gen.go:1625: Parent *Node
  • app/transports/http/openapi/server_gen.go:1534: Parent *NodeSlug
  • app/transports/http/openapi/server_gen.go:1574: Slug *NodeSlug
  • app/transports/http/openapi/server_gen.go:1607: Parent *LinkReference

Please implement safeguards such as setting a maximum serialization depth or ensuring proper termination conditions to prevent potential infinite recursion and stack overflow errors.

🔗 Analysis chain

LGTM! Consider adding error handling for recursive serialization.

The addition of the Parent field to the openapi.Asset struct in the serialiseAsset function is consistent with the PR objectives. It allows including the parent asset information in the API response, enhancing asset relationships.

While the current implementation looks good, it's worth considering potential edge cases. Let's verify if there's any risk of infinite recursion:

If the script returns any matches, consider adding a depth limit to prevent potential stack overflow errors in case of circular references.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential circular references in asset relationships

# Test: Search for any circular references in asset relationships
rg --type go -e "Parent.*Parent" -e "ParentID.*ParentID"

Length of output: 2584


Script:

#!/bin/bash
# Description: Identify struct definitions with Parent fields referencing the same struct to detect potential circular references

ast-grep --lang go --pattern '
type $_ struct {
    $$$
    Parent *$_
    $$$
}'

Length of output: 20904

Comment on lines +49 to +94
async function handleFileReject({ files }: FileUploadFileRejectDetails) {
if (files.length === 0) {
return;
}

const file = files[0];
if (!file) {
console.error(
"handleFileReject: files list non-empty but first file is falsy",
);
return;
}

const accepted = acceptedMIMEs.reduce((prev: string[], curr: string) => {
const extensions = mime[curr]?.extensions;
if (!extensions) {
return prev;
}

return [...prev, ...extensions];
}, []);

const acceptedList = accepted.map((e) => `.${e}`).join(", ");

// Vast majority of the time, there will only be one error, but join anyway.
const errorMessage = file.errors
.map((error) => {
switch (error) {
case "FILE_INVALID":
return "Invalid file.";
case "FILE_TOO_LARGE":
return "File is too large.";
case "FILE_INVALID_TYPE":
return `File must be of type ${acceptedList}`;
case "FILE_TOO_SMALL":
return "File is too small.";
case "TOO_MANY_FILES":
return "Too many files.";
default:
return "An unexpected error occurred while reading the file.";
}
})
.join(", ");

toast.error(errorMessage);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Improve performance and refactor for better maintainability.

The handleFileReject function provides comprehensive error handling, but there are opportunities for improvement:

  1. Performance issue: The reduce function on lines 62-69 uses spread syntax, which can lead to O(n²) time complexity.
  2. The function is quite long and could benefit from refactoring for better readability and maintainability.

To address the performance issue, replace the spread syntax with push:

     const accepted = acceptedMIMEs.reduce((prev: string[], curr: string) => {
       const extensions = mime[curr]?.extensions;
       if (!extensions) {
         return prev;
       }

-      return [...prev, ...extensions];
+      prev.push(...extensions);
+      return prev;
     }, []);

Consider refactoring the error message generation into a separate function for better maintainability:

function getErrorMessage(error: string, acceptedList: string): string {
  switch (error) {
    case "FILE_INVALID":
      return "Invalid file.";
    case "FILE_TOO_LARGE":
      return "File is too large.";
    case "FILE_INVALID_TYPE":
      return `File must be of type ${acceptedList}`;
    case "FILE_TOO_SMALL":
      return "File is too small.";
    case "TOO_MANY_FILES":
      return "Too many files.";
    default:
      return "An unexpected error occurred while reading the file.";
  }
}

Then use it in the handleFileReject function:

const errorMessage = file.errors
  .map((error) => getErrorMessage(error, acceptedList))
  .join(", ");

This refactoring will make the function more concise and easier to maintain.

🧰 Tools
🪛 Biome

[error] 68-68: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

Comment on lines +302 to +306
builder.WriteString(", ")
if v := a.ParentAssetID; v != nil {
builder.WriteString("parent_asset_id=")
builder.WriteString(fmt.Sprintf("%v", *v))
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix formatting issue in 'String' method when 'ParentAssetID' is nil

The String method unconditionally writes a comma before checking if ParentAssetID is non-nil. If ParentAssetID is nil, this results in an extra comma, leading to inconsistent formatting. Consider adjusting the code to add the comma only when ParentAssetID is not nil.

Apply this diff to conditionally add the comma:

 builder.WriteString(", ")
-if v := a.ParentAssetID; v != nil {
+if v := a.ParentAssetID; v != nil {
+    builder.WriteString(", ")
     builder.WriteString("parent_asset_id=")
     builder.WriteString(fmt.Sprintf("%v", *v))
 }

This change ensures that the comma is only included when ParentAssetID is present, maintaining correct formatting in the string representation.

Committable suggestion was skipped due to low confidence.

Comment on lines +412 to +429
if nodes := ac.mutation.ParentIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: asset.ParentTable,
Columns: []string{asset.ParentColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(asset.FieldID, field.TypeString),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_node.ParentAssetID = &nodes[0]
_spec.Edges = append(_spec.Edges, edge)
}
if nodes := ac.mutation.AssetsIDs(); len(nodes) > 0 {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Validate that only one Parent ID is provided in AssetCreate mutation

In the AssetCreate method (lines 412-429), when setting up the parent edge, nodes := ac.mutation.ParentIDs() may contain multiple IDs. Since an asset should have only one parent, it's important to ensure that only one Parent ID is provided. Consider adding a validation check to enforce this constraint.

Apply this diff to add the validation:

 if nodes := ac.mutation.ParentIDs(); len(nodes) > 0 {
+    if len(nodes) != 1 {
+        return nil, errors.New("Asset can have only one parent")
+    }
     edge := &sqlgraph.EdgeSpec{
         Rel:     sqlgraph.M2O,
         Inverse: true,
         Table:   asset.ParentTable,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if nodes := ac.mutation.ParentIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: asset.ParentTable,
Columns: []string{asset.ParentColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(asset.FieldID, field.TypeString),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_node.ParentAssetID = &nodes[0]
_spec.Edges = append(_spec.Edges, edge)
}
if nodes := ac.mutation.AssetsIDs(); len(nodes) > 0 {
if nodes := ac.mutation.ParentIDs(); len(nodes) > 0 {
if len(nodes) != 1 {
return nil, errors.New("Asset can have only one parent")
}
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: asset.ParentTable,
Columns: []string{asset.ParentColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(asset.FieldID, field.TypeString),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_node.ParentAssetID = &nodes[0]
_spec.Edges = append(_spec.Edges, edge)
}
if nodes := ac.mutation.AssetsIDs(); len(nodes) > 0 {

Comment on lines +887 to +892
func (auo *AssetUpdateOne) SetNillableParentID(id *xid.ID) *AssetUpdateOne {
if id != nil {
auo = auo.SetParentID(*id)
}
return auo
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Redundant assignment in SetNillableParentID method

In the SetNillableParentID method of AssetUpdateOne, the assignment auo = auo.SetParentID(*id) is redundant since SetParentID already returns auo. You can remove the assignment and simply call auo.SetParentID(*id).

Apply this diff to remove the redundant assignment:

 func (auo *AssetUpdateOne) SetNillableParentID(id *xid.ID) *AssetUpdateOne {
 	if id != nil {
-		auo = auo.SetParentID(*id)
+		auo.SetParentID(*id)
 	}
 	return auo
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (auo *AssetUpdateOne) SetNillableParentID(id *xid.ID) *AssetUpdateOne {
if id != nil {
auo = auo.SetParentID(*id)
}
return auo
}
func (auo *AssetUpdateOne) SetNillableParentID(id *xid.ID) *AssetUpdateOne {
if id != nil {
auo.SetParentID(*id)
}
return auo
}

Comment on lines +166 to +184
// SetPrimaryAssetID sets the "primary_asset_id" field.
func (nu *NodeUpdate) SetPrimaryAssetID(x xid.ID) *NodeUpdate {
nu.mutation.SetPrimaryAssetID(x)
return nu
}

// SetNillablePrimaryAssetID sets the "primary_asset_id" field if the given value is not nil.
func (nu *NodeUpdate) SetNillablePrimaryAssetID(x *xid.ID) *NodeUpdate {
if x != nil {
nu.SetPrimaryAssetID(*x)
}
return nu
}

// ClearPrimaryAssetID clears the value of the "primary_asset_id" field.
func (nu *NodeUpdate) ClearPrimaryAssetID() *NodeUpdate {
nu.mutation.ClearPrimaryAssetID()
return nu
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add Validation for "primary_asset_id" in NodeUpdate

Consider adding validation to ensure that the xid.ID provided to SetPrimaryAssetID corresponds to an existing Asset entity. This will help prevent runtime errors due to invalid references and maintain data integrity.

Comment on lines +1101 to +1120
// SetPrimaryAssetID sets the "primary_asset_id" field.
func (nuo *NodeUpdateOne) SetPrimaryAssetID(x xid.ID) *NodeUpdateOne {
nuo.mutation.SetPrimaryAssetID(x)
return nuo
}

// SetNillablePrimaryAssetID sets the "primary_asset_id" field if the given value is not nil.
func (nuo *NodeUpdateOne) SetNillablePrimaryAssetID(x *xid.ID) *NodeUpdateOne {
if x != nil {
nuo.SetPrimaryAssetID(*x)
}
return nuo
}

// ClearPrimaryAssetID clears the value of the "primary_asset_id" field.
func (nuo *NodeUpdateOne) ClearPrimaryAssetID() *NodeUpdateOne {
nuo.mutation.ClearPrimaryAssetID()
return nuo
}

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add Validation for "primary_asset_id" in NodeUpdateOne

Similar to the NodeUpdate struct, please add validation in the NodeUpdateOne methods to ensure the xid.ID provided is valid and exists. This ensures consistency and prevents potential runtime errors caused by invalid Asset references.

Copy link

@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: 17

🧹 Outside diff range and nitpick comments (29)
web/src/lib/library/metadata.ts (2)

5-9: LGTM: CoverImage schema and type are well-defined.

The CoverImageSchema correctly defines the structure for a cover image position with 'top' and 'left' properties. The use of z.infer for the CoverImage type ensures consistency between the schema and the type.

Consider adding constraints to the number fields (e.g., z.number().min(0)) to ensure non-negative values for 'top' and 'left' if that aligns with your requirements.


16-25: LGTM: parseNodeMetadata function is well-implemented, with a suggestion for improvement.

The function correctly uses safeParse for validation and handles both defined and undefined input. Returning an empty object as a fallback is a reasonable approach.

Consider adding error logging when parsing fails. This could help with debugging in case of unexpected metadata formats:

export function parseNodeMetadata(
  metadata: Metadata | undefined,
): NodeMetadata {
  const parsed = NodeMetadataSchema.safeParse(metadata);
  if (parsed.success) {
    return parsed.data;
  }

  console.error('Failed to parse node metadata:', parsed.error);
  return {};
}
web/src/api/openapi-schema/asset.ts (1)

17-17: LGTM: Addition of parent property enhances Asset structure

The addition of the optional parent property of type Asset to the Asset interface is a good enhancement. It allows for the representation of hierarchical relationships between assets, which aligns with the PR objectives, particularly the consideration of adding a recursive backreference to Assets for change versioning.

However, please consider the following:

  1. Ensure that the implementation handles potential circular references to avoid infinite loops.
  2. Update any existing asset management and querying logic to account for this new hierarchical structure.
  3. Consider adding documentation or comments explaining the purpose and usage of this new property.
web/src/api/openapi-schema/parentAssetIDQueryParameter.ts (1)

5-7: Consider the implications of the current versioning strategy.

The comment about the API versioning strategy is noteworthy:

The Storyden API does not adhere to semantic versioning but instead applies a rolling strategy with deprecations and minimal breaking changes. This has been done mainly for a simpler development process and it may be changed to a more fixed versioning strategy in the future. Ultimately, the primary way Storyden tracks versions is dates, there are no set release tags currently.

While this approach may simplify the development process, it could potentially lead to challenges for API consumers. Consider the following points:

  1. Predictability: Without semantic versioning, it may be harder for consumers to anticipate the impact of updates.
  2. Dependency management: Rolling releases can complicate dependency management in client projects.
  3. Long-term support: The lack of fixed version tags might make it difficult to support older versions of the API.

It might be beneficial to revisit this strategy in the future and consider adopting a more structured versioning approach, such as semantic versioning, to provide clearer expectations for API consumers.

internal/ent/schema/asset.go (3)

31-31: LGTM! Consider adding a comment for clarity.

The addition of the parent_asset_id field is well-implemented, allowing for a hierarchical structure of assets. The use of Optional() and Nillable() is appropriate for this relationship.

Consider adding a brief comment explaining the purpose of this field for better code documentation:

+       // parent_asset_id represents the ID of the parent asset in a hierarchical structure
        field.String("parent_asset_id").GoType(xid.ID{}).Optional().Nillable(),

58-62: LGTM! Consider renaming the edge for clarity.

The new edge correctly establishes a parent-child relationship between assets, complementing the parent_asset_id field. The use of Unique() ensures a proper tree-like structure.

For improved clarity, consider renaming the edge from "assets" to "child_assets" to make it more explicit that it refers to the children of the current asset:

-       edge.To("assets", Asset.Type).
+       edge.To("child_assets", Asset.Type).
            From("parent").
            Unique().
            Field("parent_asset_id"),

This change would make the relationship more immediately clear to other developers working with this schema.


31-31: Consider additional validations for the hierarchical structure.

The introduction of a parent-child relationship for assets is a significant change that might affect various parts of the application. While the implementation looks solid, there are a few additional considerations:

  1. Preventing circular references: It might be beneficial to add a validation to ensure that an asset cannot be its own ancestor.
  2. Depth limitations: Depending on the use case, you might want to consider limiting the depth of the asset hierarchy.
  3. Cascading operations: Consider how operations like delete should behave for parent assets with children.

Would you like assistance in implementing any of these additional validations or in analyzing the potential impact on existing queries and operations?

Also applies to: 58-62

app/resources/asset/asset.go (3)

22-27: LGTM! Consider adding documentation.

The new AddVersion method is a good addition for implementing asset versioning. The method signature is consistent with other methods in the interface.

Consider adding a brief comment explaining the purpose of this method and the significance of the parent parameter.


46-46: LGTM! Consider adding documentation.

The new Parent field of type opt.Optional[Asset] is a good addition for implementing asset versioning. The use of Optional allows for nullable parent references, which is appropriate for this use case.

Consider adding a brief comment explaining the purpose of this field and its role in asset versioning.


50-51: LGTM! Consider a minor optimization.

The changes to FromModel correctly implement the new Parent field, using opt.NewPtrMap to handle the optional nature of the parent asset.

Consider moving the parent initialization closer to where it's used in the struct initialization for better readability. Also, you might want to use a named function instead of a lambda for better clarity:

func assetFromModel(a ent.Asset) Asset { return *FromModel(&a) }

func FromModel(a *ent.Asset) *Asset {
	return &Asset{
		ID: AssetID(a.ID),
		Name: Filename{
			id:    opt.New(a.ID),
			name:  a.Filename,
			hasID: true,
		},
		Size:     a.Size,
		Metadata: a.Metadata,
		Parent:   opt.NewPtrMap(a.Edges.Parent, assetFromModel),
	}
}

Also applies to: 61-61

web/src/api/openapi-schema/assetUploadParams.ts (1)

34-42: LGTM: New property parent_asset_id is well-defined and documented.

The new parent_asset_id property is correctly implemented as an optional field, maintaining backward compatibility. Its purpose and usage are clearly explained in the comment, which is great for developers using this type.

Consider adding a brief example in the comment to illustrate how this property might be used in practice. This could further enhance the clarity for developers. For instance:

/**
 * ...existing comment...
 * 
 * Example:
 * {
 *   filename: "updated_image.jpg",
 *   parent_asset_id: "asset_123"
 * }
 */
web/src/components/ui/file-upload.tsx (3)

15-22: Approval: New RootProvider enhances file upload functionality

The addition of the RootProvider component with its comprehensive type signature improves the overall structure and type safety of the file upload functionality. This aligns well with the PR's objective of enhancing library pages.

For improved readability, consider using a type alias for the complex Assign type:

type RootProviderPropsType = Assign<
  Assign<HTMLStyledProps<"div">, FileUpload.RootProviderBaseProps>,
  FileUploadVariantProps
>;

export const RootProvider = withProvider<HTMLDivElement, RootProviderPropsType>(
  FileUpload.RootProvider,
  "root"
);

This suggestion is optional and aims to enhance code maintainability.


24-31: Approval: Consistent update to Root component

The update to the Root component maintains consistency with the newly added RootProvider and enhances type safety. The use of withProvider appropriately indicates its role in providing context to child components.

For consistency with the previous suggestion and improved readability, consider using a type alias here as well:

type RootPropsType = Assign<
  Assign<HTMLStyledProps<"div">, FileUpload.RootBaseProps>,
  FileUploadVariantProps
>;

export const Root = withProvider<HTMLDivElement, RootPropsType>(
  FileUpload.Root,
  "root"
);

This suggestion is optional and aims to maintain consistency across the file.


83-86: Approval: Clear exports with minor suggestion

The export of FileUploadContext and FileUploadHiddenInput from the external library, with renaming, is appropriate. This approach allows for consistent naming within your application while utilizing the functionality from the external library.

For improved clarity, consider adding a comment explaining why these components are exported directly:

// Export these components directly as they are used without additional styling or context wrapping
export {
  FileUploadContext as Context,
  FileUploadHiddenInput as HiddenInput,
} from "@ark-ui/react/file-upload";

This suggestion is optional and aims to enhance code readability and maintainability.

app/transports/http/bindings/assets.go (1)

51-52: LGTM! Consider adding a comment for clarity.

The addition of the parentID variable aligns well with the PR objective of introducing a new asset relation. The use of opt.NewPtrMap correctly handles the optional nature of the parent asset ID.

Consider adding a brief comment explaining the purpose of this new variable:

// Extract the optional parent asset ID from the request parameters
parentID := opt.NewPtrMap(request.Params.ParentAssetId, deserialiseAssetID)
web/src/components/asset/AssetUploadAction.tsx (1)

24-125: LGTM: Well-implemented AssetUploadAction component

The component is well-structured with clear separation of concerns. It handles both file uploads and rejections effectively, and the UI dynamically changes based on the operation prop. The use of the handle function for error handling is a good practice.

One minor suggestion:

Consider enhancing the error logging in the handleFile function. Instead of just logging "no file was provided", it might be helpful to include more context, such as the length of the files array. This could help in debugging potential issues with the file selection process.

- console.error("handleFile: no file was provided", files);
+ console.error(`handleFile: no file was provided. Files array length: ${files.length}`, files);
🧰 Tools
🪛 Biome

[error] 68-68: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

web/src/screens/library/LibraryPageScreen/LibraryPageScreen.tsx (5)

3-4: Improved component structure and image handling capabilities.

The new imports and the separation of LibraryPageScreen and LibraryPage components enhance code organization and align well with the PR objectives. The addition of image-related imports supports the new cover image functionality.

Consider grouping related imports together for better readability. For example, you could group all UI component imports and all API-related imports.

Also applies to: 11-11, 13-13, 17-17, 20-22, 27-32, 34-43, 45-45


Line range hint 46-62: Well-structured component with clear separation of concerns.

The LibraryPage component effectively uses the useLibraryPageScreen hook to manage its state and handlers. The addition of image-related props (e.g., cropperRef, primaryAssetURL) aligns well with the PR objectives.

Consider using destructuring for the props parameter to make it clear which props are expected:

export function LibraryPage({ node }: Props) {
  // ... rest of the component
}

This change would improve code readability and self-documentation.


86-104: Improved UI structure for edit actions.

The conditional rendering of edit actions using HStack improves the user interface and simplifies the code structure. This change enhances the user experience by showing relevant controls based on the editing state.

For consistency, consider using a ternary operator instead of an if-else structure for the conditional rendering:

{isAllowedToEdit && (
  <HStack>
    {editing ? (
      <>
        <CancelAction type="button" onClick={handleEditMode}>
          Cancel
        </CancelAction>
        <SaveAction type="submit">Save</SaveAction>
      </>
    ) : (
      <EditAction onClick={handleEditMode}>Edit</EditAction>
    )}
    <LibraryPageMenu
      node={node}
      onVisibilityChange={handleVisibilityChange}
      onDelete={handleDelete}
    />
  </HStack>
)}

This would make the code more concise and easier to read.


122-174: Effective implementation of cover image functionality.

The use of FixedCropper for editing and Image for display effectively addresses the challenge of handling dynamic width while maintaining a fixed height for the cover image. This implementation aligns well with the PR objectives.

Consider the following improvements:

  1. Extract the FixedCropper and Image components into separate, reusable components to improve code organization and reusability.
  2. Use a constant for the height value (currently "64") to improve maintainability.
  3. Consider adding a fallback or placeholder for when primaryAssetURL is not available in non-editing mode.

Example of extracting the FixedCropper component:

const CoverImageCropper = ({ url, cropperRef, initialCoordinates }) => (
  <FixedCropper
    ref={cropperRef}
    className={css({
      maxWidth: "full",
      maxHeight: "64",
      borderRadius: "lg",
      backgroundColor: "bg.default",
    })}
    // ... other props
    src={url}
  />
);

// Usage in the main component
{editing && primaryAssetEditingURL ? (
  <Box width="full" height="64">
    <CoverImageCropper
      url={primaryAssetEditingURL}
      cropperRef={cropperRef}
      initialCoordinates={initialCoverCoordinates}
    />
  </Box>
) : (
  // ... Image component
)}

This refactoring would improve code readability and maintainability.


Line range hint 175-238: Consistent implementation of existing functionality.

The structure for title, content, and link inputs, as well as the rendering of child pages, remains consistent and works well with the new changes.

Consider adding a comment or documentation for the NodeCardRows component to explain its purpose and expected props. This would improve code maintainability and make it easier for other developers to understand the component's role in the library page structure.

internal/ent/migrate/schema.go (1)

123-123: Consider the broader impact of schema changes

The additions to both AssetsColumns and NodesColumns, along with their corresponding foreign key relationships, enhance the database schema in a consistent and logical manner. These changes support the PR objectives and add valuable functionality to the system.

However, please consider the following points:

  1. Database Migration: Ensure that a proper migration script is created to add these new columns and relationships to the existing database.

  2. Application Logic: Update the application logic to utilize these new relationships, particularly for managing asset hierarchies and setting/retrieving primary assets for nodes.

  3. Performance: Monitor query performance, especially for operations involving these new relationships, to ensure they don't introduce any significant performance overhead.

  4. Documentation: Update any relevant documentation to reflect these new relationships and their intended use in the system.

Also applies to: 137-142, 550-550, 576-581

internal/ent/mutation.go (4)

4131-4178: New methods for parent_asset_id field handling

The new methods for handling the "parent_asset_id" field in AssetMutation are well-implemented and consistent with the existing codebase. They provide comprehensive functionality for setting, getting, clearing, and resetting the parent asset ID.

One minor suggestion:

Consider adding a comment to explain the purpose of the parent-child relationship for assets. This would help developers understand the use case for this new functionality.


4382-4420: New methods for parent edge handling in AssetMutation

The new methods for handling the "parent" edge in AssetMutation are well-implemented and consistent with the existing edge handling patterns in the codebase. They provide comprehensive functionality for setting, getting, clearing, and resetting the parent edge.

One suggestion for improvement:

Consider adding a comment to explain the difference between ParentCleared() and ParentAssetIDCleared() methods, as their purposes might not be immediately clear to other developers.


15210-15258: New methods for primary_asset_id field handling in NodeMutation

The new methods for handling the "primary_asset_id" field in NodeMutation are well-implemented and consistent with the existing codebase. They provide comprehensive functionality for setting, getting, clearing, and resetting the primary asset ID.

One suggestion for improvement:

Consider adding a comment to explain the purpose and usage of the primary asset in the context of a Node. This would help developers understand the significance of this new field.


15527-15566: New methods for primary_image edge handling in NodeMutation

The new methods for handling the "primary_image" edge in NodeMutation are well-implemented and consistent with the existing edge handling patterns in the codebase. They provide comprehensive functionality for setting, getting, clearing, and resetting the primary image edge.

One suggestion for improvement:

Consider adding a comment to explain the difference between PrimaryImageCleared() and PrimaryAssetIDCleared() methods, as their purposes might not be immediately clear to other developers.

app/services/asset/asset_upload/uploader.go (1)

61-67: Consider adding unit tests for the new 'ParentID' functionality

Adding tests to cover scenarios where ParentID is provided or omitted will ensure that both code paths in the upload process function correctly.

web/src/screens/library/LibraryPageScreen/useLibraryPageScreen.ts (2)

177-177: Address the TODO regarding asset deletion

There's a TODO comment indicating uncertainty about deleting the original asset. To manage storage efficiently and maintain data consistency, consider implementing logic to delete or archive the original asset if it's no longer needed.

Would you like assistance in implementing the asset deletion logic or opening a GitHub issue to track this task?


197-199: Address the TODO about filename and ID separation

The TODO comment suggests splitting the filename from the ID on the API side to use the original name. This could enhance clarity and usability when managing assets.

Do you need help implementing this feature or should we create a GitHub issue to track this improvement?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between db2fcdb and f2e2a9c.

⛔ Files ignored due to path filters (6)
  • api/config.yaml is excluded by !**/*.yaml
  • api/openapi.yaml is excluded by !**/*.yaml
  • go.mod is excluded by !**/*.mod
  • go.sum is excluded by !**/*.sum, !**/*.sum
  • web/package.json is excluded by !**/*.json
  • web/yarn.lock is excluded by !**/yarn.lock, !**/*.lock, !**/*.lock
📒 Files selected for processing (54)
  • app/resources/asset/asset.go (3 hunks)
  • app/resources/asset/db.go (1 hunks)
  • app/resources/library/mapping.go (1 hunks)
  • app/resources/library/node.go (1 hunks)
  • app/resources/library/node_querier/node_querier.go (1 hunks)
  • app/resources/library/node_search/search.go (1 hunks)
  • app/resources/library/node_writer/node_writer.go (1 hunks)
  • app/services/asset/asset_upload/uploader.go (2 hunks)
  • app/services/library/node_mutate/node.go (3 hunks)
  • app/transports/http/bindings/assets.go (2 hunks)
  • app/transports/http/bindings/nodes.go (5 hunks)
  • app/transports/http/openapi/server_gen.go (13 hunks)
  • internal/deletable/deletable.go (1 hunks)
  • internal/ent/asset.go (7 hunks)
  • internal/ent/asset/asset.go (7 hunks)
  • internal/ent/asset/where.go (3 hunks)
  • internal/ent/asset_create.go (6 hunks)
  • internal/ent/asset_query.go (8 hunks)
  • internal/ent/asset_update.go (8 hunks)
  • internal/ent/client.go (2 hunks)
  • internal/ent/er.html (4 hunks)
  • internal/ent/migrate/schema.go (6 hunks)
  • internal/ent/mutation.go (41 hunks)
  • internal/ent/node.go (14 hunks)
  • internal/ent/node/node.go (7 hunks)
  • internal/ent/node/where.go (3 hunks)
  • internal/ent/node_create.go (6 hunks)
  • internal/ent/node_query.go (8 hunks)
  • internal/ent/node_update.go (8 hunks)
  • internal/ent/schema/asset.go (2 hunks)
  • internal/ent/schema/node.go (2 hunks)
  • web/panda.config.ts (2 hunks)
  • web/src/api/openapi-schema/asset.ts (1 hunks)
  • web/src/api/openapi-schema/assetUploadParams.ts (2 hunks)
  • web/src/api/openapi-schema/index.ts (1 hunks)
  • web/src/api/openapi-schema/nodeCommonProps.ts (2 hunks)
  • web/src/api/openapi-schema/nodeInitialProps.ts (2 hunks)
  • web/src/api/openapi-schema/nodeMutableProps.ts (2 hunks)
  • web/src/api/openapi-schema/nullableIdentifier.ts (1 hunks)
  • web/src/api/openapi-schema/parentAssetIDQueryParameter.ts (1 hunks)
  • web/src/components/asset/AssetUploadAction.tsx (1 hunks)
  • web/src/components/library/LibraryPageCoverImageControl/LibraryPageCoverImageControl.tsx (1 hunks)
  • web/src/components/library/LibraryPageCoverImageControl/useLibraryPageCoverImageControl.ts (1 hunks)
  • web/src/components/library/LibraryPageImportFromURL/LibraryPageImportFromURL.tsx (1 hunks)
  • web/src/components/ui/file-upload.tsx (1 hunks)
  • web/src/lib/library/library.ts (7 hunks)
  • web/src/lib/library/metadata.ts (1 hunks)
  • web/src/recipes/file-upload.ts (1 hunks)
  • web/src/screens/library/LibraryPageScreen/LibraryPageScreen.tsx (3 hunks)
  • web/src/screens/library/LibraryPageScreen/useLibraryPageScreen.ts (7 hunks)
  • web/src/screens/media/MediaEditModal.tsx (1 hunks)
  • web/src/screens/media/MediaEditScreen.tsx (1 hunks)
  • web/styled-system/recipes/file-upload.d.ts (1 hunks)
  • web/styled-system/recipes/file-upload.mjs (1 hunks)
🔥 Files not summarized due to errors (2)
  • app/transports/http/openapi/server_gen.go: Error: Server error: no LLM provider could handle the message
  • internal/ent/mutation.go: Error: Server error: no LLM provider could handle the message
✅ Files skipped from review due to trivial changes (1)
  • web/src/components/library/LibraryPageImportFromURL/LibraryPageImportFromURL.tsx
🚧 Files skipped from review as they are similar to previous changes (19)
  • app/resources/library/mapping.go
  • app/resources/library/node.go
  • app/resources/library/node_querier/node_querier.go
  • app/resources/library/node_search/search.go
  • app/services/library/node_mutate/node.go
  • app/transports/http/bindings/nodes.go
  • internal/ent/node_create.go
  • internal/ent/schema/node.go
  • web/panda.config.ts
  • web/src/api/openapi-schema/nodeCommonProps.ts
  • web/src/api/openapi-schema/nodeInitialProps.ts
  • web/src/api/openapi-schema/nodeMutableProps.ts
  • web/src/components/library/LibraryPageCoverImageControl/LibraryPageCoverImageControl.tsx
  • web/src/components/library/LibraryPageCoverImageControl/useLibraryPageCoverImageControl.ts
  • web/src/recipes/file-upload.ts
  • web/src/screens/media/MediaEditModal.tsx
  • web/src/screens/media/MediaEditScreen.tsx
  • web/styled-system/recipes/file-upload.d.ts
  • web/styled-system/recipes/file-upload.mjs
🧰 Additional context used
🪛 Biome
web/src/components/asset/AssetUploadAction.tsx

[error] 68-68: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (168)
web/src/lib/library/metadata.ts (2)

1-3: LGTM: Imports are appropriate for the file's functionality.

The imports for Zod and the Metadata type are correctly included and necessary for the schema definitions and parsing function.


11-14: LGTM: NodeMetadata schema and type are correctly implemented.

The NodeMetadataSchema is well-structured, including an optional coverImage property that uses the previously defined CoverImageSchema. The use of z.infer for the NodeMetadata type ensures consistency with the schema.

web/src/api/openapi-schema/nullableIdentifier.ts (2)

1-9: LGTM: Generated file header is informative and clear.

The header provides valuable information about the file's generated nature and the API's versioning strategy. This is good practice for maintaining clarity in the codebase.


11-15: LGTM: Well-defined and documented type for nullable identifiers.

The NullableIdentifier type is correctly defined as string | null, aligning with the PR objectives. The JSDoc comment provides clear documentation, including the @nullable tag, which enhances code clarity and IDE support.

web/src/api/openapi-schema/parentAssetIDQueryParameter.ts (1)

1-19: LGTM! Well-documented type definition.

The implementation of ParentAssetIDQueryParameter is clear, concise, and well-documented. The detailed comment explaining its purpose and use case is particularly helpful for developers who will be working with this type.

web/src/api/openapi-schema/assetUploadParams.ts (1)

13-13: LGTM: New import statement is correctly added.

The import for ParentAssetIDQueryParameter is properly formatted and consistent with the existing import style. It's correctly used in the new property of AssetUploadParams.

web/src/components/ui/file-upload.tsx (4)

1-13: Improved type safety and modularity in imports

The updated import statements and new utility imports enhance the type safety and modularity of the component. The introduction of Assign from "@ark-ui/react" and createStyleContext from a custom utility file suggests a more robust approach to combining types and managing styles.


33-46: Approval: Consistent and type-safe updates to Dropzone, ItemDeleteTrigger, and ItemGroup

The updates to these components demonstrate a consistent approach to enhancing type safety and context usage. The use of withContext with specific HTML element types ensures that each component correctly consumes the context provided by Root or RootProvider. The Assign type effectively combines HTML props with the base props from FileUpload, maintaining a high level of type safety.

This consistent structure across components will improve maintainability and reduce the likelihood of type-related errors in the future.


63-81: Approval: Consistent updates to Item, ItemSizeText, Label, and Trigger

The updates to these components maintain the consistent approach seen throughout the file. Each component uses withContext with the appropriate HTML element type, and the type signatures effectively combine HTML props with the base props from FileUpload using the Assign type.

This consistency in structure and type safety across all components in the file is commendable. It will greatly improve the maintainability of the code and reduce the potential for type-related errors.


48-61: ⚠️ Potential issue

Approval for ItemName and ItemPreviewImage, concern about ItemPreview

The updates to ItemName and ItemPreviewImage are consistent with the previous components, maintaining type safety and context usage.

However, the ItemPreview component still has an inconsistency between its element type (HTMLImageElement) and its props type (HTMLStyledProps<"div">). This issue was flagged in a previous review but hasn't been addressed.

To resolve this, please update the ItemPreview component as follows:

export const ItemPreview = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<"div">, FileUpload.ItemPreviewBaseProps>
>(FileUpload.ItemPreview, "itemPreview");

This change ensures consistency between the element type and the props type.

app/transports/http/bindings/assets.go (1)

59-59: LGTM! ParentID field added successfully.

The addition of the ParentID field to the asset_upload.Options struct is consistent with the earlier changes and supports the PR objective of associating assets with a parent.

web/src/components/asset/AssetUploadAction.tsx (3)

1-15: LGTM: Well-organized imports

The imports are well-structured, separating external library imports from internal project imports. All imported modules appear to be relevant to the component's functionality.


16-22: LGTM: Well-structured type definitions

The type definitions for AssetUploadActionProps and Props are clear and provide a good structure for the component's properties. The use of a union type for the operation prop enhances type safety, and extending AssetUploadActionProps in Props allows for flexible prop passing.


127-147: LGTM: Robust MIME type handling

The getMIMEs utility function is well-implemented and thoroughly handles all possible input types for the accept parameter. The comment explaining the reason for its complexity is helpful for future maintainers. The function name accurately describes its purpose, enhancing code readability.

app/resources/library/node_writer/node_writer.go (3)

76-80: LGTM: WithPrimaryImage function implementation

The WithPrimaryImage function is well-implemented and aligns with the PR objectives. It correctly uses the Option pattern and sets the primary asset ID as expected.


82-86: LGTM: WithPrimaryImageRemoved function implementation

The WithPrimaryImageRemoved function is well-implemented and addresses the suggestion from the past review comment. It correctly uses the Option pattern and clears the primary asset ID as expected.


76-86: Great addition of primary image management functions

The new WithPrimaryImage and WithPrimaryImageRemoved functions are excellent additions to the node_writer package. They provide a clean and consistent way to manage the primary image for nodes, aligning perfectly with the PR objectives of adding a primary asset cover image to library pages.

These functions:

  1. Follow the existing Option pattern, maintaining code consistency.
  2. Offer both setting and removing capabilities for the primary image.
  3. Address the suggestion from the past review comment about image removal.

These additions enhance the flexibility of node management and improve the overall functionality of the library pages.

internal/ent/er.html (4)

43-43: LGTM: Asset parent-child relationship added

The addition of parent_asset_id to the Asset entity is a good implementation. This change enables the creation of relationships between assets, which aligns with the PR objective of handling original and cropped versions of images for library page cover images.


166-166: LGTM: Primary asset reference added to Node entity

The addition of primary_asset_id to the Node entity is an excellent implementation that directly fulfills the main objective of the PR. This change allows nodes (likely representing library pages) to have a primary image, which can serve as the cover image for library pages.


244-244: LGTM: New relationships added to support cover images

The new relationships added to the ERD are well-implemented and crucial for the cover image feature:

  1. Asset |o--o{ Asset : "assets/parent": This relationship enables the parent-child structure for assets, supporting the handling of original and cropped versions of images.

  2. Node }o--o| Asset : "primary_image": This relationship directly implements the cover image feature for library pages by linking a Node (library page) to its primary Asset (cover image).

Both relationships use correct cardinality notation and are consistent with the new fields added to the entities.

Also applies to: 264-264


43-43: Summary: ERD updates successfully implement cover image feature

The changes made to the entity-relationship diagram effectively implement the cover image feature for library pages:

  1. The Asset entity now has a parent_asset_id, allowing for parent-child relationships between assets.
  2. The Node entity has a new primary_asset_id, enabling the association of a primary image with each node.
  3. New relationships have been added to support these features:
    • Asset to Asset for parent-child asset relationships
    • Node to Asset for linking primary images to nodes

These modifications provide a solid foundation for managing cover images in library pages, supporting both original and cropped versions of images. The changes are consistent with the PR objectives and well-implemented within the existing data model.

Also applies to: 166-166, 244-244, 264-264

web/src/api/openapi-schema/index.ts (1)

252-252: LGTM! New exports added successfully.

The new exports nullableIdentifier and parentAssetIDQueryParameter have been added correctly. These additions align with the PR objectives of adding a primary asset cover image to library pages.

As this file is auto-generated, please ensure that these changes are properly reflected in the OpenAPI specification. You can verify this by running the following command:

If the types are not found, please update the OpenAPI specification accordingly.

Also applies to: 258-258

internal/ent/node/node.go (5)

37-38: LGTM: New constants for primary asset and image.

The new constants for FieldPrimaryAssetID, EdgePrimaryImage, PrimaryImageTable, PrimaryImageInverseTable, and PrimaryImageColumn are well-defined and consistent with the existing naming conventions. They provide the necessary information for database operations and relationships, aligning with the PR objective of adding a primary asset cover image to library pages.

Also applies to: 51-52, 82-88


137-137: LGTM: FieldPrimaryAssetID added to Columns slice.

The addition of FieldPrimaryAssetID to the Columns slice is correct and consistent with the other fields. This ensures that the new field is properly recognized as part of the SQL columns for node fields.


262-265: LGTM: New ByPrimaryAssetID function added.

The new ByPrimaryAssetID function is well-implemented and consistent with other ordering functions in the file. It correctly uses the sql.OrderByField function and aligns with the new FieldPrimaryAssetID constant, providing a way to order results by the primary_asset_id field.


409-415: LGTM: New newPrimaryImageStep function added.

The newPrimaryImageStep function is well-implemented and consistent with other step functions in the file. It correctly uses the previously defined constants and sets up an appropriate Many-to-One (M2O) relationship for the primary image. This function properly supports the ByPrimaryImageField ordering function defined earlier.


305-310: LGTM: New ByPrimaryImageField function added, but verify step function.

The new ByPrimaryImageField function is well-implemented and consistent with other ordering functions for related entities. It correctly uses sqlgraph.OrderByNeighborTerms for ordering.

However, please note that this function depends on newPrimaryImageStep(), which should be defined later in the file.

✅ Verification successful

Verified: newPrimaryImageStep() function is defined.

The newPrimaryImageStep() function is correctly defined in internal/ent/node/node.go at line 409, satisfying the dependency for the ByPrimaryImageField function.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify that newPrimaryImageStep() is defined in the file
grep -n "func newPrimaryImageStep()" internal/ent/node/node.go

Length of output: 112

internal/ent/migrate/schema.go (2)

123-123: LGTM: Asset hierarchy implementation

The addition of the parent_asset_id column to the AssetsColumns and the corresponding foreign key relationship in the AssetsTable is well-implemented. This change allows for a hierarchical structure of assets, which can be useful for organizing related assets (e.g., original and cropped versions of an image).

The SetNull action on delete is appropriate, as it ensures that deleting a parent asset doesn't cascade to its children, maintaining data integrity.

Also applies to: 137-142


550-550: LGTM: Primary asset for nodes implemented

The addition of the primary_asset_id column to the NodesColumns and the corresponding foreign key relationship in the NodesTable is well-implemented. This change directly supports the PR objective of adding a primary asset (cover image) to library pages.

The SetNull action on delete is appropriate, as it ensures that deleting an asset doesn't affect the node itself, only removing the reference to the primary asset.

Also applies to: 576-581

internal/ent/node_query.go (4)

36-36: LGTM: New field for primary image query.

The addition of the withPrimaryImage field to the NodeQuery struct aligns with the PR objective of introducing a primary image for nodes. This field will likely be used for eager loading of the primary image asset.


542-552: LGTM: New method for eager loading of primary image.

The WithPrimaryImage method is a well-implemented addition that allows for eager loading of the primary image when querying nodes. It follows the same pattern as other similar methods in the file and provides flexibility through optional function arguments.


750-755: LGTM: Proper handling of primary image loading.

The addition to the sqlAll method correctly implements the loading of the primary image. It follows the same pattern as other relationship loading in the method and includes a proper check to ensure withPrimaryImage is set before attempting to load.


888-919: LGTM: Well-implemented loadPrimaryImage method.

The loadPrimaryImage method is a solid implementation for loading primary images for nodes. It follows the established pattern of other loading methods in the file, correctly handles potential nil PrimaryAssetID values, and includes proper error handling and query construction.

internal/ent/mutation.go (29)

3765-3769: New fields added for parent-child relationship and asset management

These new fields enhance the AssetMutation struct by introducing support for parent-child relationships and asset management within assets. The naming convention and types are consistent with the existing code style.


4564-4564: Updated field count in Fields() method

The number of fields in the Fields() method has been correctly updated from 6 to 7, likely to account for the new "parent_asset_id" field. This change ensures that the method accurately reports the number of fields in the struct.


4583-4585: Added parent_asset_id to Fields() method

The "parent_asset_id" field has been correctly added to the Fields() method. This ensures that the field is included in the list of fields when it's set, maintaining consistency with how other fields are handled in this method.


4606-4607: Added parent_asset_id to field access method

The "parent_asset_id" field has been correctly added to the switch statement in the field access method. This ensures that the field can be accessed through the generic field access method, maintaining consistency with how other fields are handled.


4629-4630: Added parent_asset_id to OldField method

The "parent_asset_id" field has been correctly added to the OldField method. This ensures that the old value of the field can be retrieved, maintaining consistency with how other fields are handled in this method.


4682-4688: Added parent_asset_id to SetField method

The "parent_asset_id" field has been correctly added to the SetField method. The implementation includes proper type checking and error handling, and it uses the SetParentAssetID method to set the value. This is consistent with how other fields are handled in this method.


4737-4739: Added parent_asset_id to ClearedFields method

The "parent_asset_id" field has been correctly added to the ClearedFields method. This ensures that the field is included in the list of cleared fields when it's cleared, maintaining consistency with how other fields are handled in this method.


4757-4759: Added parent_asset_id to ClearField method

The "parent_asset_id" field has been correctly added to the ClearField method. The implementation calls the ClearParentAssetID method to clear the field, and the error handling is consistent with other cases in this method.


4786-4795: Added parent_asset_id reset and updated edge count

The "parent_asset_id" field has been correctly added to the reset method, calling ResetParentAssetID to reset the field. The error handling is consistent with other cases in this method.

Additionally, the number of edges in the AddedEdges method has been updated from 6 to 7, likely to account for the new parent-child relationship.


4808-4813: Added parent and assets edges to AddedEdges method

The "parent" and "assets" edges have been correctly added to the AddedEdges method. This ensures that these new relationships are included in the list of added edges when they're set, maintaining consistency with how other edges are handled in this method.


4846-4855: Added parent and assets edges to EdgeIDs method

The "parent" and "assets" edges have been correctly added to the EdgeIDs method. The implementation for the "parent" edge is consistent with other single-entity edges, while the "assets" edge correctly handles multiple IDs. Both implementations follow the established patterns in the codebase.


4868-4868: Updated edge count in RemovedEdges method

The number of edges in the RemovedEdges() method has been correctly updated from 6 to 7. This change likely accounts for the new parent-child relationship or the assets edge, ensuring that the method accurately reports the number of possible edges that can be removed.


4878-4880: Added assets edge to RemovedEdges method

The "assets" edge has been correctly added to the RemovedEdges method. This ensures that the edge is included in the list of removed edges when it's removed, maintaining consistency with how other edges are handled in this method.


4909-4914: Added assets edge to RemovedEdgeIDs method

The "assets" edge has been correctly added to the RemovedEdgeIDs method. The implementation properly handles multiple IDs for the "assets" edge and follows the established patterns in the codebase for handling removed edge IDs.


4927-4927: Updated edge count in ClearedEdges method

The number of edges in the ClearedEdges() method has been correctly updated from 6 to 7. This change likely accounts for the new parent-child relationship or the assets edge, ensuring that the method accurately reports the number of possible edges that can be cleared.


4940-4945: Added parent and assets edges to ClearedEdges method

The "parent" and "assets" edges have been correctly added to the ClearedEdges method. This ensures that these edges are included in the list of cleared edges when they're cleared, maintaining consistency with how other edges are handled in this method.


4964-4967: Added parent and assets edges to EdgeCleared method

The "parent" and "assets" edges have been correctly added to the EdgeCleared method. The implementation returns the appropriate boolean values for the cleared state of these edges, maintaining consistency with how other edges are handled in this method.


4981-4983: Added parent edge to ClearEdge method

The "parent" edge has been correctly added to the ClearEdge method. The implementation calls the ClearParent method to clear the edge, and the error handling is consistent with other cases in this method.


5004-5009: Added parent and assets edges to ResetEdge method

The "parent" and "assets" edges have been correctly added to the ResetEdge method. The implementation calls the ResetParent and ResetAssets methods to reset the respective edges. The error handling is consistent with other cases in this method.


14709-14710: New fields added for primary image in NodeMutation

These new fields enhance the NodeMutation struct by introducing support for a primary image in nodes. The naming convention and types are consistent with the existing code style.


15844-15844: Updated field count in Fields() method for NodeMutation

The number of fields in the Fields() method has been correctly updated from 12 to 13, likely to account for the new "primary_asset_id" field in the Node struct. This change ensures that the method accurately reports the number of fields in the struct.


15872-15874: Added primary_asset_id to Fields() method in NodeMutation

The "primary_asset_id" field has been correctly added to the Fields() method in NodeMutation. This ensures that the field is included in the list of fields when it's set, maintaining consistency with how other fields are handled in this method.


15910-15911: Added primary_asset_id to field access method in NodeMutation

The "primary_asset_id" field has been correctly added to the switch statement in the field access method of NodeMutation. This ensures that the field can be accessed through the generic field access method, maintaining consistency with how other fields are handled.


15945-15946: Added primary_asset_id to OldField method in NodeMutation

The "primary_asset_id" field has been correctly added to the OldField method in NodeMutation. This ensures that the old value of the field can be retrieved, maintaining consistency with how other fields are handled in this method.


16025-16031: Added primary_asset_id to SetField method in NodeMutation

The "primary_asset_id" field has been correctly added to the SetField method in NodeMutation. The implementation includes proper type checking and error handling, and it uses the SetPrimaryAssetID method to set the value. This is consistent with how other fields are handled in this method.


16095-16097: Added primary_asset_id to ClearedFields method in NodeMutation

The "primary_asset_id" field has been correctly added to the ClearedFields method in NodeMutation. This ensures that the field is included in the list of cleared fields when it's cleared, maintaining consistency with how other fields are handled in this method.


16130-16132: Added primary_asset_id to ClearField method in NodeMutation

The "primary_asset_id" field has been correctly added to the ClearField method in NodeMutation. The implementation calls the ClearPrimaryAssetID method to clear the field, and the error handling is consistent with other cases in this method.


16174-16176: Added primary_asset_id reset to NodeMutation

The "primary_asset_id" field has been correctly added to the reset method in NodeMutation, calling ResetPrimaryAssetID to reset the field. The error handling is consistent with other cases in this method.


16192-16192: Updated edge count in AddedEdges method for NodeMutation

The number of edges in the AddedEdges() method of NodeMutation has been correctly updated from 8 to 9. This change likely accounts for the new primary image edge, ensuring that the method accurately reports the number of possible edges that can be added.

app/transports/http/openapi/server_gen.go (12)

Line range hint 23-27: LGTM: Addition of nullable package import

The addition of the "github.com/oapi-codegen/nullable" import is appropriate and aligns with the need to handle nullable fields in OpenAPI specifications.


460-463: LGTM: Addition of Parent field to Asset struct

The introduction of the Parent *Asset field creates a self-referential structure for Assets, enabling a parent-child relationship. The use of a pointer and the omitempty JSON tag are appropriate for this optional field.


1453-1456: LGTM: Addition of Parent and PrimaryImage fields to Node struct

The introduction of Parent *Node and PrimaryImage *Asset fields aligns well with the PR objectives. The PrimaryImage field enables the addition of a primary image to library pages, while the Parent field allows for a hierarchical structure of nodes. The use of pointers and omitempty tags is appropriate for these optional fields.


1493-1496: Please clarify the purpose of this duplicate code

This code segment is identical to the previous one (lines 1453-1456). Could you please clarify if this duplication is intentional? If it's defining the same fields for different structs or in different contexts, it might be beneficial to add a comment explaining the reason for the duplication.


1526-1528: LGTM: Addition of PrimaryImageAssetId field

The introduction of the PrimaryImageAssetId *AssetID field aligns well with the PR objectives of adding a primary image to library pages. This field allows referencing the primary image asset by its ID. The use of a pointer and the omitempty JSON tag is appropriate for this optional field.


1568-1570: LGTM: Addition of nullable PrimaryImageAssetId field

The introduction of the PrimaryImageAssetId nullable.Nullable[NullableIdentifier] field aligns with the PR objectives of adding a primary image to library pages. The use of nullable.Nullable allows for explicit representation of null values, which can be beneficial in certain scenarios. This approach differs from the pointer-based approach seen earlier but serves a similar purpose.


1690-1692: LGTM: Addition of NullableIdentifier type

The introduction of the NullableIdentifier type alias for string is a good practice. It provides a clear and specific type for nullable identifiers, which can improve code readability and type safety throughout the codebase. This change supports the earlier additions of nullable fields.


2672-2674: LGTM: Addition of ParentAssetIDQuery type

The introduction of the ParentAssetIDQuery type alias for string is a good practice. It provides a clear and specific type for parent asset ID queries, which can improve code readability and type safety. This change supports the new parent-child relationship functionality for assets.


3060-3066: LGTM: Addition of ParentAssetId field with excellent documentation

The introduction of the ParentAssetId *ParentAssetIDQuery field aligns well with the PR objectives of supporting versioning for assets. The field allows specifying a parent asset when uploading new versions of an existing asset. The use of a pointer and omitempty tags is appropriate for this optional field.

The detailed comment accompanying this field is particularly commendable. It provides clear guidance on the field's purpose, usage, and implications, which will be very helpful for developers using this API.


6161-6176: LGTM: Correct implementation of ParentAssetId query parameter handling

The added logic for handling the ParentAssetId query parameter is consistent with the previous additions and follows the established pattern for parameter processing. The implementation correctly uses runtime.StyleParamWithLocation and url.ParseQuery functions, and includes proper error handling. This change ensures that the new ParentAssetId field can be properly processed in API requests.


18168-18174: LGTM: Correct binding of parent_asset_id query parameter

The added logic for binding the parent_asset_id query parameter to the ParentAssetId field is consistent with the previous additions and follows the established pattern for parameter binding. The implementation correctly uses the runtime.BindQueryParameter function and includes proper error handling, returning an HTTP 400 error with a descriptive message in case of an invalid format. This change ensures that the new ParentAssetId field can be properly populated from API requests.


27628-27944: Update to Swagger specification acknowledged

This change updates the base64 encoded, gzipped, JSON marshaled Swagger object, which likely includes the new fields and endpoints related to the primary image and asset versioning features. While the encoded content can't be directly reviewed here, it's an expected change given the modifications made to the API.

If a detailed review of the API specification changes is required, please decode and examine the content manually or provide a decoded version for review.

internal/deletable/deletable.go (1)

13-16: Well-defined Value[T] struct for managing deletable fields.

The Value[T] struct is clearly defined and well-documented, providing a type-safe way to handle deletable fields in resources.

app/services/asset/asset_upload/uploader.go (1)

52-52: Addition of 'ParentID' field enhances functionality

The introduction of the ParentID field to the Options struct allows for optional asset versioning during uploads, aligning with the PR objectives.

internal/ent/asset.go (11)

35-36: Addition of ParentAssetID field is appropriate

The ParentAssetID field is correctly added to handle the parent asset relationship.


53-56: Correct addition of Parent and Assets edges

The Parent and Assets edges are appropriately added to represent the parent-child relationships between assets.


61-61: Ensure consistency of loadedTypes indices with new edges

The loadedTypes array size has been increased to 7 to include the new edges. Please verify that all methods using loadedTypes are updated accordingly to prevent any indexing errors.


102-111: Correct implementation of ParentOrErr method

The ParentOrErr method is properly implemented to retrieve the Parent edge, with appropriate error handling.


113-120: Correct implementation of AssetsOrErr method

The AssetsOrErr method correctly handles retrieval of the Assets edge, ensuring proper error handling when the edge is not loaded.


125-127: Verify updated index in EventOrErr method

The index e.loadedTypes[6] in the EventOrErr method has been updated. Please verify that this index correctly corresponds to the Event edge after adding new edges to ensure proper functionality.


136-137: Appropriate handling of ParentAssetID in scanValues

The scanValues method correctly handles the ParentAssetID field by using sql.NullScanner for scanning nullable values.


207-213: Correct assignment of ParentAssetID in assignValues

The assignValues method appropriately handles the assignment of the nullable ParentAssetID field, ensuring that the value is correctly set when valid.


247-250: Correct implementation of QueryParent method

The QueryParent method is correctly implemented to allow querying of the parent edge of the Asset entity.


252-255: Correct implementation of QueryAssets method

The QueryAssets method properly facilitates querying the assets edge of the Asset entity.


302-306: Correct inclusion of ParentAssetID in String method

The String method properly includes the ParentAssetID when it is not nil, ensuring accurate string representation of the Asset entity.

internal/ent/asset/asset.go (10)

30-31: Addition of FieldParentAssetID is appropriate

The definition of FieldParentAssetID aligns with existing field definitions and follows naming conventions.


40-43: Definition of EdgeParent and EdgeAssets constants

The added edge constants EdgeParent and EdgeAssets correctly define the edge names for mutations and are consistent with existing patterns.


70-77: Self-referential edge table and column definitions are correct

The definitions for ParentTable, ParentColumn, AssetsTable, and AssetsColumn appropriately establish self-referential relationships within the assets table.


96-96: Including FieldParentAssetID in Columns

Adding FieldParentAssetID to the Columns slice ensures it will be included in SQL queries and operations, which is necessary for the new field.


167-170: Implementation of ByParentAssetID function

The ByParentAssetID function correctly enables ordering of query results by the parent_asset_id field, enhancing query capabilities.


221-226: Addition of ByParentField function for flexible ordering

The ByParentField function allows ordering assets based on a specified field of their parent, providing more flexibility in queries.


228-233: Definition of ByAssetsCount function

The ByAssetsCount function enables ordering assets by the count of their related assets, which can be useful for analytics and reporting.


235-240: Addition of ByAssets function for advanced ordering

The ByAssets function allows ordering based on terms from related assets, offering more advanced query options.


283-289: Correct implementation of newParentStep function

The newParentStep function accurately defines the traversal step for the parent edge, facilitating queries involving parent assets.


290-296: Proper definition of newAssetsStep function

The newAssetsStep function correctly sets up the traversal step for the assets edge, enabling navigation to child assets in queries.

internal/ent/node.go (8)

14-14: Approved: Added necessary import for asset package

The import statement "github.com/Southclaws/storyden/internal/ent/asset" is required for the use of *Asset in the code.


65-66: Approved: Added PrimaryImage edge to NodeEdges

The PrimaryImage edge is correctly added to NodeEdges, allowing the node to reference its primary image asset.


81-81: Approved: Updated loadedTypes array size

The loadedTypes array size is updated to [10]bool to include the new PrimaryImage edge. This ensures proper tracking of loaded types during eager loading.


115-125: Approved: Added PrimaryImageOrErr method

The PrimaryImageOrErr method is added to NodeEdges, adhering to the established pattern for edge accessors. It allows safe retrieval of the PrimaryImage edge with appropriate error handling.


187-188: Approved: Handled PrimaryAssetID in scanValues

The scanValues method correctly includes a case for node.FieldPrimaryAssetID, preparing a sql.NullScanner to handle potential null values.


275-281: Approved: Assigned PrimaryAssetID in assignValues

The assignValues method properly handles the assignment of PrimaryAssetID, checking the type and validity before assignment to accommodate null values.


330-333: Approved: Added QueryPrimaryImage method

The QueryPrimaryImage method is added to the Node struct, enabling querying of the PrimaryImage edge, consistent with other query methods.


421-425: Approved: Included PrimaryAssetID in String method

The String method now includes the PrimaryAssetID when constructing the string representation of the Node, ensuring comprehensive output.

internal/ent/asset/where.go (4)

84-88: Addition of ParentAssetID Predicate Function

The function ParentAssetID correctly applies an equality check on the parent_asset_id field, consistent with other predicate functions in the file. The implementation is correct and aligns with established patterns.


354-433: Correct Implementation of Predicate Functions for ParentAssetID

The suite of predicate functions for ParentAssetID (e.g., ParentAssetIDEQ, ParentAssetIDNEQ, ParentAssetIDIn, etc.) are correctly implemented following the established patterns in the codebase. The handling of xid.ID types and conversion to strings where necessary is appropriate.


526-547: Addition of Edge Predicates HasParent and HasParentWith

The functions HasParent and HasParentWith are correctly added to handle the "parent" edge predicates. The implementation aligns with existing edge predicate patterns and will facilitate querying parent relationships effectively.


549-570: Addition of Edge Predicates HasAssets and HasAssetsWith

The functions HasAssets and HasAssetsWith are appropriately implemented for the "assets" edge predicates. This addition follows the standard pattern used in the file and extends the querying capabilities for related assets.

internal/ent/asset_create.go (7)

84-88: LGTM!

The new SetParentAssetID method correctly sets the parent_asset_id field in the AssetCreate mutation.


90-96: LGTM!

The new SetNillableParentAssetID method correctly sets the parent_asset_id field if the provided value is not nil, allowing for optional parent asset relationships.


585-601: LGTM!

The new SetParentAssetID, UpdateParentAssetID, and ClearParentAssetID methods in the AssetUpsert correctly handle setting, updating, and clearing the parent_asset_id field during upsert operations.


738-757: LGTM!

The new SetParentAssetID, UpdateParentAssetID, and ClearParentAssetID methods in the AssetUpsertOne correctly handle setting, updating, and clearing the parent_asset_id field during upsert operations for a single asset.


1061-1080: LGTM!

The new SetParentAssetID, UpdateParentAssetID, and ClearParentAssetID methods in the AssetUpsertBulk correctly handle setting, updating, and clearing the parent_asset_id field during bulk upsert operations.


412-428: Verify the cardinality of the parent asset relationship.

The code assumes a many-to-one relationship between assets and their parent, which aligns with the method names SetParentAssetID and SetNillableParentAssetID. However, it's crucial to verify that this matches the intended cardinality of the parent-child asset relationship in the schema design.

Run the following script to confirm the schema definition:


429-444: Verify the cardinality of the child assets relationship.

The code assumes a one-to-many relationship between assets and their child assets, which aligns with the method name AddAssetIDs. However, it's important to verify that this matches the intended cardinality of the parent-child asset relationship in the schema design.

Run the following script to confirm the schema definition:

internal/ent/asset_query.go (11)

36-37: Fields Added to Support Parent and Assets Relations

The addition of withParent *AssetQuery and withAssets *AssetQuery in the AssetQuery struct extends the querying capabilities to include parent assets and associated assets.


164-184: Implementation of QueryParent Method

The QueryParent method allows querying the parent asset associated with a given asset. The implementation follows the pattern used in other edge query methods and correctly establishes the many-to-one relationship.


186-207: Implementation of QueryAssets Method

The QueryAssets method enables querying child assets linked to an asset. It correctly sets up the one-to-many relationship and mirrors the structure of existing edge query methods.


426-427: Ensuring Cloned Queries Maintain New Relationships

The Clone method now includes withParent: aq.withParent.Clone() and withAssets: aq.withAssets.Clone(). This ensures that when an AssetQuery is cloned, it retains the state of the new withParent and withAssets relationships.


480-490: Addition of WithParent Method

The WithParent method enables eager loading of the parent asset when querying assets. This is consistent with the eager-loading pattern used elsewhere in the code.


491-501: Addition of WithAssets Method

The WithAssets method allows for eager loading of child assets associated with an asset. The implementation aligns with existing conventions for edge eager loading.


591-597: Updating Loaded Types for New Relationships

In the sqlAll method, the loadedTypes array now includes aq.withParent != nil and aq.withAssets != nil. This ensures that the query loader properly handles the new relationships when executing queries.


649-661: Loading Parent and Assets in Queries

The loadParent and loadAssets functions have been added to handle the loading of parent and child assets, respectively. They correctly manage foreign key relationships and populate the edges as intended.


884-915: Implementation of loadParent Function

The loadParent function effectively retrieves and assigns the parent asset to each asset in the query result. It handles cases where the ParentAssetID may be null and includes error handling for unexpected foreign keys.


916-948: Implementation of loadAssets Function

The loadAssets function appropriately handles the loading of child assets. It constructs the correct query to fetch related assets and assigns them to the corresponding parent assets.


1012-1014: Including ParentAssetID in Node Columns When Necessary

In the querySpec method, the code now adds the ParentAssetID column when aq.withParent is not nil. This ensures that the parent asset ID is available when needed for queries involving the parent relationship.

internal/ent/node/where.go (17)

104-108: Consistent addition of PrimaryAssetID predicate

The PrimaryAssetID function and its associated comment are correctly added, maintaining consistency with other field predicates. This enhances querying capabilities based on the primary_asset_id field.


674-678: Correct implementation of PrimaryAssetIDEQ predicate

The PrimaryAssetIDEQ function is properly implemented, following the standard pattern for equality predicates on the primary_asset_id field.


679-683: Accurate addition of PrimaryAssetIDNEQ predicate

The PrimaryAssetIDNEQ function correctly applies the NEQ predicate on the primary_asset_id field, enabling not-equal queries.


684-688: Proper addition of PrimaryAssetIDIn predicate

The PrimaryAssetIDIn function allows querying for nodes with primary_asset_id in a given list, which is correctly implemented.


689-693: Correct addition of PrimaryAssetIDNotIn predicate

The PrimaryAssetIDNotIn function is properly implemented to exclude specified primary_asset_id values from queries.


694-698: Addition of PrimaryAssetIDGT predicate

The PrimaryAssetIDGT function correctly applies the greater-than predicate on the primary_asset_id field.


699-703: Implementation of PrimaryAssetIDGTE predicate

The PrimaryAssetIDGTE function is correctly added, enabling greater-than-or-equal queries.


704-708: Correct addition of PrimaryAssetIDLT predicate

The PrimaryAssetIDLT function properly implements the less-than predicate on the primary_asset_id field.


709-713: Implementation of PrimaryAssetIDLTE predicate

The PrimaryAssetIDLTE function is correctly added, allowing less-than-or-equal queries.


714-719: Ensure proper conversion in PrimaryAssetIDContains predicate

The PrimaryAssetIDContains function correctly converts the xid.ID to a string for substring searches. The implementation is accurate.


720-725: Correct implementation of PrimaryAssetIDHasPrefix predicate

The PrimaryAssetIDHasPrefix function properly handles prefix checks on the primary_asset_id field.


726-731: Addition of PrimaryAssetIDHasSuffix predicate

The PrimaryAssetIDHasSuffix function is correctly implemented for suffix checks.


732-736: Correct use of IsNil predicate in PrimaryAssetIDIsNil

The PrimaryAssetIDIsNil function appropriately handles null value checks for the primary_asset_id field.


737-741: Implementation of PrimaryAssetIDNotNil predicate

The PrimaryAssetIDNotNil function is correctly added, allowing queries for non-null primary_asset_id values.


742-747: Proper addition of PrimaryAssetIDEqualFold predicate

The PrimaryAssetIDEqualFold function correctly enables case-insensitive equality checks on the primary_asset_id field.


748-753: Correct implementation of PrimaryAssetIDContainsFold predicate

The PrimaryAssetIDContainsFold function allows for case-insensitive substring searches, which is properly implemented.


933-954: Implementation of HasPrimaryImage and HasPrimaryImageWith predicates

The addition of HasPrimaryImage and HasPrimaryImageWith functions correctly enables edge predicates for the primary_image relationship, enhancing query capabilities related to the primary image of nodes.

internal/ent/asset_update.go (17)

111-117: SetNillableParentAssetID method follows best practices.

The method correctly sets the parent_asset_id field if the provided value is not nil, maintaining consistency with other nillable setter methods.


119-123: ClearParentAssetID method implementation is appropriate.

The method appropriately clears the value of the parent_asset_id field in the mutation.


187-193: SetNillableParentID method is consistent with patterns.

The method correctly sets the "parent" edge if the provided ID is not nil, aligning with established patterns for nillable edges.


195-198: SetParent method correctly assigns the parent asset.

The method sets the "parent" edge using an Asset entity, enhancing usability when the parent entity is already available.


200-213: Addition of AddAssetIDs and AddAssets methods enhances functionality.

These methods allow adding multiple child assets to the current asset, facilitating the creation of asset hierarchies.


304-308: ClearParent method correctly clears the parent edge.

The method appropriately clears the "parent" edge from the mutation.


310-329: ClearAssets, RemoveAssetIDs, and RemoveAssets methods are properly implemented.

These methods provide functionality to clear or remove child asset associations, offering flexibility in managing asset relationships.


804-823: Added methods in AssetUpdateOne for parent_asset_id field are consistent.

The methods SetParentAssetID, SetNillableParentAssetID, and ClearParentAssetID mirror those in AssetUpdate, maintaining consistency across builders.


880-898: SetParentID, SetNillableParentID, and SetParent methods are properly added to AssetUpdateOne.

These methods extend the functionality to update the "parent" edge in single-entity updates, aligning with bulk update capabilities.


899-913: Inclusion of AddAssetIDs and AddAssets methods enhances AssetUpdateOne.

Adding these methods allows for associating multiple child assets during a single-entity update, providing consistency with bulk update operations.


1003-1008: ClearParent method in AssetUpdateOne correctly clears the parent edge.

The method is implemented appropriately, allowing the parent association to be removed in single-entity updates.


1009-1029: Child asset management methods in AssetUpdateOne are correctly implemented.

Methods to clear and remove child assets (ClearAssets, RemoveAssetIDs, RemoveAssets) provide necessary functionality for managing relationships.


105-109: ⚠️ Potential issue

Confirm modifications to generated code.

This file is generated by Ent (// Code generated by ent, DO NOT EDIT.). Direct modifications may be overwritten when the code is regenerated. It's recommended to update the schema definitions and regenerate the code to include these changes.

Would you like to update the Ent schema and regenerate the code to incorporate these changes?


181-185: Validate the existence of the parent edge in the schema.

The SetParentID method sets the "parent" edge to the Asset entity by ID. Ensure that the parent edge is defined in the Asset schema.

Run the following script to confirm the parent edge definition:

#!/bin/bash
# Description: Verify the 'parent' edge in the Asset schema.

# Test: Search for the 'parent' edge definition.
rg --type go 'Edge.*"parent"' ./internal/ent/schema/asset.go

596-669: Ensure edge specifications in sqlSave align with schema definitions.

The sqlSave method includes logic for handling the "parent" and "assets" edges. Verify that the edge specifications match the relationships defined in the schema.

Run the following script to compare edge definitions:

#!/bin/bash
# Description: Compare edge definitions in 'sqlSave' with the schema.

# Test: Extract edge definitions from the schema.
rg --type go 'Edges\(\)' -A 15 ./internal/ent/schema/asset.go

# Test: Extract edge specifications from 'asset_update.go'.
rg --type go 'EdgeSpec{' -A 10 ./internal/ent/asset_update.go

1325-1398: Edge handling in sqlSave method for AssetUpdateOne looks appropriate.

The method includes logic for updating the "parent" and "assets" edges. Ensure that this logic is consistent with the schema definitions and behaves as expected during single-entity updates.

Run the following script to verify edge handling:

#!/bin/bash
# Description: Verify edge handling in 'sqlSave' for AssetUpdateOne.

# Test: Compare edge specifications with schema definitions.
rg --type go 'EdgeSpec{' -A 10 ./internal/ent/asset_update.go
rg --type go 'Edges\(\)' -A 15 ./internal/ent/schema/asset.go

105-109: Ensure SetParentAssetID aligns with schema definitions.

The method SetParentAssetID sets the parent_asset_id field. Please verify that this field is defined in the Ent schema for the Asset entity.

Run the following script to check if parent_asset_id is defined in the schema:

internal/ent/node_update.go (4)

166-185: Methods for primary_asset_id field are correctly implemented

The methods SetPrimaryAssetID, SetNillablePrimaryAssetID, and ClearPrimaryAssetID in the NodeUpdate struct follow established patterns and are correctly implemented.


399-404: Method ClearPrimaryImage is correctly implemented

The ClearPrimaryImage method correctly clears the primary_image edge in the NodeUpdate struct.


1101-1120: Methods for primary_asset_id field in NodeUpdateOne are correctly implemented

The methods SetPrimaryAssetID, SetNillablePrimaryAssetID, and ClearPrimaryAssetID in the NodeUpdateOne struct are consistent with existing mutation methods and are correctly implemented.


1334-1339: Method ClearPrimaryImage in NodeUpdateOne is correctly implemented

The ClearPrimaryImage method properly clears the primary_image edge in the NodeUpdateOne struct.

internal/ent/client.go (3)

1332-1346: Well-implemented QueryParent method in AssetClient

The QueryParent method correctly constructs a query to retrieve the parent asset associated with a given asset. The method properly sets up the many-to-one relationship using sqlgraph.NewStep with the correct parameters.


1348-1362: Well-implemented QueryAssets method in AssetClient

The QueryAssets method accurately constructs a query to fetch the child assets of a given asset, representing the one-to-many relationship. The use of sqlgraph.NewStep and the specified edge parameters are appropriate and consistent with the codebase.


3571-3585: Well-implemented QueryPrimaryImage method in NodeClient

The QueryPrimaryImage method correctly adds functionality to query the primary image associated with a node. It properly establishes the many-to-one relationship and constructs the query using sqlgraph.NewStep, aligning with existing patterns in the codebase.

Comment on lines +42 to +61
func (d *database) AddVersion(ctx context.Context,
accountID xid.ID,
parent AssetID,
filename Filename,
size int,
) (*Asset, error) {
asset, err := d.db.Asset.
Create().
SetID(filename.GetID()).
SetParentAssetID(parent).
SetFilename(filename.name).
SetSize(size).
SetAccountID(xid.ID(accountID)).
Save(ctx)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.Internal))
}

return FromModel(asset), nil
}
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

AddVersion method lacks validation for parent asset existence.

The AddVersion method in app/resources/asset/db.go does not currently verify if the parent asset exists before creating a new version. Implementing the following improvements is recommended:

  1. Check Parent Asset Exists:

    • Use the GetByID method to ensure the parent asset exists.
  2. Validate Parent Parameter:

    • Ensure the parent parameter is not empty or invalid.
  3. Handle Filename Conflicts:

    • Prevent creating a version with a filename that already exists to avoid conflicts.

Implementing these validations will enhance the method's reliability and prevent potential issues related to invalid parent assets or duplicate filenames.

🔗 Analysis chain

Consider adding validation and error handling for the new AddVersion method.

The implementation of the AddVersion method looks good and is consistent with the existing Add method. However, consider the following improvements:

  1. Add a check to ensure the parent asset exists before creating a new version.
  2. Validate the parent parameter to ensure it's not empty or invalid.
  3. Handle potential conflicts if a version with the same filename already exists.

Here's a suggested implementation with these improvements:

func (d *database) AddVersion(ctx context.Context,
	accountID xid.ID,
	parent AssetID,
	filename Filename,
	size int,
) (*Asset, error) {
	if parent == "" {
		return nil, fault.New("parent asset ID cannot be empty")
	}

	// Check if parent asset exists
	_, err := d.GetByID(ctx, parent)
	if err != nil {
		return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.InvalidArgument))
	}

	// Check if a version with the same filename already exists
	existing, err := d.Get(ctx, filename)
	if err == nil && existing != nil {
		return nil, fault.New("asset version already exists").
			With(fctx.With(ctx), ftag.With(ftag.AlreadyExists))
	}

	asset, err := d.db.Asset.
		Create().
		SetID(filename.GetID()).
		SetParentAssetID(parent).
		SetFilename(filename.name).
		SetSize(size).
		SetAccountID(xid.ID(accountID)).
		Save(ctx)
	if err != nil {
		return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.Internal))
	}

	return FromModel(asset), nil
}

This implementation adds validation for the parent asset ID, checks if the parent asset exists, and handles potential conflicts with existing versions.

To ensure the parent asset check is working correctly, we can verify the usage of the GetByID method:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of GetByID method in the codebase

# Test: Search for the GetByID method usage
rg --type go -A 5 'GetByID\('

Length of output: 20451


Script:

#!/bin/bash
# Description: Check if AddVersion method in app/resources/asset/db.go calls GetByID

rg --type go 'func \(d \*database\) AddVersion.*\{([^}]*)GetByID\(' app/resources/asset/db.go

Length of output: 95

@@ -99,6 +102,7 @@ func serialiseAsset(a asset.Asset) openapi.Asset {
Id: a.ID.String(),
Filename: a.Name.String(),
Path: path,
Parent: opt.Map(a.Parent, serialiseAsset).Ptr(),
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Potential Issue: Recursive Calls in serialiseAsset Function

The serialiseAsset function currently calls itself recursively:

return serialiseAsset(*a)

While this allows for the serialization of parent assets, it poses a risk of stack overflow or excessive response sizes if assets are deeply nested.

Recommendation: Implement a depth limit for recursion or consider refactoring to an iterative approach to ensure stability and performance.

🔗 Analysis chain

LGTM! Consider handling for deeply nested assets.

The addition of the Parent field in the serialiseAsset function successfully includes parent asset information in the API response. The use of opt.Map correctly handles cases where there might not be a parent.

However, the recursive call to serialiseAsset for the parent could potentially lead to issues with deeply nested assets. Consider implementing a depth limit to prevent potential stack overflow or excessive response sizes.

To verify the current usage and potential impact, you can run the following script:

This script will help identify where assets are referenced and if there are any existing patterns of recursive asset handling that might need attention.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for nested asset references and their depth

# Search for asset references in the codebase
echo "Searching for asset references:"
rg --type go -n 'Parent.*Asset'

# Check for potential recursive calls in asset-related functions
echo "\nChecking for potential recursive calls:"
rg --type go -n 'func.*Asset.*\{' -A 10 | rg 'Parent'

Length of output: 37613


Script:

#!/bin/bash
# Description: Check for recursive calls to serialiseAsset in the codebase

echo "Searching for recursive calls within serialiseAsset function:"
rg --type go 'func\s+serialiseAsset\s*\(' -A 20 | rg 'serialiseAsset\s*\('

Length of output: 365

Comment on lines +62 to +69
const accepted = acceptedMIMEs.reduce((prev: string[], curr: string) => {
const extensions = mime[curr]?.extensions;
if (!extensions) {
return prev;
}

return [...prev, ...extensions];
}, []);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Optimize reduce operation for better performance

The current implementation uses spread syntax inside the reduce function, which can lead to O(n²) time complexity. Although this operation is only performed when a file is rejected, optimizing it would be beneficial for edge cases with many file types.

Consider refactoring the reduce operation to avoid using spread syntax:

-    const accepted = acceptedMIMEs.reduce((prev: string[], curr: string) => {
-      const extensions = mime[curr]?.extensions;
-      if (!extensions) {
-        return prev;
-      }
-
-      return [...prev, ...extensions];
-    }, []);
+    const accepted = acceptedMIMEs.reduce((prev: string[], curr: string) => {
+      const extensions = mime[curr]?.extensions;
+      if (extensions) {
+        prev.push(...extensions);
+      }
+      return prev;
+    }, []);

This change maintains the same functionality while improving performance by avoiding the creation of new arrays in each iteration.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const accepted = acceptedMIMEs.reduce((prev: string[], curr: string) => {
const extensions = mime[curr]?.extensions;
if (!extensions) {
return prev;
}
return [...prev, ...extensions];
}, []);
const accepted = acceptedMIMEs.reduce((prev: string[], curr: string) => {
const extensions = mime[curr]?.extensions;
if (extensions) {
prev.push(...extensions);
}
return prev;
}, []);
🧰 Tools
🪛 Biome

[error] 68-68: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

Comment on lines +146 to +166
// QueryPrimaryImage chains the current query on the "primary_image" edge.
func (nq *NodeQuery) QueryPrimaryImage() *AssetQuery {
query := (&AssetClient{config: nq.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := nq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := nq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(node.Table, node.FieldID, selector),
sqlgraph.To(asset.Table, asset.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, node.PrimaryImageTable, node.PrimaryImageColumn),
)
fromU = sqlgraph.SetNeighbors(nq.driver.Dialect(), step)
return fromU, nil
}
return query
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix the edge definition in QueryPrimaryImage method.

The implementation of the QueryPrimaryImage method looks correct overall. However, there's still a potential issue with the edge definition that needs to be addressed.

The boolean argument in sqlgraph.Edge() should be true for many-to-one edges to indicate the edge's direction. Currently, it's set to false, which may cause incorrect query behavior.

Please apply the following fix:

 func (nq *NodeQuery) QueryPrimaryImage() *AssetQuery {
     // ... (other code remains the same)
     step := sqlgraph.NewStep(
         sqlgraph.From(node.Table, node.FieldID, selector),
         sqlgraph.To(asset.Table, asset.FieldID),
-        sqlgraph.Edge(sqlgraph.M2O, false, node.PrimaryImageTable, node.PrimaryImageColumn),
+        sqlgraph.Edge(sqlgraph.M2O, true, node.PrimaryImageTable, node.PrimaryImageColumn),
     )
     // ... (other code remains the same)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// QueryPrimaryImage chains the current query on the "primary_image" edge.
func (nq *NodeQuery) QueryPrimaryImage() *AssetQuery {
query := (&AssetClient{config: nq.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := nq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := nq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(node.Table, node.FieldID, selector),
sqlgraph.To(asset.Table, asset.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, node.PrimaryImageTable, node.PrimaryImageColumn),
)
fromU = sqlgraph.SetNeighbors(nq.driver.Dialect(), step)
return fromU, nil
}
return query
}
// QueryPrimaryImage chains the current query on the "primary_image" edge.
func (nq *NodeQuery) QueryPrimaryImage() *AssetQuery {
query := (&AssetClient{config: nq.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := nq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := nq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(node.Table, node.FieldID, selector),
sqlgraph.To(asset.Table, asset.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, node.PrimaryImageTable, node.PrimaryImageColumn),
)
fromU = sqlgraph.SetNeighbors(nq.driver.Dialect(), step)
return fromU, nil
}
return query
}

Comment on lines +1626 to 1628
PrimaryImage *Asset `json:"primary_image,omitempty"`
Recomentations DatagraphItemList `json:"recomentations"`

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

LGTM: PrimaryImage addition, but there's a typo nearby

The addition of the PrimaryImage *Asset field is consistent with the PR's objective and earlier changes.

However, there's a typo in the nearby Recomentations field. It should be corrected:

- Recomentations DatagraphItemList `json:"recomentations"`
+ Recommendations DatagraphItemList `json:"recommendations"`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PrimaryImage *Asset `json:"primary_image,omitempty"`
Recomentations DatagraphItemList `json:"recomentations"`
PrimaryImage *Asset `json:"primary_image,omitempty"`
Recommendations DatagraphItemList `json:"recommendations"`

Comment on lines +227 to +231
await nodeUpdate(slug, {
primary_image_asset_id: null,
// NOTE: We don't have access to the original node's meta, so we can't
// remove the old cover config, but it doesn't really matter.
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for nodeUpdate in removeNodeCoverImage

The removeNodeCoverImage function does not handle errors that might occur during the nodeUpdate API call. If the API request fails, the application may end up in an inconsistent state. Consider adding error handling to manage possible exceptions.

Apply the following changes to implement error handling:

+  try {
     await nodeUpdate(slug, {
       primary_image_asset_id: null,
       // NOTE: We don't have access to the original node's meta, so we can't
       // remove the old cover config, but it doesn't really matter.
     });
+  } catch (error) {
+    // Handle the error appropriately, e.g., show a notification or revert changes
+    console.error("Failed to remove node cover image:", error);
+    // Optional: Revert the optimistic update if necessary
+    await mutate(nodeKeyFn);
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await nodeUpdate(slug, {
primary_image_asset_id: null,
// NOTE: We don't have access to the original node's meta, so we can't
// remove the old cover config, but it doesn't really matter.
});
try {
await nodeUpdate(slug, {
primary_image_asset_id: null,
// NOTE: We don't have access to the original node's meta, so we can't
// remove the old cover config, but it doesn't really matter.
});
} catch (error) {
// Handle the error appropriately, e.g., show a notification or revert changes
console.error("Failed to remove node cover image:", error);
// Optional: Revert the optimistic update if necessary
await mutate(nodeKeyFn);
}

Comment on lines +193 to 201
...newNode,
primary_image_asset_id: cover?.asset.id,
// NOTE: We don't have access to the original node's meta, so we have to
// fully replace it. Right now no other features use metadata, but this
// will need to be fixed eventually. Probably by either calling the API
// within this hook to fetch the latest version of the node and spreading.
meta: newMeta,
});

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for nodeUpdate in updateNode

The updateNode function lacks error handling for the nodeUpdate API call. Without proper error management, a failed API request could lead to inconsistent application state. Consider wrapping the API call in a try-catch block to handle potential exceptions.

Here's how you can implement error handling:

+  try {
     await nodeUpdate(slug, {
       ...newNode,
       primary_image_asset_id: cover?.asset.id,
       meta: newMeta,
     });
+  } catch (error) {
+    // Handle the error appropriately, e.g., show a notification or revert changes
+    console.error("Failed to update node:", error);
+    // Optional: Revert the optimistic update if necessary
+    await mutate(nodeKeyFn);
+  }

Committable suggestion was skipped due to low confidence.

Comment on lines +184 to +199
const newMeta =
cover && !cover.isReplacement
? ({
// TODO: Spread original node metadata here
coverImage: cover.config,
} satisfies NodeMetadata)
: undefined;

await nodeUpdate(slug, {
name: newNode.name,
slug: newNode.slug,
asset_ids: newNode.asset_ids,
url: newNode.url,
content: newNode.content,
meta: newNode.meta,
...newNode,
primary_image_asset_id: cover?.asset.id,
// NOTE: We don't have access to the original node's meta, so we have to
// fully replace it. Right now no other features use metadata, but this
// will need to be fixed eventually. Probably by either calling the API
// within this hook to fetch the latest version of the node and spreading.
meta: newMeta,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid overwriting existing metadata when updating the node

In the updateNode function, the existing meta field is completely overwritten by newMeta, which may lead to loss of any additional metadata stored in the node. To prevent potential data loss, consider merging newMeta with the existing meta.

You can apply this diff to merge the existing metadata:

 await nodeUpdate(slug, {
   ...newNode,
   primary_image_asset_id: cover?.asset.id,
-  meta: newMeta,
+  meta: {
+    ...data.meta,
+    ...newMeta,
+  },
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const newMeta =
cover && !cover.isReplacement
? ({
// TODO: Spread original node metadata here
coverImage: cover.config,
} satisfies NodeMetadata)
: undefined;
await nodeUpdate(slug, {
name: newNode.name,
slug: newNode.slug,
asset_ids: newNode.asset_ids,
url: newNode.url,
content: newNode.content,
meta: newNode.meta,
...newNode,
primary_image_asset_id: cover?.asset.id,
// NOTE: We don't have access to the original node's meta, so we have to
// fully replace it. Right now no other features use metadata, but this
// will need to be fixed eventually. Probably by either calling the API
// within this hook to fetch the latest version of the node and spreading.
meta: newMeta,
const newMeta =
cover && !cover.isReplacement
? ({
// TODO: Spread original node metadata here
coverImage: cover.config,
} satisfies NodeMetadata)
: undefined;
await nodeUpdate(slug, {
...newNode,
primary_image_asset_id: cover?.asset.id,
// NOTE: We don't have access to the original node's meta, so we have to
// fully replace it. Right now no other features use metadata, but this
// will need to be fixed eventually. Probably by either calling the API
// within this hook to fetch the latest version of the node and spreading.
meta: {
...data.meta,
...newMeta,
},

Comment on lines +228 to +231
primary_image_asset_id: null,
// NOTE: We don't have access to the original node's meta, so we can't
// remove the old cover config, but it doesn't really matter.
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure consistent metadata when removing the cover image

When removing the cover image in removeNodeCoverImage, the meta field's coverImage configuration is not updated, potentially leading to inconsistencies if meta.coverImage still exists after the primary_image is removed. Consider updating the meta field to remove the coverImage configuration.

Modify the nodeUpdate call to clear the coverImage from metadata:

 await nodeUpdate(slug, {
   primary_image_asset_id: null,
+  meta: {
+    ...data.meta,
+    coverImage: null,
+  },
   // NOTE: We don't have access to the original node's meta, so we can't
   // remove the old cover config, but it doesn't really matter.
 });

Ensure that you have access to the current meta data when making this update.

Committable suggestion was skipped due to low confidence.

Comment on lines +43 to +44
// PrimaryAssetID holds the value of the "primary_asset_id" field.
PrimaryAssetID *xid.ID `json:"primary_asset_id,omitempty"`
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Inconsistent ID field type: Consider consistency for PrimaryAssetID

The PrimaryAssetID field is declared as a pointer to xid.ID (*xid.ID), whereas other ID fields like ParentNodeID, AccountID, and LinkID are declared as xid.ID. For consistency and to avoid potential nil pointer dereferences, consider declaring PrimaryAssetID as xid.ID if it cannot be null. If it can be null, ensure that null values are properly handled throughout the codebase.

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.

1 participant