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

Communication: Migrate Metis to standalone components #9904

Conversation

cremertim
Copy link
Contributor

@cremertim cremertim commented Nov 29, 2024

Checklist

General

Client

Motivation and Context

Description

This PR adresses the MetisModule of Artemis. It migrates every component to a standalone component.

Steps for Testing

Prerequisites:

  • 1 Course with Communication enabled
  • 1 Student
  • 1 Instructor
  1. Log in to Artemis
  2. Navigate to into a Course
  3. Test the entire Communication Section

Testserver States

Note

These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Click on the badges to get to the test servers.







Review Progress

Performance Review

  • I (as a reviewer) confirm that the client changes (in particular related to REST calls and UI responsiveness) are implemented with a very good performance even for very large courses with more than 2000 students.
  • I (as a reviewer) confirm that the server changes (in particular related to database calls) are implemented with a very good performance even for very large courses with more than 2000 students.

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Exam Mode Test

  • Test 1
  • Test 2

Performance Tests

  • Test 1
  • Test 2

Summary by CodeRabbit

Release Notes

  • New Features

    • Multiple components (e.g., LinkPreviewContainer, AnswerPost, EmojiPicker, etc.) are now standalone, enhancing modularity and reusability.
    • New imports added across various components, improving functionality and integration with shared modules.
  • Bug Fixes

    • Corrected import statements for consistency and clarity across components.
  • Documentation

    • Updated component metadata to reflect standalone status and new imports.

These changes improve the overall structure and usability of the application, allowing for better component management and integration.

@cremertim cremertim requested a review from a team as a code owner November 29, 2024 12:03
@github-actions github-actions bot added the client Pull requests that update TypeScript code. (Added Automatically!) label Nov 29, 2024
Copy link

coderabbitai bot commented Nov 29, 2024

Walkthrough

The pull request introduces multiple modifications across various components in an Angular application, primarily focusing on marking components as standalone. This allows them to function independently without being declared in a module. Additionally, several components have new imports added to their decorators, enhancing their modularity and functionality. The changes also include updates to input properties and the overall structure of components, maintaining existing logic while improving reusability.

Changes

File Path Change Summary
src/main/webapp/app/shared/link-preview/components/link-preview-container/link-preview-container.component.ts Marked as standalone; imported LinkPreviewComponent.
src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts Marked as standalone; imported ArtemisConfirmIconModule and ArtemisSharedModule.
src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts Marked as standalone; imported multiple components and modules.
src/main/webapp/app/shared/metis/emoji/emoji-picker.component.ts Marked as standalone; imported EmojiModule, ArtemisSharedModule, and PickerModule.
src/main/webapp/app/shared/metis/emoji/emoji.component.ts Marked as standalone; imported EmojiModule.
src/main/webapp/app/shared/metis/message/message-inline-input/message-inline-input.component.ts Marked as standalone; imported multiple components and modules.
src/main/webapp/app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component.ts Marked as standalone; imported multiple components; updated activeConversation property to use input.
src/main/webapp/app/shared/metis/metis.module.ts Updated imports, declarations, and exports for multiple components and pipes.
src/main/webapp/app/shared/metis/post/post.component.ts Marked as standalone; updated imports and corrected input import casing.
src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts Marked as standalone; imported FontAwesomeModule.
src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.ts Marked as standalone.
src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts Marked as standalone; imported multiple modules.
src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts Marked as standalone; updated input/output properties.
src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts Marked as standalone; imported multiple components.
src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts Marked as standalone; imported several components and modules.
src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts Marked as standalone; imported Angular Material modules.
src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts Marked as standalone; imported AnswerPostComponent and ArtemisSharedModule.
src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.ts Marked as standalone; imported multiple components.
src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.ts Marked as standalone; imported multiple components.
src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts Marked as standalone; imported multiple modules; updated activeConversation property.
src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts Marked as standalone; imported multiple modules.
src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts Marked as standalone; imported multiple modules.
src/main/webapp/app/shared/metis/posting-thread/posting-thread.component.ts Marked as standalone; imported PostComponent.
src/main/webapp/app/shared/pipes/html-for-posting-markdown.pipe.ts Marked as standalone.
src/main/webapp/app/shared/pipes/reacting-users-on-posting.pipe.ts Marked as standalone.

Possibly related PRs

  • Communication: Fix an issue with link previews #9532: This PR modifies the LinkPreviewContainerComponent, which is directly related to the changes made in the main PR that also involves the LinkPreviewContainerComponent. Both PRs focus on enhancing the functionality and reactivity of link previews within the communication feature.
  • Communication: Add emoji support to messages #9595: This PR introduces emoji support in messages, which may relate to the LinkPreviewContainerComponent as it handles link previews that could include emojis. The changes in both PRs aim to improve user interaction and content representation in the messaging system.
  • Communication: Remember last scroll position when switching conversations #9614: This PR enhances user experience by saving the last scroll position in conversation messages, which could be relevant to the LinkPreviewContainerComponent as it deals with displaying content in a messaging context. Both PRs focus on improving user interaction within the communication features.
  • Communication: Fix an issue that stops channels from loading messages #9745: This PR addresses an issue with loading messages in communication channels, which is relevant to the overall functionality of the messaging system that the LinkPreviewContainerComponent is part of. Both PRs aim to enhance the reliability and usability of the communication features.

Suggested labels

ready for review, small, component:Communication

Suggested reviewers

  • JohannesWt
  • florian-glombik
  • sachmii
  • EneaGore
  • edkaya
  • Malekos74
  • HawKhiem
  • SimonEntholzer
  • rabeatwork

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 or @coderabbitai title anywhere in the PR title to generate the title automatically.

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: 6

🧹 Outside diff range and nitpick comments (48)
src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.ts (2)

Line range hint 4-6: Consider enhancing the DialogData interface

The interface could be more descriptive about the expected slide reference format and any constraints.

Consider adding JSDoc comments and type constraints:

+/**
+ * Data interface for the enlarge slide image dialog
+ * @property slideToReference - Reference to the slide image (e.g., URL or identifier)
+ */
 export interface DialogData {
-    slideToReference: string;
+    slideToReference: string; // Consider using a more specific type if possible
 }

Line range hint 1-13: Consider implementing OnDestroy for proper cleanup

To prevent potential memory leaks, it's recommended to implement cleanup logic for the dialog.

Consider implementing OnDestroy:

-import { Component, Inject } from '@angular/core';
+import { Component, Inject, OnDestroy } from '@angular/core';
 import { MAT_DIALOG_DATA } from '@angular/material/dialog';

 export interface DialogData {
     slideToReference: string;
 }

 @Component({
     templateUrl: './enlarge-slide-image.component.html',
     standalone: true,
 })
-export class EnlargeSlideImageComponent {
+export class EnlargeSlideImageComponent implements OnDestroy {
     constructor(@Inject(MAT_DIALOG_DATA) public data: DialogData) {}
+    
+    ngOnDestroy(): void {
+        // Cleanup any subscriptions or resources if needed
+    }
 }
src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts (2)

7-7: Document the reason for ESLint disable.

Consider adding a brief comment explaining why the component-selector rule is disabled here.

-    /* eslint-disable-next-line  @angular-eslint/component-selector */
+    /* eslint-disable-next-line  @angular-eslint/component-selector -- Using attribute selector for button enhancement */

13-13: Add JSDoc documentation as per PR requirements.

The PR objectives specify that TypeScript code should be documented using JSDoc style. Please add class-level documentation explaining the component's purpose and usage.

+/**
+ * A reusable button component that supports icons, labels, and loading states.
+ * Can be used as a primary or outlined button, with optional size variants.
+ *
+ * @example
+ * <button jhi-posting-button
+ *     [buttonIcon]="icon"
+ *     [buttonLabel]="'Submit'"
+ *     [buttonLoading]="isLoading">
+ * </button>
+ */
 export class PostingButtonComponent {
src/main/webapp/app/shared/metis/posting-thread/posting-thread.component.ts (1)

Line range hint 25-25: Consider memory leak prevention for ElementRef

The injected ElementRef could potentially cause memory leaks. Consider implementing OnDestroy and cleaning up any subscriptions or event listeners that might be using this reference.

-export class PostingThreadComponent {
+export class PostingThreadComponent implements OnDestroy {
     elementRef = inject(ElementRef);
+    
+    ngOnDestroy(): void {
+        // Clean up any subscriptions or event listeners
+    }
 }
src/main/webapp/app/shared/pipes/html-for-posting-markdown.pipe.ts (1)

Line range hint 12-20: Consider enhancing JSDoc documentation

The documentation is well-structured, but could be enhanced with:

  1. Add @since tag to document the standalone migration
  2. Include usage examples, especially important now that it's standalone
     /**
      * Converts markdown used in posting content into html, sanitizes it and then declares it as safe to bypass further security.
+     * @since 1.x.x Converted to standalone pipe
      * @param {string} markdown the original markdown text
      * @param {boolean} contentBeforeReference to indicate if this is markdown content before a possible reference or after
      * @param {string[]} allowedHtmlTags to allow during sanitization
      * @param {string[]} allowedHtmlAttributes to allow during sanitization
      * @returns {string} the resulting html as a SafeHtml object that can be inserted into the angular template
+     * @example
+     * // In your standalone component:
+     * import { HtmlForPostingMarkdownPipe } from './html-for-posting-markdown.pipe';
+     * 
+     * @Component({
+     *   standalone: true,
+     *   imports: [HtmlForPostingMarkdownPipe],
+     *   template: '{{ markdownContent | htmlForPostingMarkdown }}'
+     * })
      */
src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.ts (1)

Line range hint 18-23: Consider memory leak prevention for EventEmitter.

While the component structure is good, ensure that the openPostingCreateEditModal EventEmitter is properly cleaned up when the component is destroyed. Consider implementing OnDestroy to handle cleanup.

-export class AnswerPostHeaderComponent extends PostingHeaderDirective<AnswerPost> implements OnInit, OnChanges {
+export class AnswerPostHeaderComponent extends PostingHeaderDirective<AnswerPost> implements OnInit, OnChanges, OnDestroy {
     @Input()
     isReadOnlyMode = false;
 
     @Input() lastReadDate?: dayjs.Dayjs;
     @Output() openPostingCreateEditModal = new EventEmitter<void>();
+
+    ngOnDestroy(): void {
+        this.openPostingCreateEditModal.complete();
+        super.ngOnDestroy();
+    }
src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts (2)

Line range hint 42-71: Enhance error handling and prevent memory leaks

The removeLinkPreview method has potential issues:

  1. Subscriptions are not being cleaned up, which could lead to memory leaks
  2. Missing error handling for HTTP calls

Consider applying this improvement:

 removeLinkPreview(linkPreview: LinkPreview) {
     const urlToSearchFor = linkPreview.url;
 
     if (this.posting) {
         // Find all URL matches in the text (in the content of the post)
         let match;
         let modifiedContent = this.posting.content!;
         while ((match = urlRegex.exec(modifiedContent)) !== null) {
             const url = match[0];
             const start = match.index;
             const end = start + url.length;
 
             if (url === urlToSearchFor || url.includes(urlToSearchFor)) {
                 modifiedContent = modifiedContent.substring(0, start) + `<${url}>` + modifiedContent.substring(end);
             }
         }
 
         this.posting.content = modifiedContent;
 
         if (this.isReply) {
-            this.metisService.updateAnswerPost(this.posting).subscribe({
-                next: () => {},
-            });
+            this.metisService.updateAnswerPost(this.posting).subscribe({
+                next: () => {
+                    console.log('Answer post updated successfully');
+                },
+                error: (error) => {
+                    console.error('Failed to update answer post:', error);
+                    // Consider adding user notification here
+                }
+            });
         } else {
-            this.metisService.updatePost(this.posting).subscribe({
-                next: () => {},
-            });
+            this.metisService.updatePost(this.posting).subscribe({
+                next: () => {
+                    console.log('Post updated successfully');
+                },
+                error: (error) => {
+                    console.error('Failed to update post:', error);
+                    // Consider adding user notification here
+                }
+            });
         }
     }
 }

Additionally, consider implementing the following:

  1. Store subscriptions in a class property
  2. Implement OnDestroy and unsubscribe in ngOnDestroy
  3. Use the takeUntil operator with a destroy subject

Line range hint 17-25: Add JSDoc documentation for better code maintainability

Consider adding JSDoc documentation for the component class and its input properties to improve code maintainability.

Add documentation like this:

+/**
+ * Component for displaying and managing link previews in posts and replies.
+ */
 export class LinkPreviewComponent implements OnInit {
+    /** The link preview data to be displayed */
     @Input() linkPreview: LinkPreview;
+    /** Flag indicating if loading progress should be shown */
     @Input() showLoadingsProgress: boolean;
+    /** Flag indicating if the preview has finished loading */
     @Input() loaded: boolean;
+    /** Flag indicating if an error occurred during preview loading */
     @Input() hasError: boolean;
+    /** The posting associated with this preview */
     @Input() posting?: Posting;
+    /** Flag indicating if this preview is in a reply context */
     @Input() isReply?: boolean;
+    /** Flag indicating if multiple previews are allowed */
     @Input() multiple?: boolean;
src/main/webapp/app/shared/pipes/reacting-users-on-posting.pipe.ts (3)

Line range hint 23-30: Fix potential memory leak in transform method

The subscription to translateService.onLangChange is never cleaned up, which could lead to memory leaks. The pipe instance might persist longer than expected, causing multiple subscriptions to accumulate.

Consider using this implementation instead:

 transform(reactingUsers: string[]): Observable<string> {
-    return new Observable((observer: any) => {
-        observer.next(this.updateReactingUsersString(reactingUsers));
-        this.translateService.onLangChange.subscribe(() => {
-            observer.next(this.updateReactingUsersString(reactingUsers));
-        });
-    });
+    return new Observable((observer) => {
+        observer.next(this.updateReactingUsersString(reactingUsers));
+        const subscription = this.translateService.onLangChange.subscribe(() => {
+            observer.next(this.updateReactingUsersString(reactingUsers));
+        });
+        
+        // Clean up subscription when the observable is unsubscribed
+        return () => subscription.unsubscribe();
+    });

Line range hint 23-23: Improve type safety by removing 'any' type

Replace the any type with the proper Observer<string> type for better type safety.

-    return new Observable((observer: any) => {
+    return new Observable((observer: Observer<string>) => {

Don't forget to add the import:

import { Observable, Observer } from 'rxjs';

Line range hint 35-65: Consider using type-safe translation keys

While the internationalization implementation is good, consider using type-safe translation keys to prevent typos and make maintenance easier.

Create an interface for your translation keys:

interface MetisTranslations {
    'artemisApp.metis.you': string;
    'artemisApp.metis.reactedTooltip': string;
    'artemisApp.metis.reactedTooltipTrimmed': string;
    'artemisApp.metis.and': string;
}

Then use it with the translate service:

private translateService: TranslateService<MetisTranslations>
src/main/webapp/app/shared/link-preview/components/link-preview-container/link-preview-container.component.ts (3)

Line range hint 47-49: Consider implementing configurable link preview limit

The TODO comment indicates the need for a configurable limit. This should be implemented to improve flexibility at the course level.

Would you like me to help create a configuration service for managing these limits?


Line range hint 50-75: Potential race conditions and memory leaks in link preview handling

Several concerns in the link preview handling:

  1. Multiple concurrent subscriptions without proper cleanup
  2. Possible race conditions when updating linkPreviews array
  3. No error handling timeout for failed requests

Consider implementing these improvements:

 private findPreviews() {
     const links: Link[] = this.linkifyService.find(this.data() ?? '');
-    links
+    // Store subscription references for cleanup
+    this.subscriptions = links
         .filter((link) => !link.isLinkPreviewRemoved)
         .slice(0, 5)
-        .forEach((link) => {
+        .map((link) => 
             this.linkPreviewService.fetchLink(link.href).subscribe({
                 next: (linkPreview) => {
                     linkPreview.shouldPreviewBeShown = !!(linkPreview.url && linkPreview.title && linkPreview.description && linkPreview.image);
-                    const existingLinkPreviewIndex = this.linkPreviews().findIndex((preview) => preview.url === linkPreview.url);
-                    if (existingLinkPreviewIndex !== -1) {
-                        this.linkPreviews.update((previews) => {
-                            const existingLinkPreview = previews[existingLinkPreviewIndex];
-                            Object.assign(existingLinkPreview, linkPreview);
-                            return previews;
-                        });
-                    } else {
-                        this.linkPreviews.set([...this.linkPreviews(), linkPreview]);
-                    }
+                    // Use atomic update to prevent race conditions
+                    this.linkPreviews.update(previews => {
+                        const existingIndex = previews.findIndex(preview => preview.url === linkPreview.url);
+                        if (existingIndex !== -1) {
+                            return previews.map((preview, index) => 
+                                index === existingIndex ? { ...preview, ...linkPreview } : preview
+                            );
+                        }
+                        return [...previews, linkPreview];
+                    });
                     this.hasError.set(false);
                     this.loaded.set(true);
                     this.showLoadingsProgress.set(false);
                     this.multiple.set(this.linkPreviews().length > 1);
                 },
+                error: () => {
+                    this.hasError.set(true);
+                    this.showLoadingsProgress.set(false);
+                }
             })
-        });
+        );
 }

Line range hint 77-79: Consider using identity-based tracking

The current tracking function might not be optimal if the URL is not unique or changes. Consider using a more stable identifier.

-trackLinks(index: number, preview: LinkPreview) {
-    return preview?.url;
+trackLinks(_: number, preview: LinkPreview) {
+    return preview?.id ?? preview?.url;
 }
src/main/webapp/app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component.ts (3)

Line range hint 22-27: Prevent potential memory leaks from EventEmitter.

While the property declarations look good, consider implementing proper cleanup for the EventEmitter to prevent memory leaks.

Add this to prevent memory leaks:

+ import { Subject } from 'rxjs';
+ import { takeUntil } from 'rxjs/operators';

export class MessageReplyInlineInputComponent extends PostingCreateEditDirective<AnswerPost> implements OnInit, OnChanges, OnDestroy {
+    private readonly destroy$ = new Subject<void>();
    
    @Output()
    valueChange = new EventEmitter<void>();

+    ngOnDestroy(): void {
+        this.destroy$.next();
+        this.destroy$.complete();
+    }

Line range hint 41-52: Improve type safety and simplify ngOnChanges logic.

The current implementation of ngOnChanges could be more robust and simpler.

Consider this improved implementation:

- ngOnChanges(changes: SimpleChanges | void) {
-     if (this.formGroup && changes) {
-         for (const propName in changes) {
-             if (changes.hasOwnProperty(propName) && propName === 'posting') {
-                 if (changes['posting'].previousValue?.post?.id === changes['posting'].currentValue?.post?.id) {
-                     this.posting.content = this.formGroup.get('content')?.value;
-                 }
-             }
-         }
-     }
-     super.ngOnChanges();
- }
+ ngOnChanges(changes: SimpleChanges): void {
+     if (this.formGroup && changes?.['posting']) {
+         const postingChange = changes['posting'];
+         if (postingChange.previousValue?.post?.id === postingChange.currentValue?.post?.id) {
+             this.posting.content = this.formGroup.get('content')?.value ?? '';
+         }
+     }
+     super.ngOnChanges();
+ }

Line range hint 71-99: Improve subscription management and error handling.

The current implementation of createPosting and updatePosting methods could benefit from better subscription management and error handling.

Consider these improvements:

 createPosting(): void {
     this.posting.content = this.formGroup.get('content')?.value;
     this.metisService.createAnswerPost(this.posting).subscribe({
         next: (answerPost: AnswerPost) => {
             this.resetFormGroup('');
             this.isLoading = false;
             this.onCreate.emit(answerPost);
         },
-        error: () => {
+        error: (error: Error) => {
             this.isLoading = false;
+            console.error('Failed to create answer post:', error);
+            // TODO: Add user-friendly error handling
         },
-    });
+    }).pipe(takeUntil(this.destroy$));
 }

 updatePosting(): void {
     this.posting.content = this.formGroup.get('content')?.value;
     this.metisService.updateAnswerPost(this.posting).subscribe({
         next: () => {
             this.isLoading = false;
         },
-        error: () => {
+        error: (error: Error) => {
             this.isLoading = false;
+            console.error('Failed to update answer post:', error);
+            // TODO: Add user-friendly error handling
         },
-    });
+    }).pipe(takeUntil(this.destroy$));
 }
src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts (1)

Line range hint 21-108: Consider enhancing subscription management and error handling.

While the implementation is solid, consider these improvements:

  1. Subscription Management:

    • Consider implementing OnDestroy to clean up subscriptions
    • Use takeUntil pattern for subscription management
  2. Error Handling:

    • Add specific error handling instead of just setting isLoading to false
    • Consider showing user-friendly error messages

Here's a suggested implementation:

+ import { Subject } from 'rxjs';
+ import { takeUntil } from 'rxjs/operators';

 export class AnswerPostCreateEditModalComponent extends PostingCreateEditModalDirective<AnswerPost> {
+    private destroy$ = new Subject<void>();
+
     // ... existing code ...
+
+    ngOnDestroy(): void {
+        this.destroy$.next();
+        this.destroy$.complete();
+    }

     createPosting(): void {
         this.posting.content = this.formGroup.get('content')?.value;
-        this.metisService.createAnswerPost(this.posting).subscribe({
+        this.metisService.createAnswerPost(this.posting).pipe(
+            takeUntil(this.destroy$)
+        ).subscribe({
             next: (answerPost: AnswerPost) => {
                 this.resetFormGroup();
                 this.isLoading = false;
                 this.onCreate.emit(answerPost);
                 this.createEditAnswerPostContainerRef?.clear();
             },
-            error: () => {
+            error: (error) => {
                 this.isLoading = false;
+                console.error('Failed to create answer post:', error);
+                // TODO: Add user-friendly error handling
             },
         });
     }
src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts (2)

18-19: Consider lazy loading Material modules for better performance.

While the standalone component setup is correct, the direct import of multiple Material modules could impact initial load time. Consider lazy loading these modules, especially if they're not immediately needed on component initialization.

Example implementation:

@Component({
    selector: 'jhi-post-tag-selector',
    templateUrl: './post-tag-selector.component.html',
    styleUrls: ['./post-tag-selector.component.scss'],
    standalone: true,
-    imports: [MatChipsModule, MatAutocompleteModule, MatSelectModule, MatFormFieldModule, MatDialogModule, ArtemisSharedModule],
+    imports: [
+        ArtemisSharedModule,
+        // Lazy load Material modules
+        forwardRef(() => MatChipsModule),
+        forwardRef(() => MatAutocompleteModule),
+        forwardRef(() => MatSelectModule),
+        forwardRef(() => MatFormFieldModule),
+        forwardRef(() => MatDialogModule),
+    ],
})

Don't forget to add the import:

import { forwardRef } from '@angular/core';

Line range hint 21-116: Consider using takeUntil pattern for subscription management.

While the current subscription cleanup in ngOnDestroy is functional, consider using the takeUntil pattern for more robust subscription management, especially if more subscriptions are added in the future.

Example implementation:

export class PostTagSelectorComponent implements OnInit, OnChanges, OnDestroy, AfterContentChecked {
    private readonly destroy$ = new Subject<void>();

    ngOnInit(): void {
        this.metisService.tags.pipe(
            takeUntil(this.destroy$)
        ).subscribe((tags: string[]) => {
            this.existingPostTags = this.tagCtrl.valueChanges.pipe(
                takeUntil(this.destroy$),
                startWith(undefined),
                map((category: string | undefined) => 
                    (category ? this._filter(category) : tags.slice())
                ),
            );
        });
        this.tags = this.postTags ? this.postTags : [];
    }

    ngOnDestroy(): void {
        this.destroy$.next();
        this.destroy$.complete();
    }
}

Don't forget to add the imports:

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts (3)

8-13: Consider reorganizing imports per Angular style guide

While the imports are correct, consider reorganizing them according to the Angular style guide:

  1. Angular imports
  2. Third-party imports
  3. Application imports
+import { OverlayModule } from '@angular/cdk/overlay';
+
 import { ReactingUsersOnPostingPipe } from 'app/shared/pipes/reacting-users-on-posting.pipe';
 import { EmojiComponent } from 'app/shared/metis/emoji/emoji.component';
 import { ArtemisSharedModule } from 'app/shared/shared.module';
 import { ArtemisConfirmIconModule } from 'app/shared/confirm-icon/confirm-icon.module';
 import { EmojiPickerComponent } from 'app/shared/metis/emoji/emoji-picker.component';
-import { OverlayModule } from '@angular/cdk/overlay';

Line range hint 109-114: Prevent potential memory leak in subscription

The subscription to updateAnswerPost should be cleaned up to prevent memory leaks.

 toggleResolvesPost(): void {
     if (this.isAtLeastTutorInCourse || this.isAuthorOfOriginalPost) {
         this.posting.resolvesPost = !this.posting.resolvesPost;
-        this.metisService.updateAnswerPost(this.posting).subscribe();
+        this.metisService.updateAnswerPost(this.posting)
+            .subscribe({
+                next: () => this.postingUpdated.emit(),
+                error: (error) => console.error('Failed to update answer post', error)
+            });
     }
 }

Line range hint 82-92: Improve code readability and null safety in setMayDelete

The complex boolean logic could be simplified and null checks added for better maintainability.

 setMayDelete(): void {
+    const post = this.posting?.post;
+    if (!post) {
+        this.mayDelete = false;
+        this.mayDeleteOutput.emit(false);
+        return;
+    }
+
-    this.isAuthorOfOriginalPost = this.metisService.metisUserIsAuthorOfPosting(this.posting.post!);
-    this.isAnswerOfAnnouncement = getAsChannelDTO(this.posting.post?.conversation)?.isAnnouncementChannel ?? false;
-    const isCourseWideChannel = getAsChannelDTO(this.posting.post?.conversation)?.isCourseWide ?? false;
+    this.isAuthorOfOriginalPost = this.metisService.metisUserIsAuthorOfPosting(post);
+    const channel = getAsChannelDTO(post.conversation);
+    this.isAnswerOfAnnouncement = channel?.isAnnouncementChannel ?? false;
+    const isCourseWideChannel = channel?.isCourseWide ?? false;
     const isAtLeastInstructorInCourse = this.metisService.metisUserIsAtLeastInstructorInCourse();
-    const mayEditOrDeleteOtherUsersAnswer =
-        (isCourseWideChannel && isAtLeastInstructorInCourse) || (getAsChannelDTO(this.metisService.getCurrentConversation())?.hasChannelModerationRights ?? false);
+    const hasModeratorRights = getAsChannelDTO(this.metisService.getCurrentConversation())?.hasChannelModerationRights ?? false;
+    const mayEditOrDeleteOtherUsersAnswer = (isCourseWideChannel && isAtLeastInstructorInCourse) || hasModeratorRights;
     this.mayDelete = !this.isReadOnlyMode && (this.isAuthorOfPosting || mayEditOrDeleteOtherUsersAnswer);
     this.mayDeleteOutput.emit(this.mayDelete);
 }
src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (1)

31-32: Consider breaking down the component for better maintainability.

The component has multiple responsibilities (handling files, dialogs, references, icons). Consider:

  1. Extracting file handling logic into a separate component
  2. Creating a dedicated component for reference handling
  3. Moving icon mapping to a separate service

This would improve:

  • Code maintainability
  • Component reusability
  • Testing isolation

Example refactor for icon handling:

// reference-icon.service.ts
@Injectable({
    providedIn: 'root'
})
export class ReferenceIconService {
    getIcon(reference: ReferenceType): IconProp {
        // Move icon mapping logic here
    }
}
// posting-content-part.component.ts
export class PostingContentPartComponent {
    constructor(
        private referenceIconService: ReferenceIconService,
        // other dependencies
    ) {}

    referenceIcon(reference: ReferenceType): IconProp {
        return this.referenceIconService.getIcon(reference);
    }
}
src/main/webapp/app/shared/metis/metis.module.ts (2)

55-63: Consider organizing standalone components by feature

The direct import of standalone components like PostReactionsBarComponent, AnswerPostReactionsBarComponent, etc., while maintaining them in exports could lead to maintenance challenges. Consider organizing these components into feature modules for better maintainability.

Consider creating feature modules like:

  • PostingModule (for post-related components)
  • ReactionsModule (for reaction-related components)
  • EditorModule (for editor-related components)

This would improve code organization and make the dependencies clearer.


Line range hint 1-119: Consider breaking down MetisModule into smaller feature modules

The MetisModule has grown quite large and handles multiple concerns (posts, reactions, editors, etc.). While the migration to standalone components is a good step, consider further improvements:

  1. Split into feature modules:

    • MetisPostingModule
    • MetisReactionsModule
    • MetisEditorModule
    • MetisMessageModule
  2. Create a MetisSharedModule for common components

This would:

  • Improve maintainability
  • Enable lazy loading
  • Make dependencies clearer
  • Facilitate testing

Would you like assistance in planning this modular architecture?

src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (2)

35-36: Consider optimizing shared module imports.

Instead of importing the entire ArtemisSharedModule, consider importing only the specific components and directives needed from the shared module. This can help reduce the bundle size.


Potential memory leak in change detection and ViewChild references

The component has several potential memory leak sources that aren't properly cleaned up in ngOnDestroy:

  • changeDetector.detectChanges() is called in ngAfterContentChecked but the reference isn't cleaned up
  • Multiple @Output() EventEmitters (openThread, userReferenceClicked, channelReferenceClicked) aren't unsubscribed
  • Multiple @ViewChild references that should be nullified on destroy
  • The component extends PostingFooterDirective which might have its own resources to clean up

Recommended cleanup in ngOnDestroy:

ngOnDestroy(): void {
    this.answerPostCreateEditModal?.createEditAnswerPostContainerRef?.clear();
    this.openThread.complete();
    this.userReferenceClicked.complete();
    this.channelReferenceClicked.complete();
    this.containerRef?.clear();
    this.answerPostCreateEditModal = undefined;
    this.createAnswerPostModalComponent = undefined;
}
🔗 Analysis chain

Line range hint 89-91: Verify: Ensure complete cleanup in ngOnDestroy.

The component cleans up the modal reference, but please verify if there are any other subscriptions or event listeners that need cleanup:

  • EventEmitter outputs (openThread, userReferenceClicked, channelReferenceClicked)
  • Change detector references
  • Any potential subscriptions in the MetisService
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Look for potential memory leak sources
# Search for Subscription, Subject, or Observable usage

echo "Checking for potential memory leak sources..."

# Search for Observable/Subject/Subscription declarations
rg "(?:Observable|Subject|Subscription)[<]" src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts

# Search for .subscribe() calls
rg "\.subscribe\(" src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts

Length of output: 337


Script:

#!/bin/bash
# Let's check the component implementation more thoroughly

# First, let's see the full component implementation
echo "=== Component Implementation ==="
cat src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts

# Check for event emitter declarations
echo -e "\n=== Event Emitter Usage ==="
rg "@Output\(\)" -A 1 src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts

# Check for ViewChild/ViewChildren usage
echo -e "\n=== ViewChild Usage ==="
rg "@ViewChild|@ViewChildren" -A 1 src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts

# Check for service injections that might create subscriptions
echo -e "\n=== Constructor Injections ==="
ast-grep --pattern 'constructor($$$) { $$ }'

Length of output: 8014

src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (2)

Line range hint 76-76: Consider enhancing type safety for the input property.

While the input migration is correct, consider adding runtime validation to ensure the ConversationDTO is properly structured:

-readonly activeConversation = input<ConversationDTO>();
+readonly activeConversation = input<ConversationDTO>({
+  required: true,
+  transform: (value: ConversationDTO) => {
+    if (!value?.id) {
+      console.warn('Invalid ConversationDTO provided');
+    }
+    return value;
+  }
+});

Also applies to: 83-83


Line range hint 1-186: Consider implementing proper cleanup for subscriptions and event listeners.

While the component migration looks good, consider implementing OnDestroy to clean up any potential subscriptions or event listeners, particularly from the markdown editor and overlay components.

-export class PostingMarkdownEditorComponent implements OnInit, ControlValueAccessor, AfterContentChecked, AfterViewInit {
+export class PostingMarkdownEditorComponent implements OnInit, ControlValueAccessor, AfterContentChecked, AfterViewInit, OnDestroy {
+    private readonly destroy$ = new Subject<void>();
+
+    ngOnDestroy(): void {
+        this.destroy$.next();
+        this.destroy$.complete();
+    }
src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (1)

Line range hint 44-200: Consider enhancing error handling and state management.

While the existing implementation is solid, consider these architectural improvements:

  1. Extract form validation logic into a separate service
  2. Implement proper error messages for failed HTTP operations
  3. Consider using a state management solution for complex form state
src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts (4)

28-29: Consider optimizing imports for better performance.

While the standalone component setup is correct, consider these improvements:

  1. Group related imports together (e.g., all emoji-related components)
  2. Consider lazy loading for PostCreateEditModalComponent since it's used in a modal
 standalone: true,
-imports: [ReactingUsersOnPostingPipe, EmojiComponent, ArtemisSharedModule, ArtemisConfirmIconModule, EmojiPickerComponent, OverlayModule, PostCreateEditModalComponent],
+imports: [
+    // Core modules
+    ArtemisSharedModule,
+    OverlayModule,
+    // Feature modules
+    ArtemisConfirmIconModule,
+    // Components
+    EmojiComponent,
+    EmojiPickerComponent,
+    // Pipes
+    ReactingUsersOnPostingPipe,
+    // Lazy loaded components
+    PostCreateEditModalComponent
+],

Line range hint 89-91: Add subscription cleanup to prevent memory leaks.

The component should properly clean up all subscriptions in ngOnDestroy.

+private destroy$ = new Subject<void>();

 ngOnDestroy() {
     this.postCreateEditModal?.modalRef?.close();
+    this.destroy$.next();
+    this.destroy$.complete();
 }

Update service subscriptions to use takeUntil:

-this.metisService.updatePostDisplayPriority(this.posting.id!, this.displayPriority).subscribe();
+this.metisService.updatePostDisplayPriority(this.posting.id!, this.displayPriority)
+    .pipe(takeUntil(this.destroy$))
+    .subscribe();

Line range hint 136-159: Improve null safety in getShowNewMessageIcon method.

The method has potential null reference issues when accessing nested properties.

 getShowNewMessageIcon(): boolean {
     let showIcon = false;
     // iterate over all answer posts
-    this.sortedAnswerPosts.forEach((answerPost) => {
+    this.sortedAnswerPosts?.forEach((answerPost) => {
         // check if the answer post is newer than the last read date
         const isAuthor = this.metisService.metisUserIsAuthorOfPosting(answerPost);
-        if (!isAuthor && !!(!this.lastReadDate || (this.lastReadDate && answerPost.creationDate && answerPost.creationDate.isAfter(this.lastReadDate!)))) {
+        if (!isAuthor && answerPost?.creationDate && (!this.lastReadDate || answerPost.creationDate.isAfter(this.lastReadDate))) {
             showIcon = true;
         }
     });
     return showIcon;
 }

Line range hint 182-186: Simplify editPosting logic.

The current implementation has a nested if-else that can be simplified.

-editPosting() {
-    if (this.posting.title != '') {
-        this.createEditModal.open();
-    } else {
-        this.isModalOpen.emit();
-    }
-}
+editPosting() {
+    this.posting.title ? this.createEditModal.open() : this.isModalOpen.emit();
+}
src/main/webapp/app/shared/metis/post/post.component.ts (5)

Line range hint 1-15: Remove duplicate import of 'input' from '@angular/core'

There's a duplicate import of input (lowercase) along with Input (uppercase) from '@angular/core'. The lowercase version appears to be unused and should be removed.

import {
    AfterContentChecked,
    ChangeDetectionStrategy,
    ChangeDetectorRef,
    Component,
    EventEmitter,
    HostListener,
    Inject,
    Input,
    OnChanges,
    OnInit,
    Output,
    Renderer2,
    ViewChild,
    ViewContainerRef,
-    input,
} from '@angular/core';

55-66: Fix indentation in imports array

The imports array indentation should follow the 4-space rule as per coding guidelines.

    standalone: true,
    imports: [
-        EmojiPickerComponent,
-        OverlayModule,
-        ArtemisSharedModule,
-        MessageInlineInputComponent,
-        PostFooterComponent,
-        PostReactionsBarComponent,
-        AnswerPostCreateEditModalComponent,
-        PostingContentComponent,
-        PostHeaderComponent,
+       EmojiPickerComponent,
+       OverlayModule,
+       ArtemisSharedModule,
+       MessageInlineInputComponent,
+       PostFooterComponent,
+       PostReactionsBarComponent,
+       AnswerPostCreateEditModalComponent,
+       PostingContentComponent,
+       PostHeaderComponent,
    ],

Line range hint 169-172: Prevent potential memory leaks from document click listener

The document click listener should be cleaned up when the component is destroyed to prevent memory leaks.

+ private destroy$ = new Subject<void>();

+ ngOnDestroy() {
+     this.destroy$.next();
+     this.destroy$.complete();
+ }

- @HostListener('document:click', ['$event'])
- onClickOutside() {
-     this.showDropdown = false;
-     this.enableBodyScroll();
- }
+ constructor() {
+     fromEvent(document, 'click')
+         .pipe(takeUntil(this.destroy$))
+         .subscribe(() => {
+             this.showDropdown = false;
+             this.enableBodyScroll();
+         });
+ }

Line range hint 187-190: Avoid manual change detection in lifecycle hooks

Using changeDetector.detectChanges() in ngAfterContentChecked can lead to performance issues and infinite change detection loops.

Consider using ChangeDetectionStrategy.OnPush with explicit change detection triggers or async pipe in template instead of manual change detection.


Line range hint 156-166: Replace direct DOM manipulation with CSS classes

Direct DOM manipulation for scroll handling should be replaced with CSS classes for better performance and maintainability.

+ @HostBinding('class.scroll-disabled')
+ get isScrollDisabled(): boolean {
+     return this.showDropdown;
+ }

- disableBodyScroll() {
-     const mainContainer = this.document.querySelector('.posting-infinite-scroll-container');
-     if (mainContainer) {
-         this.renderer.setStyle(mainContainer, 'overflow', 'hidden');
-     }
- }

- enableBodyScroll() {
-     const mainContainer = this.document.querySelector('.posting-infinite-scroll-container');
-     if (mainContainer) {
-         this.renderer.setStyle(mainContainer, 'overflow-y', 'auto');
-     }
- }

Add corresponding CSS:

.posting-infinite-scroll-container.scroll-disabled {
    overflow: hidden;
}
src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts (3)

31-31: Avoid importing modules in standalone components

Importing ArtemisSharedModule on line 31 into a standalone component may include unnecessary code and affect tree-shaking.

Consider importing only the required components, directives, and pipes from ArtemisSharedModule directly into your imports array. This optimizes bundle size and adheres to the standalone component paradigm.

Example:

- import { ArtemisSharedModule } from 'app/shared/shared.module';

Then, add the necessary components to your imports:

imports: [
    // Other imports...
    // Add specific components, directives, or pipes needed from ArtemisSharedModule
],

Line range hint 12-12: Remove invalid import { input } from @angular/core

The import { input } does not exist in @angular/core. This can cause runtime errors.

Apply this diff to remove the invalid import:

- import { ..., Input, ..., input } from '@angular/core';
+ import { ..., Input, ... } from '@angular/core';

Line range hint 64-64: Use @Input() decorator for input property isConsecutive

The property isConsecutive is incorrectly assigned using input<boolean>(false);. Instead, use the @Input() decorator to properly declare it as an input property.

Apply this diff to fix the issue:

- isConsecutive = input<boolean>(false);
+ @Input()
+ isConsecutive = false;

This change ensures that isConsecutive is correctly recognized as an input property by Angular.

src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts (4)

Line range hint 1-1: Ensure Consistent Usage of Inputs and Outputs

The component mixes traditional @Input() and @Output() decorators with the new input() and output() functions from Angular Signals. For clarity and maintainability, consider using a consistent approach throughout the component.

To standardize, you can convert existing @Input() and @Output() properties to use input() and output(), or revert to using @Input() and @Output() decorators for all inputs and outputs.


11-14: Replace Module Imports with Specific Component Imports

In standalone components, it's recommended to import only the necessary components, directives, and pipes directly instead of entire modules. This improves tree-shaking and reduces bundle size.

Consider importing specific components and directives used in your template:

-import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module';
-import { ArtemisSharedModule } from 'app/shared/shared.module';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe';
+import { TranslateDirective } from 'app/shared/language/translate.directive';
import { LinkPreviewContainerComponent } from 'app/shared/link-preview/components/link-preview-container/link-preview-container.component';
import { PostingContentPartComponent } from 'app/shared/metis/posting-content/posting-content-part/posting-content-part.components';

@Component({
    // ...
    imports: [
+       FaIconComponent,
+       HtmlForMarkdownPipe,
+       TranslateDirective,
        LinkPreviewContainerComponent,
        PostingContentPartComponent,
    ],
})

Line range hint 23-27: Access Input Signals Correctly

When using the input() function from Angular Signals, the property becomes a signal and should be accessed by invoking it as a function. Ensure you're consistently accessing these properties using this.propertyName().

For example, when checking isSubscribeToMetis, use:

if (!this.isSubscribeToMetis()) { ... }

Review all usages of input() properties to ensure they are accessed correctly.


Line range hint 53-57: Inconsistent Logic in ngOnChanges Method

The logic in ngOnChanges differs from that in ngOnInit. In ngOnInit, when isSubscribeToMetis is false, you call computePostingContentParts, but in ngOnChanges, you call computeContentPartsOfPosts when isSubscribeToMetis is false. This may lead to unexpected behavior.

Consider updating ngOnChanges to align with ngOnInit:

ngOnChanges(): void {
    if (!this.isSubscribeToMetis()) {
+       const patternMatches: PatternMatch[] = this.getPatternMatches();
+       this.computePostingContentParts(patternMatches);
    } else {
        this.computeContentPartsOfPosts();
    }
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7d1a660 and 7276758.

📒 Files selected for processing (25)
  • src/main/webapp/app/shared/link-preview/components/link-preview-container/link-preview-container.component.ts (1 hunks)
  • src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts (2 hunks)
  • src/main/webapp/app/shared/metis/emoji/emoji-picker.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/emoji/emoji.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/message/message-inline-input/message-inline-input.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/metis.module.ts (3 hunks)
  • src/main/webapp/app/shared/metis/post/post.component.ts (2 hunks)
  • src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (2 hunks)
  • src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (2 hunks)
  • src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (2 hunks)
  • src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-thread/posting-thread.component.ts (1 hunks)
  • src/main/webapp/app/shared/pipes/html-for-posting-markdown.pipe.ts (1 hunks)
  • src/main/webapp/app/shared/pipes/reacting-users-on-posting.pipe.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (25)
src/main/webapp/app/shared/link-preview/components/link-preview-container/link-preview-container.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/emoji/emoji-picker.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/emoji/emoji.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/message/message-inline-input/message-inline-input.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/metis.module.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/post/post.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-thread/posting-thread.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/pipes/html-for-posting-markdown.pipe.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/pipes/reacting-users-on-posting.pipe.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

🔇 Additional comments (43)
src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.ts (1)

10-10: 🛠️ Refactor suggestion

Verify standalone component dependencies

While marking the component as standalone is correct, we need to ensure all required dependencies are properly imported in the imports array of the @component decorator.

Apply this diff to add the necessary imports:

 @Component({
     templateUrl: './enlarge-slide-image.component.html',
     standalone: true,
+    imports: [
+        // Add Material Dialog modules if using any in the template
+        // Example: MatDialogModule, MatButtonModule, etc.
+    ],
 })
✅ Verification successful

No additional imports needed for this standalone component

The component is correctly configured as standalone and only needs MAT_DIALOG_DATA injection token which is already properly imported. The template is simple, only using standard HTML elements (div and img) with Angular's built-in property bindings, requiring no additional imports.

  • Template uses only basic HTML elements and Angular's built-in bindings ([style], [src])
  • Component already imports the necessary MAT_DIALOG_DATA from @angular/material/dialog
  • No Material Dialog UI components are used in the template
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check template file for Material Dialog dependencies
rg -l "mat-dialog" "src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.html"

Length of output: 122


Script:

#!/bin/bash
# Let's first check if the template file exists and see its content
cat src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.html

# Also check the component file for any Material imports and usage
cat src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.ts

# Additionally search for any Material Dialog related imports in the component
rg "MatDialog|MAT_DIALOG" src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.ts

Length of output: 966

src/main/webapp/app/shared/metis/emoji/emoji.component.ts (1)

4-4: Verify EmojiModule usage in the template

The EmojiModule import looks correct, but let's verify it's being used in the template file.

✅ Verification successful

EmojiModule import is correctly used in the template

The template uses the ngx-emoji component which is provided by the EmojiModule, with proper property bindings for both light and dark themes. The import is necessary and correctly configured in the standalone component.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the template uses any directives from EmojiModule

# Look for the template file and check its content
rg -l "emoji.component.html" | xargs cat

Length of output: 694


Script:

#!/bin/bash
# Search for the actual template file content
fd -e html emoji.component.html --exec cat {}

Length of output: 395

src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts (3)

1-4: LGTM! Imports are well-organized and follow guidelines.

The imports follow Angular style guidelines and include the necessary FontAwesomeModule for standalone component functionality.


10-11: LGTM! Correct standalone component configuration.

The component is properly configured as standalone with the necessary FontAwesomeModule import.


Line range hint 13-24: LGTM! Well-structured component class.

The class follows Angular guidelines with proper naming conventions and appropriate use of decorators and host bindings.

src/main/webapp/app/shared/metis/posting-thread/posting-thread.component.ts (2)

4-4: Verify template dependencies are complete

The PostComponent import has been added for standalone mode, but we should verify if there are other components used in the template that need to be imported.

✅ Verification successful

Import dependencies are correctly configured

The template only uses the jhi-post selector which corresponds to the imported PostComponent (verified selector: 'jhi-post'). No additional component dependencies need to be imported.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check template for additional component dependencies
# Look for any component selectors used in the template

# Get the template content and search for component selectors
cat src/main/webapp/app/shared/metis/posting-thread/posting-thread.component.html | grep -o 'jhi-[a-zA-Z0-9-]*' | sort -u

Length of output: 132


Script:

#!/bin/bash
# Let's verify if the selector 'jhi-post' matches with PostComponent
ast-grep --pattern 'selector: $SELECTOR' src/main/webapp/app/shared/metis/post/post.component.ts

Length of output: 98


Script:

#!/bin/bash
# Let's try a different approach to verify the component selector
# First, check if the file exists
fd "post.component.ts" src/main/webapp/app/shared/metis/post/

# Then read its content to find the selector
cat src/main/webapp/app/shared/metis/post/post.component.ts | grep -A 1 "@Component"

Length of output: 241


11-12: Verify parent component/module registration

The standalone configuration looks good. Since this is part of the Metis migration, ensure this component is properly imported where it's being used.

✅ Verification successful

Component registration is properly handled

The PostingThreadComponent is correctly used in its standalone configuration:

  • It's properly imported in discussion-section.component.html and conversation-messages.component.ts
  • It's being removed from MetisModule's declarations and moved to imports, which is the correct pattern for standalone components
  • All components using it are importing it directly in their imports array
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find usages of this component in the codebase

# Search for the component selector in template files
echo "Checking component usage in templates:"
rg "jhi-posting-thread" --type html -l

# Search for component imports
echo -e "\nChecking component imports:"
rg "PostingThreadComponent" --type typescript -l

Length of output: 707


Script:

#!/bin/bash
# Search for PostingThreadComponent imports with ripgrep (using .ts extension)
echo "Checking component imports:"
rg "PostingThreadComponent" -g "*.ts" -l

# Check the content of files that use the component in templates
echo -e "\nChecking discussion-section.component.html:"
cat "src/main/webapp/app/overview/discussion-section/discussion-section.component.html"

echo -e "\nChecking discussion-section.component.ts:"
cat "src/main/webapp/app/overview/discussion-section/discussion-section.component.ts"

Length of output: 26132


Script:

#!/bin/bash
# Check the content of conversation-messages.component.ts which imports PostingThreadComponent
echo "Checking conversation-messages.component.ts:"
cat "src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts"

# Check the content of metis.module.ts to see how it was previously registered
echo -e "\nChecking metis.module.ts:"
cat "src/main/webapp/app/shared/metis/metis.module.ts"

Length of output: 23831

src/main/webapp/app/shared/metis/emoji/emoji-picker.component.ts (3)

12-13: Successfully migrated to standalone component

The component has been correctly configured as standalone with the necessary imports. This change aligns with the PR objective of migrating to standalone components and follows Angular's best practices.


Line range hint 15-31: Implementation follows Angular best practices

The component demonstrates good practices:

  • Proper use of dependency injection with inject
  • Well-typed inputs/outputs
  • Effective use of computed properties
  • Follows naming conventions (camelCase for methods/properties)

5-7: Consider optimizing shared module imports

While the imports are correctly added for standalone component support, importing the entire ArtemisSharedModule might bring unnecessary dependencies. Consider importing only the required components/directives from the shared module to optimize bundle size.

Let's check what's actually used from the shared module:

src/main/webapp/app/shared/pipes/html-for-posting-markdown.pipe.ts (1)

5-8: Verify standalone pipe usage across the application

The migration to standalone pipe looks correct. However, we should verify that all components using this pipe have properly imported it.

✅ Verification successful

Migration to standalone pipe is correctly implemented

The verification shows that:

  1. The component using the pipe (PostingContentPartComponent) is already a standalone component
  2. The pipe is properly imported in the component's imports array
  3. The pipe is used in the component's template
  4. The implementation follows Angular's standalone migration guidelines
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all components that use the htmlForPostingMarkdown pipe
# to ensure they properly import it after the standalone migration

# Search for pipe usage in templates
echo "Checking template usage:"
rg -l "htmlForPostingMarkdown" --glob "*.html"

# Search for imports in component files
echo -e "\nChecking imports:"
rg -l "HtmlForPostingMarkdownPipe" --glob "*.ts"

Length of output: 704


Script:

#!/bin/bash
# Let's check the import statements in the component that uses this pipe
echo "Checking PostingContentPartComponent implementation:"
rg -A 5 "HtmlForPostingMarkdownPipe" src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts

echo -e "\nChecking if the component is standalone:"
ast-grep --pattern 'Component({
  $$$
  standalone: $_,
  $$$
})' src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts

echo -e "\nChecking MetisModule imports:"
rg -A 10 "@NgModule" src/main/webapp/app/shared/metis/metis.module.ts

Length of output: 2597

src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.ts (2)

6-8: LGTM! Imports are properly organized.

The new imports are correctly structured and necessary for the standalone component migration.


14-15: Verify standalone status of imported components.

The standalone configuration looks correct. However, we should ensure that all imported components (ProfilePictureComponent, EmojiPickerComponent) are also marked as standalone components.

✅ Verification successful

Let me try a different approach to verify the standalone status of these components.


Let me try one more time with a corrected regex pattern.


All imported components are properly configured as standalone

Both ProfilePictureComponent and EmojiPickerComponent are correctly marked as standalone components, which is consistent with the Angular standalone migration requirements. The verification confirms that the component configuration is correct and follows best practices.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if imported components are marked as standalone

# Check ProfilePictureComponent
echo "Checking ProfilePictureComponent..."
ast-grep --pattern 'Component({
  $$$
  standalone: true,
  $$$
})
export class ProfilePictureComponent' src/main/webapp/app/shared/profile-picture/profile-picture.component.ts

# Check EmojiPickerComponent
echo "Checking EmojiPickerComponent..."
ast-grep --pattern 'Component({
  $$$
  standalone: true,
  $$$
})
export class EmojiPickerComponent' src/main/webapp/app/shared/metis/emoji/emoji-picker.component.ts

Length of output: 503


Script:

#!/bin/bash
# Search for component declarations with more relaxed patterns

echo "Checking ProfilePictureComponent declaration..."
rg -A 5 "@Component\({" src/main/webapp/app/shared/profile-picture/profile-picture.component.ts

echo -e "\nChecking EmojiPickerComponent declaration..."
rg -A 5 "@Component\({" src/main/webapp/app/shared/metis/emoji/emoji-picker.component.ts

Length of output: 578


Script:

#!/bin/bash
# Search for component declarations with fixed regex pattern

echo "Checking ProfilePictureComponent declaration..."
rg -A 5 "@Component\(" src/main/webapp/app/shared/profile-picture/profile-picture.component.ts

echo -e "\nChecking EmojiPickerComponent declaration..."
rg -A 5 "@Component\(" src/main/webapp/app/shared/metis/emoji/emoji-picker.component.ts

# Also check if these files exist
echo -e "\nChecking if files exist..."
fd "profile-picture.component.ts|emoji-picker.component.ts" src/main/webapp/app/shared/

Length of output: 1135

src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.ts (2)

16-17: LGTM! Successful migration to standalone component

The component has been correctly configured as standalone with its required dependencies. This change aligns with Angular's modern architecture patterns and the PR's objective of migrating to standalone components.


Line range hint 1-48: Verify template compatibility with standalone setup

The component logic looks good, but we should verify that the template usage aligns with the standalone configuration, especially regarding the modal component and any shared module directives.

✅ Verification successful

Template compatibility with standalone setup is verified and working correctly

The component's template dependencies are properly handled through the imports array:

  • ProfilePictureComponent is imported directly as standalone
  • FontAwesomeModule provides the fa-icon component
  • ArtemisSharedModule provides the necessary directives (jhiTranslate, ngbTooltip)
  • The modal component is also standalone and properly imported
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check template for shared module directive usage
rg -l "post-header\.component\.html" | xargs rg "^\s*<[^>]*?(jhiTranslate|jhiSort|jhiSortBy)[^>]*>" -A 2

# Check PostCreateEditModalComponent usage in other standalone components
ast-grep --pattern 'import { $_ } from "@/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component"'

Length of output: 247


Script:

#!/bin/bash
# Let's try a different approach to verify template usage

# First, find the template file
fd "post-header.component.html"

# Then check its content
fd "post-header.component.html" --exec cat {}

# Also check if PostCreateEditModalComponent is standalone
ast-grep --pattern 'standalone: true' --lang typescript

Length of output: 5414


Script:

#!/bin/bash
# Let's verify the imports needed for template directives

# Check if ArtemisSharedModule exports the required directives
fd "shared.module.ts" --exec cat {}

# Check if PostCreateEditModalComponent exists and is standalone
fd "post-create-edit-modal.component.ts" --exec cat {}

# Check for NgbTooltip import
ast-grep --pattern 'import { $_ } from "@ng-bootstrap/ng-bootstrap"'

Length of output: 27258

src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts (2)

7-8: LGTM: Module imports are correctly structured

The new module imports follow Angular's naming conventions and are properly referenced.


14-15: Verify standalone component dependencies

The standalone configuration looks good, but let's verify all required dependencies are included based on the template usage.

✅ Verification successful

Dependencies are correctly configured for the standalone component

The component's dependencies are properly set up:

  • ArtemisSharedModule provides all necessary dependencies through its sub-modules:
    • CommonModule from ArtemisSharedLibsModule provides the ngClass directive
    • ArtemisTranslatePipe for the artemisTranslate pipe is available through ArtemisSharedCommonModule
  • ArtemisConfirmIconModule provides the jhi-confirm-icon component
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check template dependencies to ensure all required modules are imported

# Find the template file and check its content for Angular directives/components
cat src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.html | grep -E 'ng[A-Z]|jhi-|fa-'

# Check for any potential missing imports by looking for other component selectors
ast-grep --pattern 'selector: $_' | grep -v 'jhi-link-preview'

Length of output: 2439


Script:

#!/bin/bash
# Let's check the component's template and TypeScript file more thoroughly

# Check the template content
echo "=== Template Content ==="
cat src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.html

# Check the component file for any other dependencies
echo -e "\n=== Component File Content ==="
cat src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts

# Check what's included in ArtemisSharedModule
echo -e "\n=== Checking ArtemisSharedModule ==="
rg -l "ArtemisSharedModule" --type ts

Length of output: 22090


Script:

#!/bin/bash
# Let's check what's included in ArtemisSharedModule to verify if it covers the template dependencies

echo "=== ArtemisSharedModule Content ==="
cat src/main/webapp/app/shared/shared.module.ts

# Also check what's in ArtemisConfirmIconModule since it's used
echo -e "\n=== ArtemisConfirmIconModule Content ==="
cat src/main/webapp/app/shared/confirm-icon/confirm-icon.module.ts

Length of output: 5378


Script:

#!/bin/bash
# Let's check what's in ArtemisSharedCommonModule and ArtemisSharedLibsModule since they're part of ArtemisSharedModule
# and we need to verify if they provide the necessary NgClass directive

echo "=== ArtemisSharedCommonModule Content ==="
cat src/main/webapp/app/shared/shared-common.module.ts

echo -e "\n=== ArtemisSharedLibsModule Content ==="
cat src/main/webapp/app/shared/shared-libs.module.ts

# Also check the pipes module since we see artemisTranslate pipe usage
echo -e "\n=== ArtemisSharedPipesModule Content ==="
cat src/main/webapp/app/shared/pipes/shared-pipes.module.ts

Length of output: 4925

src/main/webapp/app/shared/pipes/reacting-users-on-posting.pipe.ts (2)

11-14: LGTM: Standalone pipe migration looks good!

The pipe has been correctly marked as standalone, which aligns with Angular's standalone component architecture and the PR's migration objectives.


Line range hint 1-65: Great documentation and code style!

The code is well-documented with JSDoc comments, follows Angular style guidelines, and maintains good naming conventions. The constants are properly defined at the top of the file, and the methods are clearly named and organized.

src/main/webapp/app/shared/link-preview/components/link-preview-container/link-preview-container.component.ts (2)

6-6: LGTM! Successful migration to standalone component

The component has been correctly migrated to standalone with proper import configuration, improving modularity as intended.

Also applies to: 13-14


Line range hint 19-23: Verify signal cleanup in component destruction

While the new signal-based input() syntax is well implemented, ensure proper cleanup of signals when the component is destroyed to prevent memory leaks.

src/main/webapp/app/shared/metis/message/message-inline-input/message-inline-input.component.ts (2)

10-12: LGTM! Imports are properly structured.

The new imports follow Angular's style guide and are necessary for the standalone component configuration.


19-20: LGTM! Standalone component configuration is correct.

The component is properly configured as standalone with the necessary dependencies imported.

src/main/webapp/app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component.ts (1)

10-12: LGTM! Verify standalone component dependencies.

The migration to standalone component and its imports look correct.

Let's verify that all imported components are also standalone:

Also applies to: 19-20

✅ Verification successful

Dependencies verified - all components are standalone

Both PostingButtonComponent and PostingMarkdownEditorComponent are properly configured as standalone components with their own imports. The migration to standalone components is correctly implemented.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that imported components are standalone
# Expected: Each component should have standalone: true in its decorator

ast-grep --pattern 'standalone: true' PostingButtonComponent.ts PostingMarkdownEditorComponent.ts

Length of output: 253


Script:

#!/bin/bash
# First, let's find the correct paths for these components
fd -t f "posting-button.component.ts|posting-markdown-editor.component.ts"

# Then search for standalone configuration in these files
ast-grep --pattern 'Component({
  $$$
  standalone: true,
  $$$
})'

Length of output: 100549


Script:

#!/bin/bash
# Let's specifically check the standalone configuration for PostingButtonComponent and PostingMarkdownEditorComponent
ast-grep --pattern 'standalone: true' src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts

Length of output: 209


Script:

#!/bin/bash
# Let's check the Component decorators for these specific components
ast-grep --pattern '@Component({
  $$$
})' src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts

Length of output: 3002

src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts (2)

9-11: LGTM: Imports are correctly structured for standalone component.

The new imports properly include the required dependencies for the standalone component migration.


18-19: Verify component dependencies and check for circular imports.

The standalone configuration looks correct, but let's ensure all dependencies are properly handled.

Let's verify the component's dependencies and check for potential circular imports:

✅ Verification successful

Let me generate another script to better analyze the dependencies and template usage.


Component dependencies are properly configured and no circular dependencies detected

Based on the analysis:

  • The template uses jhi-posting-markdown-editor and jhi-posting-button components, which are correctly imported via PostingMarkdownEditorComponent and PostingButtonComponent
  • ArtemisSharedModule is imported which provides the common Angular directives and pipes used in the template (form directives, translate pipe)
  • The component is imported by other components (PostFooterComponent, PostComponent, AnswerPostComponent) but only as a child component reference, not creating any circular dependencies
  • The component is registered in metis.module.ts but only as a declaration, which is fine for standalone components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential circular dependencies and verify all required imports

# Check for potential circular dependencies
echo "Checking for potential circular dependencies..."
rg -l "import.*AnswerPostCreateEditModalComponent" src/main/webapp/app/shared/metis/

# Verify all components used in the template are imported
echo "Checking template dependencies..."
TEMPLATE_FILE=$(echo "src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.html")
if [ -f "$TEMPLATE_FILE" ]; then
    # Look for Angular component selectors in the template
    echo "Components used in template:"
    rg -o '<[^>]*>' "$TEMPLATE_FILE" | rg -o '[a-zA-Z0-9-]+(?=[^>]*>)' | sort -u
fi

Length of output: 1470


Script:

#!/bin/bash
# Check template content and imports more thoroughly

# First, let's see the actual template content
echo "Template content:"
cat src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.html

echo -e "\nChecking component imports and usage:"
# Check the component's implementation
cat src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts

# Check where this component is imported to verify no circular dependencies
echo -e "\nChecking imports in potential circular dependency files:"
echo "1. metis.module.ts:"
cat src/main/webapp/app/shared/metis/metis.module.ts | grep -A 5 -B 5 "AnswerPostCreateEditModalComponent"

echo -e "\n2. post-footer.component.ts:"
cat src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts | grep -A 5 -B 5 "AnswerPostCreateEditModalComponent"

echo -e "\n3. post.component.ts:"
cat src/main/webapp/app/shared/metis/post/post.component.ts | grep -A 5 -B 5 "AnswerPostCreateEditModalComponent"

echo -e "\n4. answer-post.component.ts:"
cat src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts | grep -A 5 -B 5 "AnswerPostCreateEditModalComponent"

Length of output: 14218

src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts (1)

6-12: LGTM! Import statements are well-organized.

The consolidation of Material imports and addition of necessary module imports for standalone component conversion is clean and follows Angular best practices.

src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts (1)

19-20: LGTM! Proper standalone component configuration

The component is correctly configured as standalone with all necessary dependencies imported.

src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (3)

23-25: LGTM! Required imports added for standalone component.

The imports are correctly added and follow the Angular style guide.


31-32: LGTM! Component correctly configured as standalone.

The standalone configuration and imports array are properly set up.


Line range hint 93-98: Verify dialog subscription cleanup.

The dialog.open() call could potentially cause memory leaks. Consider storing and cleaning up the dialog reference:

private dialogRef: MatDialogRef<EnlargeSlideImageComponent>;

enlargeImage(slideToReference: string) {
    this.dialogRef = this.dialog.open(EnlargeSlideImageComponent, {
        data: { slideToReference },
        maxWidth: '95vw',
    });
    
    // Clean up in ngOnDestroy
    this.dialogRef.afterClosed().subscribe();
}
src/main/webapp/app/shared/metis/metis.module.ts (1)

77-90: Verify potential circular dependencies

The current organization of components might lead to circular dependencies, particularly between posting-related components. Additionally, the components aren't grouped by functionality, which could make maintenance more difficult.

Consider:

  1. Grouping components by feature (e.g., all posting-related components together)
  2. Breaking down large components into smaller, more focused ones
  3. Implementing a clear dependency direction (e.g., content → header → button)
src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (3)

23-24: LGTM: Import statements follow Angular style guide.

The new imports are correctly structured and necessary for the standalone component configuration.


35-36: LGTM: Standalone component migration looks good.

The component is correctly configured as standalone with the necessary imports.


35-36: Verify: Check for circular dependencies.

The component imports both specific components and the shared module. Let's verify there are no circular dependencies.

✅ Verification successful

Let me check the shared module imports to complete the verification.


No circular dependencies detected

Based on the verification results, there are no circular dependencies found. Neither AnswerPostComponent, AnswerPostCreateEditModalComponent, nor ArtemisSharedModule import the PostFooterComponent, which confirms the imports are safe to use.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential circular dependencies
# Look for imports of post-footer.component in the imported components

echo "Checking for circular dependencies..."

# Check if AnswerPostComponent imports PostFooterComponent
ast-grep --pattern 'import { $_ } from "app/shared/metis/posting-footer/post-footer/post-footer.component"' src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts

# Check if AnswerPostCreateEditModalComponent imports PostFooterComponent
ast-grep --pattern 'import { $_ } from "app/shared/metis/posting-footer/post-footer/post-footer.component"' src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts

Length of output: 506


Script:

#!/bin/bash
# Check if ArtemisSharedModule imports or exports PostFooterComponent
echo "Checking shared module..."
ast-grep --pattern 'import { $_ } from "app/shared/metis/posting-footer/post-footer/post-footer.component"' src/main/webapp/app/shared/shared.module.ts

# Also check the declarations/exports
echo "Checking module declarations and exports..."
rg "PostFooterComponent" src/main/webapp/app/shared/shared.module.ts

Length of output: 380

src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (2)

45-48: LGTM! Required imports for standalone component migration.

The new imports correctly include all necessary modules and components for the standalone configuration.


62-63: Verify standalone component dependencies.

The standalone configuration is correct. However, let's verify that all imported modules are themselves standalone-ready to prevent potential circular dependencies.

✅ Verification successful

Based on the verification results, I can now generate the final response:

Standalone component dependencies are correctly configured

The PostingMarkdownEditorComponent's dependencies are properly configured for standalone mode:

  • PostingContentComponent is a standalone component with its own imports
  • ArtemisMarkdownEditorModule, ArtemisSharedModule, and ArtemisSharedComponentModule are properly exported modules
  • No circular dependencies were detected in the imports chain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if imported modules are standalone-ready
ast-grep --pattern 'export class $module implements $_ {
  $$$
}' src/main/webapp/app/shared/markdown-editor/markdown-editor.module.ts src/main/webapp/app/shared/shared.module.ts src/main/webapp/app/shared/components/shared-component.module.ts

# Check for potential circular dependencies
rg -l "import.*PostingMarkdownEditorComponent" src/main/webapp/app/shared/

Length of output: 825


Script:

#!/bin/bash
# Let's check the module declarations and imports in the modules
rg -A 10 "@NgModule\({" src/main/webapp/app/shared/markdown-editor/markdown-editor.module.ts src/main/webapp/app/shared/shared.module.ts src/main/webapp/app/shared/components/shared-component.module.ts

# Check if PostingContentComponent is standalone
ast-grep --pattern 'export class PostingContentComponent {
  $$$
}'

# Also check its decorator
ast-grep --pattern '@Component({
  $$$
  standalone: true,
  $$$
})'

Length of output: 101129


Script:

#!/bin/bash
# Let's check the module declarations with proper escaping
rg -A 10 "@NgModule\(\{" src/main/webapp/app/shared/markdown-editor/markdown-editor.module.ts

# Check PostingContentComponent specifically
rg -A 10 "export class PostingContentComponent" src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts

# Check if ArtemisMarkdownEditorModule is standalone
rg -A 10 "export class ArtemisMarkdownEditorModule" src/main/webapp/app/shared/markdown-editor/markdown-editor.module.ts

# Check if ArtemisSharedModule is standalone
rg -A 10 "export class ArtemisSharedModule" src/main/webapp/app/shared/shared.module.ts

# Check if ArtemisSharedComponentModule is standalone
rg -A 10 "export class ArtemisSharedComponentModule" src/main/webapp/app/shared/components/shared-component.module.ts

Length of output: 1474

src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (2)

15-21: LGTM: Imports are properly structured for standalone component.

The new imports correctly include all necessary dependencies for the standalone component migration, following Angular's style guide.


33-42: Verify template dependencies for standalone component.

The component is correctly configured as standalone. Let's verify that all imported components and modules are actually used in the template.

✅ Verification successful

All imported components are used in the template

Based on the template content, I can verify that all standalone components are actively used:

  • <jhi-posting-markdown-editor> is used for content editing
  • <jhi-posting-button> is used in the modal footer
  • <jhi-post-tag-selector> is used for tag selection
  • <jhi-post> is used to display similar posts

The shared modules (ArtemisSharedModule and ArtemisSharedComponentModule) are also necessary as they provide common directives used in the template:

  • jhiTranslate directive
  • jhi-help-icon component
  • Form-related directives
  • NgbAccordion components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if all imported components are used in the template

# Find the template file
template_file=$(fd --type f "post-create-edit-modal.component.html" --exec echo {})

if [ -n "$template_file" ]; then
    echo "Checking component usage in template:"
    
    # Check for each component's selector
    echo "PostingMarkdownEditor usage:"
    rg "jhi-posting-markdown-editor" "$template_file" -A 2
    
    echo "PostingContent usage:"
    rg "jhi-posting-content" "$template_file" -A 2
    
    echo "PostingButton usage:"
    rg "jhi-posting-button" "$template_file" -A 2
    
    echo "PostTagSelector usage:"
    rg "jhi-post-tag-selector" "$template_file" -A 2
    
    echo "Post component usage:"
    rg "jhi-post" "$template_file" -A 2
fi

Length of output: 3920


Script:

#!/bin/bash
# Let's try a different approach to check the template content
echo "Content of post-create-edit-modal.component.html:"
cat "./src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.html"

# Also, let's verify the component selectors
echo -e "\nComponent selectors:"
# PostingMarkdownEditorComponent selector
rg -A 2 "@Component.*selector.*posting-markdown-editor"

# PostingContentComponent selector
rg -A 2 "@Component.*selector.*posting-content"

# PostingButtonComponent selector
rg -A 2 "@Component.*selector.*posting-button"

# PostTagSelectorComponent selector
rg -A 2 "@Component.*selector.*post-tag-selector"

# PostComponent selector
rg -A 2 "@Component.*selector.*post['\"]"

Length of output: 5083

src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts (1)

17-22: LGTM: Imports are well-structured and follow Angular guidelines.

The new imports are appropriate for the standalone component migration and follow proper naming conventions.

src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts (1)

28-28: Verify the import path for AnswerPostCreateEditModalComponent

Ensure that the import path for AnswerPostCreateEditModalComponent on line 28 is correct. It seems inconsistent with the directory structure.

Please confirm if the path should be:

- import { AnswerPostCreateEditModalComponent } from 'app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal.component';
+ import { AnswerPostCreateEditModalComponent } from 'app/shared/metis/answer-post-create-edit-modal/answer-post-create-edit-modal.component';

Run the following script to check if the file exists at the specified path:

src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts (2)

21-22: Verify All Template Dependencies Are Imported

Ensure that all components, directives, and pipes used in your template are included in the imports array. Missing imports can lead to runtime errors.

Review your template files and add any missing imports to the imports array.


Line range hint 85-87: Confirm Unsubscription from Subscriptions

You've correctly unsubscribed from postsSubscription in ngOnDestroy, which helps prevent memory leaks.

Comment on lines +10 to +11
standalone: true,
imports: [EmojiModule],
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Migration to standalone component is incomplete - component is still declared in MetisModule

The EmojiComponent has been configured as standalone but is still both imported and exported in the MetisModule. This is incorrect as standalone components should not be declared in NgModules. The component needs to be removed from both the imports and exports arrays of MetisModule.

  • Remove EmojiComponent from the imports array in src/main/webapp/app/shared/metis/metis.module.ts
  • Remove EmojiComponent from the exports array in src/main/webapp/app/shared/metis/metis.module.ts
🔗 Analysis chain

LGTM! Standalone component migration looks correct.

The component has been properly configured as standalone with the necessary imports. This aligns with the PR objective of migrating Metis to standalone components and follows Angular's standalone component patterns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify this component is no longer declared in any NgModule

# Search for any remaining module declarations of this component
ast-grep --pattern 'NgModule({
  $$$
  declarations: [
    $$$
    EmojiComponent,
    $$$
  ]
  $$$
})'

Length of output: 230


Script:

#!/bin/bash
# Let's try a different approach to verify the component's migration
# First, let's find any NgModule that might reference EmojiComponent
rg "EmojiComponent" -l

# Then, let's also verify the component's standalone configuration
rg "standalone.*true" --type ts

# And check for any potential module files in the same directory
fd "module\.ts$" -p src/main/webapp/app/shared/metis/emoji/

Length of output: 19479


Script:

#!/bin/bash
# Let's examine the metis.module.ts file since it references EmojiComponent
cat src/main/webapp/app/shared/metis/metis.module.ts

Length of output: 6528

Comment on lines +8 to +10
import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { ArtemisSharedModule } from 'app/shared/shared.module';
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the template content and shared module analysis, I can now provide a final response:

Consider importing specific components instead of ArtemisSharedModule

Looking at the template and shared module content, your component only uses:

  • ngIf/ngClass directives (from CommonModule)
  • ngbTooltip (from NgbModule)
  • jhiTranslate directive and artemisTranslate pipe (from SharedCommonModule)
  • fa-icon (already imported via FontAwesomeModule)

Instead of importing the entire ArtemisSharedModule, import just these specific dependencies:

imports: [
    ProfilePictureComponent,
    FontAwesomeModule,
    CommonModule,
    NgbTooltipModule,
    TranslateModule,
    ArtemisSharedCommonModule
]
🔗 Analysis chain

Verify shared module import in standalone component

While the imports for ProfilePictureComponent and FontAwesomeModule are appropriate, importing the entire ArtemisSharedModule in a standalone component might lead to circular dependencies. Consider importing only the specific components/directives needed from the shared module.


Let me gather more information about what's actually being used from ArtemisSharedModule in these components.


Let me try one more search to find the template content and shared module exports:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if ArtemisSharedModule is imported in other standalone components
ast-grep --pattern 'imports: [$_] { ArtemisSharedModule }'

# Check what's actually used from ArtemisSharedModule in the template
rg -l "post-header\.component\.html" | xargs cat

Length of output: 3455


Script:

#!/bin/bash
# Check the contents of the template files to see what shared components are used
rg -l "post-header\.component\.html" | xargs cat

# Check what's exported from ArtemisSharedModule
rg -l "shared\.module\.ts" | xargs cat

# Check if post-header component is used within ArtemisSharedModule
ast-grep --pattern 'imports: [$_] { PostHeaderComponent }'

Length of output: 3496


Script:

#!/bin/bash
# Find and show the actual template content
fd -e html post-header.component.html -x cat {}

# Find and show the shared module content
fd shared.module.ts -x cat {}

Length of output: 19414

Comment on lines +19 to +20
standalone: true,
imports: [ArtemisSharedModule, PostingButtonComponent, PostingMarkdownEditorComponent],
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Consider selective imports and encapsulation changes

  1. The component is importing the entire ArtemisSharedModule but only needs the common Angular features which are already available through standalone imports. The shared module can be removed as PostingButtonComponent and PostingMarkdownEditorComponent are already imported separately.

  2. The component uses ViewEncapsulation.None which can lead to style leaks. Given the presence of a dedicated SCSS file (message-inline-input.component.scss), the component should use the default encapsulation mode to properly scope its styles.

  • Remove ArtemisSharedModule from imports as it's not needed in standalone components
  • Change encapsulation: ViewEncapsulation.None to use default encapsulation or explicitly set it to ViewEncapsulation.Emulated
🔗 Analysis chain

Verify shared module imports and encapsulation.

Two considerations:

  1. Consider importing only the needed components/directives from ArtemisSharedModule to reduce bundle size
  2. Using ViewEncapsulation.None might lead to style leaks. Ensure styles are properly scoped.

Let's check the shared module imports and style usage:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check shared module exports and style usage

# Find shared module exports
echo "Checking shared module exports:"
rg -A 10 "export class ArtemisSharedModule" src/

# Check for style references to this component
echo "Checking style references:"
rg -l "jhi-message-inline-input" src/ --type css --type scss

Length of output: 356


Script:

#!/bin/bash
# Let's check the shared module content and style encapsulation

# Check shared module content
echo "Checking shared module content:"
cat src/main/webapp/app/shared/shared.module.ts

# Check component's style encapsulation
echo -e "\nChecking component's style encapsulation:"
rg -A 5 "@Component" src/main/webapp/app/shared/metis/message/message-inline-input/message-inline-input.component.ts

# Check for style files with correct extension
echo -e "\nChecking style files:"
fd -e scss -e css . src/main/webapp/app/shared/metis/message/message-inline-input/

# Check imports in the component
echo -e "\nChecking component imports:"
rg "import.*from.*shared" src/main/webapp/app/shared/metis/message/message-inline-input/message-inline-input.component.ts

Length of output: 6250

Comment on lines +19 to +20
standalone: true,
imports: [ReactingUsersOnPostingPipe, EmojiComponent, ArtemisSharedModule, ArtemisConfirmIconModule, EmojiPickerComponent, OverlayModule],
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 component dependencies

Based on the verification results:

  • EmojiComponent and EmojiPickerComponent are properly configured as standalone components
  • ReactingUsersOnPostingPipe appears to be a pipe, not a component (missing @component decorator)
  • ArtemisSharedModule and ArtemisConfirmIconModule are NgModules that may contain dependencies needed by the standalone component

The component imports need to be adjusted:

  • ReactingUsersOnPostingPipe should be imported directly from the pipes module
  • Consider importing only necessary components from ArtemisSharedModule instead of the entire module
  • Review if ArtemisConfirmIconModule's ConfirmIconComponent can be made standalone
🔗 Analysis chain

Verify standalone component integration

Let's verify that all required dependencies are properly available and no circular dependencies are introduced.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential circular dependencies and verify imports

# Check for circular dependencies by looking for mutual imports
echo "Checking for circular dependencies..."
rg -l "import.*AnswerPostReactionsBarComponent" | xargs -I {} rg -l "import.*from.*{}" src/main/webapp/app/shared/metis

# Verify that all imported standalone components are properly exported
echo "Verifying standalone component exports..."
for component in "ReactingUsersOnPostingPipe" "EmojiComponent" "EmojiPickerComponent"; do
    rg "export.*class.*$component.*{.*standalone.*:.*true" 
done

Length of output: 1306


Script:

#!/bin/bash
# Let's fix the regex syntax and try again with proper escaping

# Check for circular dependencies
echo "Checking for circular dependencies..."
rg -l "import.*AnswerPostReactionsBarComponent" | xargs -I {} rg -l "import.*from.*{}" src/main/webapp/app/shared/metis

# Verify standalone components with fixed regex
echo "Verifying standalone component exports..."
rg "export.*class.*ReactingUsersOnPostingPipe.*\{.*standalone:\s*true"
rg "export.*class.*EmojiComponent.*\{.*standalone:\s*true"
rg "export.*class.*EmojiPickerComponent.*\{.*standalone:\s*true"

# Check if these components are actually defined in the codebase
echo "Checking component definitions..."
rg "class (ReactingUsersOnPostingPipe|EmojiComponent|EmojiPickerComponent)"

# Check imports of ArtemisSharedModule and ArtemisConfirmIconModule
echo "Checking module definitions..."
rg "export class (ArtemisSharedModule|ArtemisConfirmIconModule)"

Length of output: 1302


Script:

#!/bin/bash
# Let's verify if these components are already standalone

echo "Checking component decorators..."
rg -A 5 "@Component\(" src/main/webapp/app/shared/pipes/reacting-users-on-posting.pipe.ts
rg -A 5 "@Component\(" src/main/webapp/app/shared/metis/emoji/emoji-picker.component.ts
rg -A 5 "@Component\(" src/main/webapp/app/shared/metis/emoji/emoji.component.ts

# Check if these modules are NgModules and what they export
echo "Checking module contents..."
rg -A 10 "@NgModule\(" src/main/webapp/app/shared/shared.module.ts
rg -A 10 "@NgModule\(" src/main/webapp/app/shared/confirm-icon/confirm-icon.module.ts

Length of output: 1533

@@ -115,6 +113,7 @@ import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-pict
HtmlForPostingMarkdownPipe,
EmojiComponent,
EmojiPickerComponent,
ReactingUsersOnPostingPipe,
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Redundant export of standalone pipe confirmed

The pipe is correctly marked as standalone and is being directly imported from its source file (app/shared/pipes/reacting-users-on-posting.pipe) by the components that use it. There's no usage through the metis.module, making its export redundant.

  • Remove ReactingUsersOnPostingPipe from the exports array in src/main/webapp/app/shared/metis/metis.module.ts
🔗 Analysis chain

Review necessity of exporting standalone pipe

Since ReactingUsersOnPostingPipe is marked as standalone, it might not need to be exported from the module. Standalone pipes can be imported directly where needed.

Consider removing the pipe from exports if it's not specifically required through the module import.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the pipe is actually used through the module export
rg -l "import.*ReactingUsersOnPostingPipe.*from.*metis\.module" 

Length of output: 65


Script:

#!/bin/bash
# Let's check how the pipe is imported in other files
rg -l "import.*ReactingUsersOnPostingPipe"

# Also check the pipe's implementation to verify if it's standalone
fd -e ts "ReactingUsersOnPostingPipe" --exec cat {}

Length of output: 710


Script:

#!/bin/bash
# Let's check the actual implementation of the pipe and its imports
ast-grep --pattern 'import { ReactingUsersOnPostingPipe } from $_'

# And check the pipe's implementation to verify standalone decorator
ast-grep --pattern '@Pipe({
  $$$
  standalone: true,
  $$$
})'

Length of output: 1835


Script:

#!/bin/bash
# Let's check how the components are importing this pipe
rg "import.*ReactingUsersOnPostingPipe" -A 2 src/main/webapp/app/shared/metis/posting-reactions-bar/

Length of output: 2492

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

🧹 Outside diff range and nitpick comments (20)
src/main/webapp/app/shared/metis/posting-create-edit.directive.ts (2)

Line range hint 13-15: Consider enhancing type safety and documentation.

A few suggestions to improve the code:

  1. Add explicit generic type to EventEmitter
  2. Add JSDoc comments to abstract methods
-@Output() onCreate: EventEmitter<T> = new EventEmitter<T>();
-@Output() isModalOpen = new EventEmitter<void>();
+@Output() onCreate = new EventEmitter<T>();
+@Output() isModalOpen = new EventEmitter<void>();

+/** Resets the form group to its initial state */
 abstract resetFormGroup(): void;

+/** Creates a new posting entity */
 abstract createPosting(): void;

+/** Updates an existing posting entity */
 abstract updatePosting(): void;

Line range hint 10-11: Consider externalizing the max content length constant.

The max content length constant could be moved to a shared configuration file since it needs to match the Java backend.

Consider creating a shared constants file:

// src/main/webapp/app/shared/constants/posting.constants.ts
export const POSTING_CONSTRAINTS = {
    MAX_CONTENT_LENGTH: 5000,
} as const;
src/main/webapp/app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component.ts (3)

1-1: Consider grouping related imports together

While the imports are valid, consider organizing them into logical groups:

  1. Angular core imports
  2. Feature/component imports
  3. Model/interface imports
  4. Utility imports
- import { Component, EventEmitter, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation, inject, input } from '@angular/core';
- import { AnswerPost } from 'app/entities/metis/answer-post.model';
- import { Validators } from '@angular/forms';
- import { PostContentValidationPattern } from 'app/shared/metis/metis.util';
- import { PostingCreateEditDirective } from 'app/shared/metis/posting-create-edit.directive';
- import { LocalStorageService } from 'ngx-webstorage';
- import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model';
- import { PostingButtonComponent } from 'app/shared/metis/posting-button/posting-button.component';
- import { ArtemisSharedModule } from 'app/shared/shared.module';
- import { PostingMarkdownEditorComponent } from 'app/shared/metis/posting-markdown-editor/posting-markdown-editor.component';

+ // Angular imports
+ import { Component, EventEmitter, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation, inject, input } from '@angular/core';
+ import { Validators } from '@angular/forms';
+ 
+ // Third-party imports
+ import { LocalStorageService } from 'ngx-webstorage';
+ 
+ // Feature components
+ import { PostingButtonComponent } from 'app/shared/metis/posting-button/posting-button.component';
+ import { PostingMarkdownEditorComponent } from 'app/shared/metis/posting-markdown-editor/posting-markdown-editor.component';
+ import { ArtemisSharedModule } from 'app/shared/shared.module';
+ 
+ // Models and utilities
+ import { AnswerPost } from 'app/entities/metis/answer-post.model';
+ import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model';
+ import { PostContentValidationPattern } from 'app/shared/metis/metis.util';
+ import { PostingCreateEditDirective } from 'app/shared/metis/posting-create-edit.directive';

Also applies to: 8-10


Line range hint 36-47: Improve type safety in ngOnChanges

The current implementation uses string indexing which could be unsafe. Consider using type-safe alternatives.

  ngOnChanges(changes: SimpleChanges | void) {
    if (this.formGroup && changes) {
-     for (const propName in changes) {
-       if (changes.hasOwnProperty(propName) && propName === 'posting') {
-         if (changes['posting'].previousValue?.post?.id === changes['posting'].currentValue?.post?.id) {
+     if ('posting' in changes) {
+       const postingChange = changes['posting'];
+       if (postingChange.previousValue?.post?.id === postingChange.currentValue?.post?.id) {
            this.posting.content = this.formGroup.get('content')?.value;
-         }
        }
      }
    }
    super.ngOnChanges();
  }

Line range hint 71-84: Enhance error handling in createPosting

The error handling could be improved by providing more specific error feedback to users.

  createPosting(): void {
    this.posting.content = this.formGroup.get('content')?.value;
    this.metisService.createAnswerPost(this.posting).subscribe({
      next: (answerPost: AnswerPost) => {
        this.resetFormGroup('');
        this.isLoading = false;
        this.onCreate.emit(answerPost);
      },
-     error: () => {
+     error: (error) => {
        this.isLoading = false;
+       // Handle specific error cases
+       const errorMessage = error.status === 403 ? 'Permission denied' : 'Failed to create post';
+       // Emit error to parent or handle locally
+       this.onError.emit(errorMessage);
      },
    });
  }
src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts (2)

Line range hint 28-33: Prevent potential memory leaks in ViewContainerRef usage

While the ViewContainerRef usage is correct, ensure proper cleanup by implementing OnDestroy.

Add the following implementation:

- export class AnswerPostCreateEditModalComponent extends PostingCreateEditModalDirective<AnswerPost> {
+ export class AnswerPostCreateEditModalComponent extends PostingCreateEditModalDirective<AnswerPost> implements OnDestroy {
+   ngOnDestroy(): void {
+     this.close();
+   }

Also, add OnDestroy to the imports:

- import { Component, EventEmitter, Input, Output, ViewContainerRef, ViewEncapsulation } from '@angular/core';
+ import { Component, EventEmitter, Input, Output, ViewContainerRef, ViewEncapsulation, OnDestroy } from '@angular/core';

Also applies to: 37-41


Line range hint 52-67: Enhance error handling with user feedback

The error handling in createPosting and updatePosting methods only resets the loading state without informing the user about the failure.

Consider adding error notifications:

 error: () => {
     this.isLoading = false;
+    // Use your application's notification service
+    this.notificationService.showError('artemisApp.metis.error.saveFailed');
 },

Don't forget to inject the notification service in the constructor.

Also applies to: 74-89

src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts (5)

1-19: LGTM! Component is properly configured as standalone

The component configuration follows Angular's standalone component pattern, enabling better lazy loading capabilities. The imports are correctly specified in the component decorator.

Consider organizing imports by grouping them:

  1. Angular core
  2. Angular material
  3. Third-party libraries
  4. Application imports

Line range hint 21-38: Add explicit type declarations for better type safety

While the code follows naming conventions, some properties could benefit from explicit type declarations.

Consider applying these type improvements:

-    existingPostTags: Observable<string[]>;
-    tags: string[];
+    existingPostTags!: Observable<string[]>;
+    tags: string[] = [];

-    tagInput: ElementRef<HTMLInputElement>;
+    tagInput!: ElementRef<HTMLInputElement>;

Line range hint 46-54: Consider protecting against potential race conditions

The subscription setup in ngOnInit could be vulnerable to race conditions if metisService.tags emits before the component is fully initialized.

Consider using combineLatest or ensuring proper initialization:

     ngOnInit(): void {
-        this.tagsSubscription = this.metisService.tags.subscribe((tags: string[]) => {
+        this.tagsSubscription = this.metisService.tags.pipe(
+            startWith([])
+        ).subscribe((tags: string[]) => {
             this.existingPostTags = this.tagCtrl.valueChanges.pipe(
                 startWith(undefined),
                 map((category: string | undefined) => (category ? this._filter(category) : tags.slice())),
             );
         });
         this.tags = this.postTags ? this.postTags : [];
     }

Line range hint 82-95: Extract magic number to a constant

The tag limit of 3 is hardcoded in multiple places. This should be extracted to a constant for better maintainability.

Consider adding a constant:

+    private static readonly MAX_TAGS = 3;
     onItemAdd(event: MatChipInputEvent) {
         const tagString = (event.value || '').trim();
         // also test for duplicated tag
-        if (tagString && !this.tags?.includes(tagString) && this.tags?.length < 3) {
+        if (tagString && !this.tags?.includes(tagString) && this.tags?.length < PostTagSelectorComponent.MAX_TAGS) {

Line range hint 114-116: Enhance subscription cleanup

While the component does unsubscribe in ngOnDestroy, it's good practice to use a cleanup subject for all observables.

Consider using a destroy subject:

+    private readonly destroy$ = new Subject<void>();

     ngOnInit(): void {
-        this.tagsSubscription = this.metisService.tags.subscribe(...);
+        this.tagsSubscription = this.metisService.tags.pipe(
+            takeUntil(this.destroy$)
+        ).subscribe(...);
     }

     ngOnDestroy(): void {
-        this.tagsSubscription.unsubscribe();
+        this.destroy$.next();
+        this.destroy$.complete();
     }
src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts (1)

Line range hint 108-124: Consider extracting scroll manipulation logic to a service

The disableBodyScroll and enableBodyScroll methods manipulate DOM directly. Consider extracting this logic into a shared service for better reusability and maintainability.

// Suggested service: ScrollManipulationService
@Injectable({ providedIn: 'root' })
export class ScrollManipulationService {
  constructor(
    private renderer: Renderer2,
    @Inject(DOCUMENT) private document: Document
  ) {}

  disableBodyScroll(selector: string) {
    const container = this.document.querySelector(selector);
    if (container) {
      this.renderer.setStyle(container, 'overflow', 'hidden');
    }
  }

  enableBodyScroll(selector: string) {
    const container = this.document.querySelector(selector);
    if (container) {
      this.renderer.setStyle(container, 'overflow-y', 'auto');
    }
  }
}
src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (2)

36-37: Consider optimizing imports for better lazy loading

While the standalone component setup is correct, importing the entire ArtemisSharedModule might impact lazy loading. Consider importing only the specific components and directives needed from the shared module.

Example approach:

-imports: [AnswerPostCreateEditModalComponent, AnswerPostComponent, ArtemisSharedModule],
+imports: [
+    AnswerPostCreateEditModalComponent,
+    AnswerPostComponent,
+    // Import specific items from ArtemisSharedModule instead of the entire module
+    // Example: CommonModule, FormsModule, specific pipes or directives
+],

Line range hint 108-146: Improve type safety and change detection in groupAnswerPosts method

The current implementation has two areas for improvement:

  1. Uses 'any' type for date manipulation
  2. Contains multiple manual change detection calls

Consider this type-safe implementation:

+interface PostWithDate extends AnswerPost {
+    creationDateDayjs?: dayjs.Dayjs;
+}

 groupAnswerPosts(): void {
     if (!this.sortedAnswerPosts || this.sortedAnswerPosts.length === 0) {
         this.groupedAnswerPosts = [];
         return;
     }

-    this.sortedAnswerPosts = this.sortedAnswerPosts
+    const typedPosts: PostWithDate[] = this.sortedAnswerPosts
         .slice()
         .reverse()
         .map((post) => ({
+            ...post,
             creationDateDayjs: post.creationDate ? dayjs(post.creationDate) : undefined,
-            return post;
         }));

-    const sortedPosts = this.sortedAnswerPosts.sort((a, b) => {
-        const aDate = (a as any).creationDateDayjs;
-        const bDate = (b as any).creationDateDayjs;
+    const sortedPosts = typedPosts.sort((a, b) => {
+        const aDate = a.creationDateDayjs;
+        const bDate = b.creationDateDayjs;
         return aDate?.valueOf() - bDate?.valueOf();
     });

     // ... rest of the method ...
-    this.changeDetector.detectChanges();  // Remove this call as it's handled in ngOnChanges
src/main/webapp/app/shared/metis/posting-reactions-bar/posting-reactions-bar.directive.ts (4)

Line range hint 52-54: Standardize output property declarations.

There's an inconsistency in how output properties are declared:

  • Some use the new output<T>() function
  • Others use the traditional @Output() = new EventEmitter<T>()

For consistency and maintainability, consider standardizing all output declarations to use the new output<T>() function.

Apply this diff to standardize the output declarations:

-@Output() openPostingCreateEditModal = new EventEmitter<void>();
-@Output() reactionsUpdated = new EventEmitter<Reaction[]>();
-@Output() isModalOpen = new EventEmitter<void>();
+readonly openPostingCreateEditModal = output<void>();
+readonly reactionsUpdated = output<Reaction[]>();
+readonly isModalOpen = output<void>();

Also applies to: 64-65


Line range hint 156-170: Prevent potential memory leaks in reaction handling.

The subscription to metisService's createReaction and deleteReaction should be properly managed to prevent memory leaks.

Consider using the following pattern:

+private destroy$ = new Subject<void>();

 addOrRemoveReaction(emojiId: string): void {
     if (this.posting.reactions && existingReactionIdx > -1) {
         const reactionToDelete = this.posting.reactions[existingReactionIdx];
-        this.metisService.deleteReaction(reactionToDelete).subscribe(() => {
+        this.metisService.deleteReaction(reactionToDelete).pipe(
+            takeUntil(this.destroy$)
+        ).subscribe(() => {
             // ... existing code
         });
     } else {
         const reactionToCreate = this.buildReaction(emojiId);
-        this.metisService.createReaction(reactionToCreate).subscribe(() => {
+        this.metisService.createReaction(reactionToCreate).pipe(
+            takeUntil(this.destroy$)
+        ).subscribe(() => {
             // ... existing code
         });
     }
 }

+ngOnDestroy(): void {
+    this.destroy$.next();
+    this.destroy$.complete();
+}

Line range hint 191-204: Improve type safety in buildReactionMetaDataMap.

The method uses non-null assertions (!), which could be replaced with proper null checks for better type safety.

Consider this safer implementation:

 buildReactionMetaDataMap(reactions: Reaction[]): ReactionMetaDataMap {
     return reactions.reduce((metaDataMap: ReactionMetaDataMap, reaction: Reaction) => {
         const hasReacted = reaction.user?.id === this.metisService.getUser().id;
-        const reactingUser = hasReacted ? PLACEHOLDER_USER_REACTED : reaction.user?.name!;
+        const reactingUser = hasReacted ? PLACEHOLDER_USER_REACTED : reaction.user?.name ?? 'Unknown User';
+        const emojiId = reaction.emojiId ?? '';
+        if (!emojiId) return metaDataMap;
         
         const reactionMetaData: ReactionMetaData = {
-            count: metaDataMap[reaction.emojiId!] ? metaDataMap[reaction.emojiId!].count + 1 : 1,
-            hasReacted: metaDataMap[reaction.emojiId!] ? metaDataMap[reaction.emojiId!].hasReacted || hasReacted : hasReacted,
-            reactingUsers: metaDataMap[reaction.emojiId!] ? metaDataMap[reaction.emojiId!].reactingUsers.concat(reactingUser) : [reactingUser],
+            count: metaDataMap[emojiId]?.count ?? 0 + 1,
+            hasReacted: metaDataMap[emojiId]?.hasReacted || hasReacted,
+            reactingUsers: metaDataMap[emojiId]?.reactingUsers.concat(reactingUser) ?? [reactingUser],
         };
-        return { ...metaDataMap, [reaction.emojiId!]: reactionMetaData };
+        return { ...metaDataMap, [emojiId]: reactionMetaData };
     }, {});
 }

Line range hint 37-211: Consider breaking down the directive into smaller, focused components.

The directive has multiple responsibilities:

  1. Reaction management
  2. Emoji selection
  3. Bookmark handling
  4. UI state management

This could make it harder to maintain and test. Consider splitting these responsibilities into separate services or components.

Suggested structure:

  • ReactionService: Handle reaction CRUD operations
  • EmojiSelectorComponent: Handle emoji selection UI and logic
  • BookmarkService: Handle bookmark operations
  • PostingReactionsStateService: Manage UI state

Would you like me to help create a detailed implementation plan for this refactoring?

src/main/webapp/app/shared/metis/post/post.component.ts (1)

Line range hint 1-307: Consider breaking down the component for better maintainability

The component handles multiple responsibilities including:

  • Post management
  • Answer post handling
  • Dropdown positioning
  • Scroll management
  • Navigation
  • User interactions

Consider:

  1. Extracting dropdown and scroll management logic into a separate service
  2. Creating smaller, focused sub-components for specific features
  3. Moving navigation logic to a dedicated service

This would improve:

  • Code maintainability
  • Testing capabilities
  • Reusability
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7276758 and 647793b.

📒 Files selected for processing (19)
  • src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts (2 hunks)
  • src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts (4 hunks)
  • src/main/webapp/app/shared/metis/message/message-inline-input/message-inline-input.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component.ts (2 hunks)
  • src/main/webapp/app/shared/metis/post/post.component.ts (4 hunks)
  • src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.ts (2 hunks)
  • src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (3 hunks)
  • src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts (3 hunks)
  • src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (4 hunks)
  • src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts (2 hunks)
  • src/main/webapp/app/shared/metis/posting-create-edit-modal/posting-create-edit-modal.directive.ts (0 hunks)
  • src/main/webapp/app/shared/metis/posting-create-edit.directive.ts (2 hunks)
  • src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (4 hunks)
  • src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts (3 hunks)
  • src/main/webapp/app/shared/metis/posting-reactions-bar/posting-reactions-bar.directive.ts (2 hunks)
  • src/main/webapp/app/shared/pipes/html-for-posting-markdown.pipe.ts (1 hunks)
  • src/main/webapp/app/shared/pipes/reacting-users-on-posting.pipe.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • src/main/webapp/app/shared/metis/posting-create-edit-modal/posting-create-edit-modal.directive.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/main/webapp/app/shared/link-preview/components/link-preview/link-preview.component.ts
  • src/main/webapp/app/shared/metis/posting-content/enlarge-slide-image/enlarge-slide-image.component.ts
  • src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts
  • src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts
  • src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts
  • src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts
  • src/main/webapp/app/shared/pipes/html-for-posting-markdown.pipe.ts
  • src/main/webapp/app/shared/pipes/reacting-users-on-posting.pipe.ts
🧰 Additional context used
📓 Path-based instructions (10)
src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/message/message-inline-input/message-inline-input.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/post/post.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-create-edit.directive.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-reactions-bar/posting-reactions-bar.directive.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

🔇 Additional comments (20)
src/main/webapp/app/shared/metis/posting-create-edit.directive.ts (2)

1-1: LGTM! Import changes align with dependency injection updates.

The addition of inject to the imports is consistent with Angular's modern dependency injection pattern.


27-29: LGTM! Modern dependency injection pattern implemented correctly.

The transition to inject() function follows Angular's recommended patterns and reduces boilerplate code while maintaining proper access levels for inheritance.

src/main/webapp/app/shared/metis/message/message-inline-input/message-inline-input.component.ts (3)

23-23: LGTM: Modern dependency injection pattern

The use of inject() function for LocalStorageService is a good practice in modern Angular, making the code more concise while maintaining proper dependency injection.


8-10: 🛠️ Refactor suggestion

Optimize imports for standalone component

The ArtemisSharedModule import is redundant since the component is now standalone and already imports the specific components it needs (PostingButtonComponent and PostingMarkdownEditorComponent).

Remove the shared module import:

-import { ArtemisSharedModule } from 'app/shared/shared.module';

And update the imports array in the @component decorator:

-    imports: [ArtemisSharedModule, PostingButtonComponent, PostingMarkdownEditorComponent],
+    imports: [PostingButtonComponent, PostingMarkdownEditorComponent],

17-18: 🛠️ Refactor suggestion

Update component encapsulation for better style isolation

The component uses ViewEncapsulation.None which can lead to style leaks. Since it has a dedicated SCSS file, it should use the default encapsulation mode.

Update the component configuration:

@Component({
    selector: 'jhi-message-inline-input',
    templateUrl: './message-inline-input.component.html',
    styleUrls: ['./message-inline-input.component.scss'],
-    encapsulation: ViewEncapsulation.None,
    standalone: true,
    imports: [PostingButtonComponent, PostingMarkdownEditorComponent],
})
src/main/webapp/app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component.ts (2)

17-18: Review ViewEncapsulation.None usage

The component is configured with ViewEncapsulation.None which means styles will leak into other components. Consider if this is intentionally needed or if Emulated encapsulation would be more appropriate.

✅ Verification successful

ViewEncapsulation.None is justified in this case

The use of ViewEncapsulation.None appears to be intentional and necessary here because:

  1. Both components need to style child elements from the markdown editor (.markdown-editor-wrapper and .markdown-editor)
  2. The components share identical styling needs for these nested elements
  3. The styles use !important flags to override markdown editor's default styles
  4. Both components are inline input components that need consistent styling across the application

These styling requirements would be difficult to achieve with Emulated encapsulation since it would prevent styling of nested component elements.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if other components in the same directory use ViewEncapsulation.None
rg -l "ViewEncapsulation.None" "src/main/webapp/app/shared/metis/message/"

Length of output: 276


Script:

#!/bin/bash
# Check the content of both files to understand the context and styling needs
rg -A 5 "ViewEncapsulation.None" "src/main/webapp/app/shared/metis/message/"

# Also check for any shared styles or CSS that might explain the need for None
fd -e scss -e css . "src/main/webapp/app/shared/metis/message/"

Length of output: 2192


Script:

#!/bin/bash
# Let's check the content of the SCSS files to understand styling requirements
cat src/main/webapp/app/shared/metis/message/message-inline-input/message-inline-input.component.scss src/main/webapp/app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component.scss

# Also check if these components use any shared components that might require style penetration
ast-grep --pattern 'extends PostingCreateEditDirective'

Length of output: 1075


Line range hint 23-28: Verify parent class dependencies

The component extends PostingCreateEditDirective but only injects LocalStorageService. Ensure all required dependencies from the parent class are properly handled.

✅ Verification successful

Parent class dependencies are properly handled

The parent class PostingCreateEditDirective uses inject() for its dependencies (MetisService, NgbModal, and FormBuilder), and these services are automatically available to child classes without needing to re-inject them. The LocalStorageService injected in the child component is an additional dependency specific to that component. Everything is correctly implemented.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check parent class constructor requirements
ast-grep --pattern 'class PostingCreateEditDirective {
  constructor($$$) {
    $$$
  }
}'

Length of output: 216


Script:

#!/bin/bash
# Let's try to find the PostingCreateEditDirective file first
fd PostingCreateEditDirective

# Then let's search for any constructor or dependency injection patterns
rg -l "PostingCreateEditDirective" 

# As a backup, let's search for class definition pattern
rg "class.*PostingCreateEditDirective"

Length of output: 1323


Script:

#!/bin/bash
# Let's examine the content of the parent directive to see its dependencies
rg -A 10 "export abstract class PostingCreateEditDirective" src/main/webapp/app/shared/metis/posting-create-edit.directive.ts

Length of output: 560


Script:

#!/bin/bash
# Let's check if there are any constructor dependencies or inject() calls in the parent class
rg -B 5 -A 10 "constructor|inject\(" src/main/webapp/app/shared/metis/posting-create-edit.directive.ts

Length of output: 687

src/main/webapp/app/shared/metis/posting-create-edit-modal/answer-post-create-edit-modal/answer-post-create-edit-modal.component.ts (1)

4-9: LGTM: Imports are properly organized

The new imports support the standalone component migration and follow Angular style guidelines.

src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts (1)

39-40: LGTM! Modern dependency injection pattern used

The use of inject() function follows current Angular best practices for dependency injection.

src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts (4)

14-15: LGTM: Proper usage of new Angular features

The addition of inject and input imports aligns with Angular's new dependency injection approach and signals pattern.


27-31: LGTM: Component imports are well-organized

The new component imports follow Angular's style guide and support the standalone architecture.


80-82: LGTM: Proper usage of dependency injection

The migration to inject() function follows Angular's recommended patterns for standalone components. The access modifiers are appropriately set for each dependency.


26-26: ⚠️ Potential issue

Fix the import path for PostingContentComponent

The import statement has a typo in the filename extension.

src/main/webapp/app/shared/metis/posting-footer/post-footer/post-footer.component.ts (1)

14-14: LGTM: Import changes align with standalone component migration

The new imports support the transition to standalone components and follow Angular's style guide.

Also applies to: 24-25

src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (3)

1-1: LGTM: Import statements are properly organized

The imports are well-structured and include all necessary dependencies for the standalone component configuration.

Also applies to: 12-18


Line range hint 41-185: LGTM: Well-structured implementation with proper error handling

The component implementation demonstrates good practices:

  • Proper form validation with custom patterns
  • Clean error handling in async operations
  • Modal cleanup on dismissal
  • Clear separation of concerns between create and update operations

30-39: Verify standalone component dependencies

The standalone component configuration looks good. All necessary dependencies are properly imported.

Let's verify that all imported components are also standalone:

✅ Verification successful

Standalone component configuration is correctly implemented

Based on the verification results, all components imported in the PostCreateEditModalComponent are properly configured as standalone components:

  • PostingButtonComponent is standalone with FontAwesomeModule imports
  • PostTagSelectorComponent is standalone with necessary Material and shared module imports
  • PostingContentComponent and its part component are both standalone
  • ArtemisSharedModule and ArtemisSharedComponentModule are properly declared as modules
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if all imported components are configured as standalone

# Search for the component declarations
ast-grep --pattern 'Component({
  $$$
  standalone: true,
  $$$
})
export class $name' src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts src/main/webapp/app/shared/metis/post/post.component.ts src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts

Length of output: 524


Script:

#!/bin/bash
# Let's try a different approach using ripgrep to find component declarations
# and check their standalone configuration

# Search for PostingMarkdownEditorComponent
echo "Checking PostingMarkdownEditorComponent:"
rg -A 5 "@Component\(" src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts

echo -e "\nChecking PostComponent:"
rg -A 5 "@Component\(" src/main/webapp/app/shared/metis/post/post.component.ts

echo -e "\nChecking PostingContentComponent:"
rg -A 5 "@Component\(" src/main/webapp/app/shared/metis/posting-content/posting-content.component.ts

echo -e "\nChecking PostingButtonComponent:"
rg -A 5 "@Component\(" src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts

echo -e "\nChecking PostTagSelectorComponent:"
rg -A 5 "@Component\(" src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-tag-selector/post-tag-selector.component.ts

Length of output: 2038


Script:

#!/bin/bash
# Let's check for PostingContentComponent in a different location and verify ArtemisSharedModule
# and ArtemisSharedComponentModule declarations

echo "Checking PostingContentComponent (alternative path):"
rg -A 5 "@Component\(" src/main/webapp/app/shared/metis/posting-content.component.ts

echo -e "\nChecking for PostingContentComponent anywhere in the codebase:"
fd -e ts -e component.ts "posting-content" --exec rg -A 5 "@Component\(" {}

echo -e "\nChecking shared module declarations:"
rg "export class ArtemisSharedModule" -A 10 src/main/webapp/app/shared
rg "export class ArtemisSharedComponentModule" -A 10 src/main/webapp/app/shared

Length of output: 1447

src/main/webapp/app/shared/metis/posting-reactions-bar/posting-reactions-bar.directive.ts (2)

1-1: LGTM! Import changes align with modern Angular practices.

The addition of inject and output imports supports the migration to standalone components and follows Angular's recommended dependency injection patterns.


69-69: LGTM! Modern dependency injection pattern.

The use of inject() function for MetisService follows Angular's modern dependency injection pattern, improving code maintainability.

src/main/webapp/app/shared/metis/post/post.component.ts (1)

52-63: LGTM: Well-structured standalone component configuration

The standalone component configuration is well-organized and includes all necessary dependencies. This migration aligns with Angular's best practices for standalone components.

Comment on lines +27 to +29
protected metisService = inject(MetisService);
protected modalService = inject(NgbModal);
protected formBuilder = inject(FormBuilder);
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Add ngOnDestroy lifecycle hook to clean up modal reference

The directive contains a modalRef property but lacks proper cleanup in ngOnDestroy. To prevent potential memory leaks, implement the OnDestroy interface and close the modal in the cleanup hook:

ngOnDestroy(): void {
    this.modalRef?.close();
}
🔗 Analysis chain

Verify modal service cleanup.

Since the directive injects NgbModal and maintains a modalRef, ensure proper cleanup is implemented to prevent memory leaks.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for modal cleanup patterns in the codebase
rg -A 5 "modalRef\?\.close\(\)" 
rg -A 5 "ngOnDestroy.*modalRef" 

Length of output: 4766


Script:

#!/bin/bash
# Let's check the specific directive file for any cleanup implementation
rg -A 10 "ngOnDestroy|OnDestroy" "src/main/webapp/app/shared/metis/posting-create-edit.directive.ts"

# Also check if there's any other cleanup pattern in this file
rg "modalRef" "src/main/webapp/app/shared/metis/posting-create-edit.directive.ts"

Length of output: 208

Comment on lines 19 to 24
export class AnswerPostCreateEditModalComponent extends PostingCreateEditModalDirective<AnswerPost> {
@Input() createEditAnswerPostContainerRef: ViewContainerRef;
isInputOpen = false;
@Output() postingUpdated = new EventEmitter<Posting>();

constructor(
protected metisService: MetisService,
protected modalService: NgbModal,
protected formBuilder: FormBuilder,
) {
super(metisService, modalService, formBuilder);
}

/**
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 constructor for dependency injection

The component references metisService and formBuilder but lacks proper dependency injection. This could cause runtime errors.

Add the following constructor:

+ constructor(
+   protected metisService: MetisService,
+   protected formBuilder: FormBuilder
+ ) {
+   super();
+ }

Also, add the missing import:

import { MetisService } from 'app/shared/metis/metis.service';

Comment on lines +16 to +17
standalone: true,
imports: [ArtemisSharedModule, PostingMarkdownEditorComponent, PostingButtonComponent],
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Migration to standalone components incomplete

The verification reveals that:

  • PostingButtonComponent is properly configured as standalone
  • PostingMarkdownEditorComponent is not marked as standalone yet

This needs to be addressed as all components used in a standalone component's imports must also be standalone.

• src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts: Add standalone configuration to make it compatible with the parent component.

🔗 Analysis chain

Verify standalone status of imported components

The component is correctly marked as standalone. Let's verify that the imported components are also standalone to ensure compatibility.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if imported components are marked as standalone

# Check PostingMarkdownEditorComponent
ast-grep --pattern 'class PostingMarkdownEditorComponent {
  $$$
}' -f 'src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts'

# Check PostingButtonComponent
ast-grep --pattern 'class PostingButtonComponent {
  $$$
}' -f 'src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts'

Length of output: 663


Script:

#!/bin/bash
# Let's try a different approach to find and check the component files

# First, find the exact paths of the components
fd "posting-markdown-editor.component.ts|posting-button.component.ts" --type f

# Then check their content for standalone configuration
echo "=== PostingMarkdownEditorComponent ==="
rg "@Component.*standalone" -A 2 "posting-markdown-editor.component.ts"

echo "=== PostingButtonComponent ==="
rg "@Component.*standalone" -A 2 "posting-button.component.ts"

Length of output: 688


Script:

#!/bin/bash
# Now using the full paths we found

echo "=== PostingMarkdownEditorComponent ==="
rg "@Component" -A 5 "src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts"

echo "=== PostingButtonComponent ==="
rg "@Component" -A 5 "src/main/webapp/app/shared/metis/posting-button/posting-button.component.ts"

Length of output: 791

Comment on lines +44 to +53
standalone: true,
imports: [
AnswerPostReactionsBarComponent,
PostingContentComponent,
AnswerPostHeaderComponent,
AnswerPostCreateEditModalComponent,
ArtemisSharedModule,
OverlayModule,
EmojiPickerComponent,
],
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

ArtemisSharedModule import can be replaced with specific imports

Based on the template analysis, the component only uses basic Angular directives (ngClass, ngStyle, ngIf) and the jhiTranslate directive. These can be imported directly from their respective modules:

  • Replace ArtemisSharedModule with:
    • CommonModule from @angular/common for ngClass, ngStyle, ngIf
    • TranslateModule from @ngx-translate/core for jhiTranslate
🔗 Analysis chain

Verify the necessity of ArtemisSharedModule import

While the standalone configuration looks good, importing ArtemisSharedModule might go against the standalone component migration objective. Consider extracting only the needed dependencies from this module.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check what dependencies from ArtemisSharedModule are actually used in this component
ast-grep --pattern 'import { ArtemisSharedModule } from "app/shared/shared.module"'

# Find template usage
rg -l "ArtemisSharedModule" "src/main/webapp/app/shared/metis/answer-post/answer-post.component.html"

Length of output: 185


Script:

#!/bin/bash
# Let's first check the content of the component file to understand its dependencies
cat "src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts"

# Let's also check the template file to see what features are being used
cat "src/main/webapp/app/shared/metis/answer-post/answer-post.component.html"

# Let's examine what ArtemisSharedModule exports
rg -A 10 "export class ArtemisSharedModule" 

Length of output: 11634


Script:

#!/bin/bash
# Let's check what directives and pipes are being used in the template
rg "ng[A-Z]\w+" "src/main/webapp/app/shared/metis/answer-post/answer-post.component.html"

# Let's also check what the shared module imports and exports
cat "src/main/webapp/app/shared/shared.module.ts"

Length of output: 6118

Comment on lines +62 to +63
private metisService = inject(MetisService);
private changeDetector = inject(ChangeDetectorRef);
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 implementing proper cleanup for change detection

While the migration to inject() is good, ensure proper cleanup of change detection to prevent memory leaks. Consider using takeUntil with a destruction subject.

Example implementation:

+private readonly destroy$ = new Subject<void>();

 ngOnInit(): void {
     this.courseId = this.metisService.getCourse().id!;
     this.isAtLeastTutorInCourse = this.metisService.metisUserIsAtLeastTutorInCourse();
     this.createdAnswerPost = this.createEmptyAnswerPost();
     this.groupAnswerPosts();
+    // If you have any subscriptions, use takeUntil
+    // someObservable$.pipe(takeUntil(this.destroy$)).subscribe();
 }

 ngOnDestroy(): void {
+    this.destroy$.next();
+    this.destroy$.complete();
     this.answerPostCreateEditModal?.createEditAnswerPostContainerRef?.clear();
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +14 to 15
inject,
input,
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 incorrect Angular import

The input import on line 15 appears to be incorrect. Angular's decorator should be capitalized as Input.

- import { inject, input } from '@angular/core';
+ import { inject, Input } from '@angular/core';
📝 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
inject,
input,
inject,
Input,

Comment on lines +112 to +116
private oneToOneChatService = inject(OneToOneChatService);
private metisConversationService = inject(MetisConversationService);
private router = inject(Router);
public renderer = inject(Renderer2);
private document = inject(DOCUMENT);
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 subscription memory leak found

The component has unmanaged subscriptions in onUserReferenceClicked() method that are not being cleaned up:

  • this.metisConversationService.createOneToOneChat(referencedUserLogin).subscribe()
  • this.oneToOneChatService.create(course.id!, referencedUserLogin).subscribe()

Issues:

  • Component doesn't implement OnDestroy
  • No subscription cleanup mechanism present
  • Fire-and-forget subscriptions without proper error handling
🔗 Analysis chain

LGTM: Modern dependency injection pattern implemented

The migration to using inject() function follows modern Angular practices and improves code readability.

Let's verify proper subscription handling throughout the component:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for subscription patterns that might cause memory leaks
ast-grep --pattern 'subscribe($$$)' 

Length of output: 161


Script:

#!/bin/bash
# Let's try a more specific search for subscription patterns in the component file
rg -U "subscribe\(" src/main/webapp/app/shared/metis/post/post.component.ts -A 2

# Also search for OnDestroy implementation and unsubscribe patterns
rg "implements.*OnDestroy" src/main/webapp/app/shared/metis/post/post.component.ts
rg "unsubscribe" src/main/webapp/app/shared/metis/post/post.component.ts

# Check the component's complete content to analyze subscription handling
cat src/main/webapp/app/shared/metis/post/post.component.ts

Length of output: 12438

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: 1

🧹 Outside diff range and nitpick comments (4)
src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (3)

Line range hint 119-134: Enhance error handling in async operations

The error handling in createPosting/updatePosting only resets the loading state but doesn't inform the user about the failure.

Consider adding error notification:

 createPosting(): void {
     this.posting = this.setPostProperties(this.posting);
     this.metisService.createPost(this.posting).subscribe({
         next: (post: Post) => {
             this.isLoading = false;
             this.modalRef?.close();
             this.onCreate.emit(post);
         },
         error: () => {
             this.isLoading = false;
+            // Consider using your application's notification service
+            this.notificationService.error('artemisApp.metis.error.createPost');
         },
     });
 }

Line range hint 156-173: Avoid mutating input parameters

The setPostProperties method mutates its input parameter which can lead to unexpected side effects.

Consider returning a new object instead:

-private setPostProperties(post: Post): Post {
-    post.title = this.formGroup.get('title')?.value;
-    post.tags = this.tags;
-    post.content = this.formGroup.get('content')?.value;
+private setPostProperties(post: Post): Post {
+    return {
+        ...post,
+        title: this.formGroup.get('title')?.value,
+        tags: [...this.tags],
+        content: this.formGroup.get('content')?.value,
+        conversation: this.formGroup.get('context')?.value?.conversation,
+    };
-    const currentContextSelectorOption: ContextSelectorOption = {
-        ...this.formGroup.get('context')?.value,
-    };
-    post = {
-        ...post,
-        ...currentContextSelectorOption,
-    };
-    if (currentContextSelectorOption.conversation) {
-        post.conversation = currentContextSelectorOption.conversation;
-    }
-    return post;
}

Line range hint 98-104: Prevent potential memory leaks

The subscription to formGroup.controls['context'].valueChanges is not being unsubscribed.

Implement proper cleanup:

+private destroy$ = new Subject<void>();

 resetFormGroup(): void {
     this.pageType = this.metisService.getPageType();
     this.tags = this.posting?.tags ?? [];
     this.similarPosts = [];
     this.posting.title = this.posting.title ?? '';
     this.resetCurrentContextSelectorOption();
     this.formGroup = this.formBuilder.group(this.postValidator());
-    this.formGroup.controls['context'].valueChanges.subscribe((context: ContextSelectorOption) => {
+    this.formGroup.controls['context'].valueChanges.pipe(
+        takeUntil(this.destroy$)
+    ).subscribe((context: ContextSelectorOption) => {
         this.currentContextSelectorOption = context;
         this.similarPosts = [];
     });
 }

+ngOnDestroy(): void {
+    this.destroy$.next();
+    this.destroy$.complete();
+}
src/main/webapp/app/shared/metis/post/post.component.ts (1)

Line range hint 246-267: Add error handling to HTTP subscriptions

The HTTP subscriptions lack error handling, which could lead to silent failures and poor user experience.

 onUserReferenceClicked(referencedUserLogin: string) {
     const course = this.metisService.getCourse();
     if (isMessagingEnabled(course)) {
         if (this.isCommunicationPage) {
             this.subscriptions.add(
-                this.metisConversationService.createOneToOneChat(referencedUserLogin).subscribe()
+                this.metisConversationService.createOneToOneChat(referencedUserLogin).subscribe({
+                    error: (error) => {
+                        console.error('Failed to create one-to-one chat:', error);
+                        // TODO: Add user notification service to show error message
+                    }
+                })
             );
         } else {
             this.subscriptions.add(
-                this.oneToOneChatService.create(course.id!, referencedUserLogin).subscribe((res) => {
-                    this.router.navigate(['courses', course.id, 'communication'], {
-                        queryParams: {
-                            conversationId: res.body!.id,
-                        },
-                    });
-                })
+                this.oneToOneChatService.create(course.id!, referencedUserLogin).subscribe({
+                    next: (res) => {
+                        this.router.navigate(['courses', course.id, 'communication'], {
+                            queryParams: {
+                                conversationId: res.body!.id,
+                            },
+                        });
+                    },
+                    error: (error) => {
+                        console.error('Failed to create one-to-one chat:', error);
+                        // TODO: Add user notification service to show error message
+                    }
+                })
             );
         }
     }
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 647793b and 9a2e63e.

📒 Files selected for processing (2)
  • src/main/webapp/app/shared/metis/post/post.component.ts (4 hunks)
  • src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/main/webapp/app/shared/metis/post/post.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

🔇 Additional comments (5)
src/main/webapp/app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component.ts (1)

1-1: LGTM: Component successfully migrated to standalone

The component has been properly configured as standalone with all necessary dependencies. The imports follow Angular's style guide.

Also applies to: 12-18, 31-40

src/main/webapp/app/shared/metis/post/post.component.ts (4)

53-64: LGTM: Proper standalone component configuration

The standalone component configuration and imports are well-organized and follow Angular's best practices.


Line range hint 246-267: Implement proper subscription management

The onUserReferenceClicked method contains unmanaged subscriptions that could lead to memory leaks.

  1. Implement OnDestroy
  2. Store and cleanup subscriptions
-export class PostComponent extends PostingDirective<Post> implements OnInit, OnChanges, AfterContentChecked {
+export class PostComponent extends PostingDirective<Post> implements OnInit, OnChanges, AfterContentChecked, OnDestroy {
+    private subscriptions = new Subscription();
+
+    ngOnDestroy() {
+        this.subscriptions.unsubscribe();
+    }

     onUserReferenceClicked(referencedUserLogin: string) {
         const course = this.metisService.getCourse();
         if (isMessagingEnabled(course)) {
             if (this.isCommunicationPage) {
-                this.metisConversationService.createOneToOneChat(referencedUserLogin).subscribe();
+                this.subscriptions.add(
+                    this.metisConversationService.createOneToOneChat(referencedUserLogin).subscribe()
+                );
             } else {
-                this.oneToOneChatService.create(course.id!, referencedUserLogin).subscribe((res) => {
+                this.subscriptions.add(
+                    this.oneToOneChatService.create(course.id!, referencedUserLogin).subscribe((res) => {
                     this.router.navigate(['courses', course.id, 'communication'], {
                         queryParams: {
                             conversationId: res.body!.id,
                         },
                     });
-                });
+                    })
+                );
             }
         }
     }

14-15: ⚠️ Potential issue

Fix incorrect Angular import casing

The input import should be capitalized as Input to match Angular's naming convention.

- import { inject, input } from '@angular/core';
+ import { inject, Input } from '@angular/core';

113-117: LGTM: Modern dependency injection pattern implemented

The migration to using inject() function follows modern Angular practices and improves code readability.

Let's verify proper subscription handling throughout the component:

@cremertim cremertim closed this Nov 29, 2024
@cremertim cremertim deleted the chore/communication/migrate-metis-service-to-standalone branch November 29, 2024 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client Pull requests that update TypeScript code. (Added Automatically!)
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

1 participant