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: Improve the user interface design on mobile devices #9460

Merged

Conversation

PaRangger
Copy link
Contributor

@PaRangger PaRangger commented Oct 12, 2024

Checklist

General

Client

  • Important: I implemented the changes with a very good performance, prevented too many (unnecessary) REST calls and made sure the UI is responsive, even with large data (e.g. using paging).
  • I strictly followed the client coding and design guidelines.
  • Following the theming guidelines, I specified colors only in the theming variable files and checked that the changes look consistent in both the light and the dark theme.
  • I added multiple integration tests (Jest) related to the features (with a high test coverage), while following the test guidelines.
  • I documented the TypeScript code using JSDoc style.
  • I added multiple screenshots/screencasts of my UI changes.
  • I translated all newly inserted strings into English and German.

Motivation and Context

The communication module is currently not optimized for mobile devices, resulting in UI elements being clipped. While native applications are available, some users may still attempt to access the module via smartphones. Addressing the clipping issue will enhance the module’s accessibility and improve the overall user experience.

Description

I resolved the clipping issues by overlaying the sidebar, search, messages, and answer thread. Additionally, I introduced a button in the conversation title to navigate back to the sidebar. When performing a search, the sidebar now automatically hides. This hide-and-show functionality of the sidebar is managed through a newly implemented event service. To avoid confusion, the "All Messages" tab is hidden on smartphones.

Most of the changes were made in CSS. However, for the sidebar functionality, the layout service is checked to determine if the device is a smartphone. These changes are designed to have no impact on the desktop version of the application.

This update serves as a quick fix to ensure basic usability of the module, including channel selection, reading and writing messages, and viewing answer threads.

Known Bugs for followup PRs:

  1. The emoji bar is clipping on some screens.
  2. On iOS, the text-cursor shows a litte blue mark on top.

Steps for Testing

Prerequisites:

  • 1 Student/Instructor/Tutor
  • 1 Course with communication enabled
  • Access the page via smartphone, simulator or in the smartphone debug window of a browser.
Bildschirmfoto 2024-10-12 um 23 45 21
  1. Log in to Artemis
  2. Navigate to the course
  3. In the sidebar, navigate to the communication tab.
  4. Check if the module is navigatable and the main use cases of the module are possible.

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

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Test Coverage

Client

Class/File Line Coverage Confirmation (assert/expect)
course-conversations.component.ts 89.2%
course-wide-search.component.ts 97.36%
conversation-header.component.ts 95.08%
course-overview.component.ts 86.89%
course-sidebar.service.ts 100%
layout.service.ts 93.33%

Screenshots

Bildschirmfoto 2024-10-12 um 23 56 13 Bildschirmfoto 2024-10-12 um 23 56 00 Bildschirmfoto 2024-10-12 um 23 56 26

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced mobile detection to disable tooltips for mobile users.
    • Enhanced sidebar functionality with a new service for managing visibility and state.
    • Improved UI responsiveness with dynamic class bindings for conversation and sidebar states.
    • Added a message input section for creating new messages based on conversation context.
    • Integrated new styles and responsive design elements for better mobile experience.
    • Implemented a mobile disclaimer in the course-wide search interface.
    • Added new localization keys for mobile guidance in the German language.
  • Bug Fixes

    • Adjusted sidebar toggle behavior to ensure correct state management on mobile devices.
  • Style

    • Updated styles for better responsiveness, including new font size classes and removal of !important directive.
    • Enhanced styling capabilities with Bootstrap SCSS imports and new responsive design elements.
  • Tests

    • Expanded test coverage for sidebar functionality and component interactions, ensuring robust behavior across scenarios.

@github-actions github-actions bot added tests client Pull requests that update TypeScript code. (Added Automatically!) labels Oct 12, 2024
@PaRangger PaRangger temporarily deployed to artemis-test5.artemis.cit.tum.de October 16, 2024 16:50 — with GitHub Actions Inactive
@PaRangger PaRangger marked this pull request as ready for review October 16, 2024 17:02
@PaRangger PaRangger requested a review from a team as a code owner October 16, 2024 17:02
Copy link

coderabbitai bot commented Oct 16, 2024

Walkthrough

The pull request introduces several changes primarily focused on enhancing the sidebar functionality and responsiveness of the user interface in an Angular application. Key modifications include the addition of a CourseSidebarService for managing sidebar visibility, updates to component classes to handle mobile detection, and adjustments to HTML templates for improved dynamic behavior. The changes also involve styling enhancements for better responsiveness and user experience, including modifications to SCSS files and the introduction of new methods in the components.

Changes

File Change Summary
src/main/webapp/app/core/core.module.ts Added import for isMobile function from ismobilejs-es5 library; updated ArtemisCoreModule constructor to disable tooltips for mobile users.
src/main/webapp/app/overview/course-conversations/course-conversations.component.html Updated button to clear search term to invoke hideSearchTerm() method; enhanced conditional rendering for sidebar and main content area classes based on state; restructured right-side message area.
src/main/webapp/app/overview/course-conversations/course-conversations.component.scss Integrated Bootstrap SCSS imports; added styles for .communication-message-wrap and .communication-answer-message-wrap; updated media queries for better responsiveness.
src/main/webapp/app/overview/course-conversations/course-conversations.component.ts Introduced new imports and subscriptions for managing sidebar state; updated logic for mobile detection and sidebar visibility; added methods for handling sidebar collapse and search term clearing.
src/main/webapp/app/overview/course-overview.component.html Modified sidebar toggle button and course management button for conditional class application based on pageTitle.
src/main/webapp/app/overview/course-overview.component.scss Added Bootstrap SCSS imports; refined sidebar behavior with transition variables; adjusted heights for responsive design.
src/main/webapp/app/overview/course-overview.component.ts Added CourseSidebarService dependency; introduced subscriptions for managing sidebar state; modified ngOnDestroy to unsubscribe from events.
src/main/webapp/app/overview/course-sidebar.service.ts Introduced CourseSidebarService class with methods and event emitters to manage sidebar visibility.
src/test/javascript/spec/component/course/course-overview.component.spec.ts Updated tests to include CourseSidebarService; added verification for sidebar state changes based on emitted events.
src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts Integrated CourseSidebarService and LayoutService into tests; added new tests for sidebar behavior in response to conversation changes.
src/test/javascript/spec/service/overview/course-sidebar.service.spec.ts Enhanced test suite for CourseSidebarService to verify event emissions and sidebar method invocations.
src/main/webapp/app/shared/breakpoints/layout.service.ts Updated isBreakpointActive method to explicitly return a boolean value indicating the active breakpoint.
src/main/webapp/content/scss/global.scss Removed !important from .sidebar-width width property for improved CSS practices; added new classes for font sizes to enhance text sizing control.
src/main/webapp/app/shared/sidebar/sidebar.component.ts Added effect function for improved sidebar event handling; refactored subscription logic into a new private method.
src/main/webapp/app/shared/sidebar/sidebar.component.scss Updated height calculations for responsive design using 100dvh for .scrollable-item-content and .sidebar classes.
src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.ts Added CourseSidebarService dependency; introduced openSidebar method to call the service's method for opening the sidebar.
src/main/webapp/app/shared/metis/answer-post/answer-post.component.scss Added Bootstrap SCSS imports; introduced media query mixin for responsive padding adjustments.
src/main/webapp/app/shared/metis/metis.component.scss Introduced new variable for profile picture height; added responsive styles using media queries.

Possibly related PRs

Suggested labels

bugfix, component:Communication, small

Suggested reviewers

  • florian-glombik
  • edkaya
  • rabeatwork
  • krusche
  • HawKhiem
  • pzdr7

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.

Caution

Inline review comments failed to post

Actionable comments posted: 31

🧹 Outside diff range comments (13)
src/main/webapp/app/shared/metis/metis.component.scss (1)

Line range hint 7-126: Existing styles maintained with improved interactivity.

The existing styles have been preserved, maintaining consistency with the current design. The addition of a hover effect to .post-edit-profile-picture-overlay enhances user interaction.

Consider adding a transition property to .post-edit-profile-picture-overlay for a smoother hover effect:

.post-edit-profile-picture-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(#000, 0.6);
    opacity: 0;
    color: var(--white);
+   transition: opacity 0.3s ease;
}
🧰 Tools
🪛 GitHub Check: Codacy Static Code Analysis

[warning] 128-128: src/main/webapp/app/shared/metis/metis.component.scss#L128
Unexpected unknown at-rule "@include" (at-rule-no-unknown)

src/main/webapp/app/overview/course-overview.component.scss (4)

Line range hint 15-131: LGTM! Enhanced responsiveness and user experience.

The updated styles for the sidebar and drawer container significantly improve responsiveness across different screen sizes and device types. The use of CSS variables and transitions enhances maintainability and user experience.

For consistency, consider using CSS variables for all fixed dimensions, such as $menu-width-closed and $menu-width-open.


Line range hint 133-226: LGTM! Improved visual feedback and hierarchy.

The updated styles for navigation links, message indicators, and course circles enhance the visual feedback and hierarchy of the UI elements. The use of placeholder selectors for message blocks is a good practice for code reuse.

Consider adding appropriate aria-label attributes to elements with visual-only indicators (like the new message dot) to improve accessibility for screen readers.


Line range hint 228-358: LGTM! Enhanced interactivity and visual feedback.

The new styles for the sidebar collapse button and dropdown menu significantly improve the interactivity and visual feedback of the UI. The use of transitions and pseudo-elements creates a smooth and engaging user experience.

To potentially improve performance, consider using transform instead of width for animations where possible, as it's generally more efficient for the browser to render.


Line range hint 360-408: LGTM! Improved print layout and mobile-specific adjustments.

The addition of print styles and the mobile-specific media query for the communication module enhances the overall user experience across different contexts. The print styles effectively hide unnecessary elements, while the mobile adjustments improve usability on small screens.

For consistency, consider using the same breakpoint variable or mixin for all mobile-specific styles throughout the file, instead of mixing max-width: 768px and @include media-breakpoint-down(sm).

src/main/webapp/app/overview/course-overview.component.ts (2)

Line range hint 70-102: LGTM! Consider using a consistent naming pattern for subscriptions.

The addition of the CourseSidebarService import and the new subscriptions for managing sidebar events are well-implemented and align with the PR objectives. The naming convention follows the Angular style guide, and the subscriptions are properly typed.

For improved consistency, consider using a common prefix for all sidebar-related subscriptions:

-private closeSidebarEventSubscription: Subscription;
-private openSidebarEventSubscription: Subscription;
-private toggleSidebarEventSubscription: Subscription;
+private sidebarCloseSubscription: Subscription;
+private sidebarOpenSubscription: Subscription;
+private sidebarToggleSubscription: Subscription;

This minor change would make the code more uniform and easier to read.


Line range hint 786-790: LGTM! Consider adding a comment for clarity.

The implementation of the keyboard shortcut for toggling the sidebar is well done and improves accessibility and user experience. The use of @HostListener is appropriate, and preventing the default behavior ensures no unintended browser actions occur.

To improve code clarity, consider adding a brief comment explaining the purpose of this shortcut:

/**
 * Toggles the sidebar visibility when the user presses Ctrl+Shift+B.
 * This keyboard shortcut improves accessibility and provides a quick way to show/hide the sidebar.
 */
@HostListener('window:keydown.Control.Shift.b', ['$event'])
onKeyDownControlShiftB(event: KeyboardEvent) {
    // ... existing code ...
}

This comment will help other developers understand the purpose and importance of this keyboard shortcut.

src/test/javascript/spec/component/course/course-overview.component.spec.ts (6)

Line range hint 290-297: LGTM! Consider enhancing test readability.

The test case is well-structured and effectively verifies the creation of sidebar items when the student course analytics dashboard feature is active. It correctly checks for the presence and order of expected items.

To improve readability, consider using constants for the expected sidebar item titles:

const EXPECTED_TITLES = ['Dashboard', 'Exercises', 'Lectures'];
sidebarItems.forEach((item, index) => {
    expect(item.title).toContain(EXPECTED_TITLES[index]);
});

Line range hint 300-306: LGTM! Consider consistent testing approach.

The test case effectively verifies the creation of default sidebar items when the student course analytics dashboard feature is not active. It correctly checks for the presence and order of expected items.

For consistency with the previous test and to improve maintainability, consider using a similar approach with constants:

const EXPECTED_TITLES = ['Exercises', 'Lectures'];
sidebarItems.forEach((item, index) => {
    expect(item.title).toContain(EXPECTED_TITLES[index]);
});

Line range hint 351-367: LGTM! Consider improving test isolation.

The test case effectively verifies that message-related data is not loaded when messaging is disabled for a course. It correctly checks that specific methods are not called when navigating through different tabs.

To improve test isolation and readability, consider extracting the tab navigation logic into a separate helper method:

function navigateThroughTabs(tabs: string[]) {
    tabs.forEach((tab) => {
        route.snapshot.firstChild!.routeConfig!.path = tab;
        component.onSubRouteActivate({ controlConfiguration: undefined });
    });
}

// In the test
navigateThroughTabs(['exercises', 'communication', 'exercises', 'communication']);

This approach would make the test more maintainable and easier to understand.


Line range hint 409-425: LGTM! Consider enhancing error handling specificity.

The test case effectively verifies the error handling behavior when loading the course fails. It correctly checks that an alert is shown in response to a 404 error.

To improve the test's robustness and specificity, consider checking the content of the alert message:

const alertServiceSpy = jest.spyOn(alertService, 'addAlert');

component.loadCourse().subscribe(
    () => {
        throw new Error('should not happen');
    },
    (error) => {
        expect(error).toBeDefined();
        expect(alertServiceSpy).toHaveBeenCalledWith({
            type: 'danger',
            msg: 'artemisApp.course.errors.notFound',
        });
    },
);

This ensures that not only is an alert shown, but it's the correct alert for the specific error scenario.


Line range hint 427-436: LGTM! Consider enhancing test clarity.

The test case effectively verifies the behavior of canRegisterForCourse when the server returns a 403 error. It correctly uses fakeAsync and tick for handling asynchronous behavior.

To improve the test's clarity and robustness, consider using the toBeObservable matcher from RxJS testing utilities:

import { TestScheduler } from 'rxjs/testing';

it('should return false for canRegisterForCourse if the server returns 403', () => {
    const scheduler = new TestScheduler((actual, expected) => {
        expect(actual).toEqual(expected);
    });

    scheduler.run(({ expectObservable }) => {
        findOneForRegistrationStub.mockReturnValue(throwError(() => new HttpResponse({ status: 403 })));

        const expected = '(a|)';
        const expectedValues = { a: false };

        expectObservable(component.canRegisterForCourse()).toBe(expected, expectedValues);
    });
});

This approach provides a more declarative way of testing observables and eliminates the need for fakeAsync and tick.


Line range hint 622-628: LGTM! Consider enhancing test completeness.

The test case effectively verifies the basic functionality of the toggleCollapseState method. It correctly checks that the isNavbarCollapsed property is toggled as expected.

To improve the test's completeness, consider adding an initial state check and verifying the method's behavior when called an odd number of times:

it('should toggle isNavbarCollapsed when toggleCollapseState is called', () => {
    fixture.detectChanges();
    
    // Check initial state
    expect(component.isNavbarCollapsed).toBeFalse();

    component.toggleCollapseState();
    expect(component.isNavbarCollapsed).toBeTrue();

    component.toggleCollapseState();
    expect(component.isNavbarCollapsed).toBeFalse();

    // Check behavior when called an odd number of times
    component.toggleCollapseState();
    expect(component.isNavbarCollapsed).toBeTrue();
});

This ensures that the method behaves correctly regardless of the number of times it's called and verifies the initial state.

🛑 Comments failed to post (31)
src/main/webapp/app/overview/course-sidebar.service.ts (2)

1-24: 🧹 Nitpick (assertive)

Overall implementation is solid, consider adding JSDoc comments.

The CourseSidebarService is well-implemented, following Angular best practices and the provided coding guidelines. It provides a clean API for managing sidebar visibility using EventEmitters, which allows for loose coupling between components.

To further improve the code:

Consider adding JSDoc comments to the class and its methods to enhance code documentation. This will provide better context for other developers using this service. For example:

/**
 * Service for managing the visibility of the course sidebar.
 */
export class CourseSidebarService {
  // ... existing properties ...

  /**
   * Emits an event to close the sidebar.
   */
  public closeSidebar(): void {
    // ... existing implementation ...
  }

  // ... add similar comments for other methods ...
}
🧰 Tools
🪛 Biome

[error] 11-11: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)


11-11: ⚠️ Potential issue

Remove unnecessary constructor.

The empty constructor doesn't serve any purpose and can be safely removed to improve code clarity.

Apply this diff to remove the unnecessary constructor:

-    constructor() {}
📝 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.


🧰 Tools
🪛 Biome

[error] 11-11: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.scss (1)

43-43: 💡 Codebase verification

Multiple SCSS files utilize vh units, which may affect the effectiveness of the max-height adjustment in addressing mobile clipping issues.

  • Consider reviewing and adjusting other vh-based calculations to ensure consistent behavior across the application.
  • Implement targeted media queries for mobile devices to more effectively resolve clipping concerns.
🔗 Analysis chain

LGTM, but please clarify the impact of these changes.

The reduction of max-height from 75vh to 74vh for both production and development environments is noted. While this change aligns with the goal of addressing clipping issues, it's a relatively small adjustment.

Could you provide more context on how this 1vh reduction specifically addresses the clipping issues on mobile devices? Are there any test results or visual comparisons that demonstrate the improvement?

Also applies to: 47-47

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for other vh-based calculations in the codebase that might need similar adjustments

# Test: Search for vh-based calculations in SCSS files
rg --type scss '\d+vh' -g '!*conversation-messages.component.scss'

Length of output: 97


Script:

#!/bin/bash
# Description: Check for other vh-based calculations in SCSS files that might need similar adjustments

# Test: Search for vh-based calculations in SCSS files excluding conversation-messages.component.scss
rg '\d+vh' -g '*.scss' -g '!.*/conversation-messages.component.scss'

Length of output: 13829

src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.scss (1)

40-40: 🧹 Nitpick (assertive)

Minor: Consider addressing linter warnings.

Two linter warnings were reported:

  1. Unknown at-rule "@include" (line 40): This is a false positive as "@include" is a valid SCSS directive.
  2. Unexpected empty line before declaration (line 58): This is a minor style issue.

While these don't affect functionality, you might consider:

  • Configuring the linter to recognize SCSS-specific rules like "@include".
  • Removing the empty line before the transition property on line 58 for consistency.

Also applies to: 58-58

🧰 Tools
🪛 GitHub Check: Codacy Static Code Analysis

[warning] 40-40: src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.scss#L40
Unexpected unknown at-rule "@include" (at-rule-no-unknown)

src/test/javascript/spec/service/overview/course-sidebar.service.spec.ts (4)

13-15: 🧹 Nitpick (assertive)

Consider using a more specific assertion

While toBeTruthy() works, it might be more precise to use toBeDefined() or toBeInstanceOf(CourseSidebarService) to explicitly check that the service is created and is of the correct type.

You could update the test like this:

it('should be created', () => {
    expect(service).toBeDefined();
    expect(service).toBeInstanceOf(CourseSidebarService);
});

17-33: 🧹 Nitpick (assertive)

LGTM: Event emission tests are well-structured

The tests for closeSidebar, openSidebar, and toggleSidebar events are correctly implemented using jest.spyOn to monitor event emissions.

Consider using toHaveBeenCalledTimes(1) instead of toHaveBeenCalled() to ensure that the emit method is called exactly once. For example:

it('should emit closeSidebar event', () => {
    const emitSpy = jest.spyOn(service.closeSidebar$, 'emit');
    service.closeSidebar();
    expect(emitSpy).toHaveBeenCalledTimes(1);
});

This change would make the tests more precise and catch potential issues where an event might be emitted multiple times unintentionally.


35-51: 🧹 Nitpick (assertive)

LGTM: Comprehensive test for event subscriptions

This test case effectively verifies that subscribed callbacks are invoked when events are emitted through the service methods.

Consider the following improvements for increased precision and readability:

  1. Use toHaveBeenCalledTimes(1) instead of toHaveBeenCalled() to ensure each callback is called exactly once.
  2. Group the service method calls and expectations together for better readability.
  3. Add assertions to check that other spies are not called when they shouldn't be.

Here's an example of how you could refactor the test:

it('should emit events when subscribing', () => {
    const closeSpy = jest.fn();
    const openSpy = jest.fn();
    const toggleSpy = jest.fn();

    service.closeSidebar$.subscribe(closeSpy);
    service.openSidebar$.subscribe(openSpy);
    service.toggleSidebar$.subscribe(toggleSpy);

    service.closeSidebar();
    expect(closeSpy).toHaveBeenCalledTimes(1);
    expect(openSpy).not.toHaveBeenCalled();
    expect(toggleSpy).not.toHaveBeenCalled();

    service.openSidebar();
    expect(openSpy).toHaveBeenCalledTimes(1);
    expect(closeSpy).toHaveBeenCalledTimes(1);
    expect(toggleSpy).not.toHaveBeenCalled();

    service.toggleSidebar();
    expect(toggleSpy).toHaveBeenCalledTimes(1);
    expect(closeSpy).toHaveBeenCalledTimes(1);
    expect(openSpy).toHaveBeenCalledTimes(1);
});

This refactored version provides more precise assertions and clearly separates the testing of each service method.


1-52: 🧹 Nitpick (assertive)

LGTM: Comprehensive test suite with good structure

The test suite provides good coverage of the CourseSidebarService, testing its creation, all public methods, and both event emission and subscription. The structure is clear and follows good testing practices.

Consider adding the following tests to further improve coverage:

  1. Test that calling the same method multiple times emits the event each time.
  2. Verify that unsubscribing from an event prevents the callback from being called.
  3. Test any error scenarios or edge cases that might exist in the service implementation.

Example of an additional test:

it('should not call callback after unsubscribing', () => {
    const spy = jest.fn();
    const subscription = service.closeSidebar$.subscribe(spy);
    
    service.closeSidebar();
    expect(spy).toHaveBeenCalledTimes(1);
    
    subscription.unsubscribe();
    service.closeSidebar();
    expect(spy).toHaveBeenCalledTimes(1); // Still only called once
});

These additional tests would provide even more confidence in the robustness of the service.

src/main/webapp/app/overview/course-conversations/course-conversations.component.scss (3)

43-50: 🧹 Nitpick (assertive)

LGTM: New classes for message wrapping

The addition of .communication-message-wrap and .communication-answer-message-wrap classes is a good approach to manage message layout and prevent clipping issues. The use of min-width and max-width will help maintain readability across different screen sizes.

Consider adding a comment explaining the purpose of these classes for better maintainability.


94-101: 🧹 Nitpick (assertive)

LGTM: Responsive styling for answer threads

The styling for .communication-answer-message-wrap effectively utilizes the available space on mobile devices when an answer thread is open. Setting max-width: 100% ensures that the content uses the full width of the screen, which is crucial for readability on smaller devices.

Consider adding a transition to the display property change for a smoother user experience when opening and closing answer threads. You can use the transition property with opacity instead of display for a smoother effect.


85-87: 🧹 Nitpick (assertive)

Consider reordering selectors for better specificity management

The static analysis tool flagged a potential issue with selector order. While this doesn't affect functionality, reordering selectors to follow a more specific-to-general pattern can improve code maintainability and reduce the risk of unexpected style overrides.

Consider moving the .sidebar-wrap selector before more specific selectors like .communication-content-sidebar.sidebar-collapsed .sidebar-wrap if possible, without breaking the intended styling.

🧰 Tools
🪛 GitHub Check: Codacy Static Code Analysis

[warning] 85-85: src/main/webapp/app/overview/course-conversations/course-conversations.component.scss#L85
Expected selector ".sidebar-wrap" to come before selector ".communication-content-sidebar.sidebar-collapsed .sidebar-wrap" (no-descending-specificity)

src/main/webapp/app/overview/course-conversations/layout/conversation-header/conversation-header.component.html (2)

4-8: 🧹 Nitpick (assertive)

Approve mobile sidebar button with accessibility suggestion.

The addition of a mobile-specific button for opening the sidebar is a good improvement for mobile usability. The conditional display and placement are appropriate.

Consider adding an aria-label to the button for better accessibility:

- <button class="btn btn-sm btn-outline-secondary d-inline-block d-sm-none me-2" (click)="openSidebar()">
+ <button class="btn btn-sm btn-outline-secondary d-inline-block d-sm-none me-2" (click)="openSidebar()" aria-label="Open sidebar">
📝 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.

            <div class="d-flex align-items-center px-3">
                <button class="btn btn-sm btn-outline-secondary d-inline-block d-sm-none me-2" (click)="openSidebar()" aria-label="Open sidebar">
                    <fa-icon [icon]="faChevronLeft" />
                </button>
                <h4 class="pointer d-inline-block rounded py-2 info mb-0" (click)="openConversationDetailDialog($event, INFO)">

44-44: 💡 Codebase verification

Inconsistent margin classes in button groups.

The me-1 class has been removed from the btn-group div in conversation-header.component.html. However, other components still use the me-1 class with btn-group, which may lead to inconsistent spacing across the application.

Please clarify the reason for this change. If it's intentional, consider applying it uniformly or updating the design guidelines to ensure consistency.

🔗 Analysis chain

Clarify the removal of margin class from button group.

The me-1 class has been removed from the button group div. While this might be intentional to allow the parent container's gap property to control spacing, it's not clear if this change is necessary or if it might cause inconsistencies with other similar elements.

Could you please clarify the reasoning behind removing this margin class? If it's intentional, consider adding a comment explaining the decision to maintain consistency across the codebase.

To verify the impact of this change, you can run the following command to check for other occurrences of similar button groups:

This will help ensure that the spacing remains consistent across all button groups in the application.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar button group structures in HTML files
rg --type html 'class="btn-group.*?role="group"' -A 5 -B 5

Length of output: 41952

src/main/webapp/app/overview/course-conversations/course-conversations.component.html (3)

43-46: 🧹 Nitpick (assertive)

LGTM: Improved semantic structure and conditional styling

The updates to the main content area's class structure enhance the semantic clarity and allow for more dynamic styling based on the environment and thread state. The use of communication-message-wrap as the main class name is descriptive and follows good naming conventions.

Consider simplifying the ngClass binding for better readability:

- [ngClass]="{ 'content-height-dev': !isProduction || isTestServer, 'is-answer-thread-open': !!postInThread }"
+ [ngClass]="{ 'content-height-dev': !isProduction || isTestServer, 'is-answer-thread-open': Boolean(postInThread) }"

This change makes the boolean conversion more explicit and potentially more readable for other developers.

📝 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.

            <div
                class="communication-message-wrap col flex-grow-1 module-bg rounded-3 scrollable-content"
                [ngClass]="{ 'content-height-dev': !isProduction || isTestServer, 'is-answer-thread-open': Boolean(postInThread) }"
            >

60-61: 🧹 Nitpick (assertive)

LGTM: Consistent styling with main content area

The updates to the adjacent content area's class structure are consistent with the changes made to the main content area. This consistency improves the overall structure and maintainability of the template.

As suggested for the main content area, consider simplifying the ngClass binding:

- [ngClass]="{ 'content-height-dev': !isProduction || isTestServer, 'is-answer-thread-open': !!postInThread }"
+ [ngClass]="{ 'content-height-dev': !isProduction || isTestServer, 'is-answer-thread-open': Boolean(postInThread) }"
📝 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.

                class="communication-answer-message-wrap col flex-grow-1 justify-end px-0 scrollable-content"
                [ngClass]="{ 'content-height-dev': !isProduction || isTestServer, 'is-answer-thread-open': Boolean(postInThread) }"

25-26: 🧹 Nitpick (assertive)

LGTM: Enhanced sidebar styling based on conversation state

The addition of the is-not-in-active-conversation class when there's no active conversation is a good improvement. It allows for more dynamic styling of the sidebar based on the application state.

Consider using kebab-case for CSS class names to align with common CSS naming conventions:

- 'is-not-in-active-conversation': !activeConversation
+ 'is-not-in-active-conversation': !activeConversation

Committable suggestion was skipped due to low confidence.

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

45-52: ⚠️ Potential issue

Improved date display structure and logic.

The new structure for date display enhances semantic meaning and styling capabilities. The conditional rendering for the "today" flag and the adaptive date format improve user experience.

Use new Angular syntax consistently.

The changes use both old (*ngIf) and new (@if) Angular syntax. As per the coding guidelines, @if and @for should always be used over the old style.

Please update the following lines to use the new Angular syntax:

-            <span [disableTooltip]="postingIsOfToday" ngbTooltip="{{ posting.creationDate | artemisDate: 'time' }}">
+            <span @if="!postingIsOfToday" [attr.ngbTooltip]="posting.creationDate | artemisDate: 'time'">

This change ensures consistency with the new Angular syntax throughout the template.

📝 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.

        <span class="post-header-date-separator">-</span>
        <span class="post-header-date">
            @if (postingIsOfToday) {
                <span [jhiTranslate]="todayFlag ?? ''" id="today-flag" class="fs-small"></span>,
            }
            <span @if="!postingIsOfToday" [attr.ngbTooltip]="posting.creationDate | artemisDate: 'time'">
                {{ postingIsOfToday ? (posting.creationDate | artemisDate: 'time') : (posting.creationDate | artemisDate: 'short') }}
            </span>
src/main/webapp/app/core/core.module.ts (1)

113-115: 🧹 Nitpick (assertive)

LGTM: Mobile detection and tooltip disabling.

The added code successfully detects mobile devices and disables tooltips accordingly, which aligns with the PR objective of improving the mobile user experience. The implementation follows Angular style guidelines and uses good practices like optional chaining.

Consider adding a brief comment explaining why tooltips are disabled on mobile devices for better code clarity:

// Disable tooltips on mobile devices to improve touch interface usability
if (isMobile(window.navigator.userAgent).any ?? false) {
    tooltipConfig.disableTooltip = true;
}
src/main/webapp/app/shared/metis/posting-header/answer-post-header/answer-post-header.component.html (1)

45-52: 🧹 Nitpick (assertive)

Approve changes with a suggestion for syntax improvement

The new structure for date display is well-organized and aligns with the PR objectives of improving the UI. The conditional rendering for the "today" flag enhances the user experience.

However, to adhere to the coding guidelines, consider updating the @if directive to use the new Angular syntax:

-            @if (postingIsOfToday) {
-                <span [jhiTranslate]="todayFlag ?? ''" id="today-flag" class="fs-small"></span>,
-            }
+            @if (postingIsOfToday) {
+                <span [jhiTranslate]="todayFlag ?? ''" id="today-flag" class="fs-small"></span>,
+            }

This change ensures consistency with the new Angular syntax as specified in the coding guidelines.

Committable suggestion was skipped due to low confidence.

src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.ts (1)

97-100: 🧹 Nitpick (assertive)

Consider adding return type and JSDoc comment.

The openSidebar method is correctly implemented and follows good practices. However, consider the following improvements:

  1. Add an explicit return type (:void) for clarity.
  2. Include a JSDoc comment to describe the method's purpose.

Here's an example of how it could look:

/**
 * Opens the course sidebar.
 * This method delegates to the CourseSidebarService.
 */
public openSidebar(): void {
    this.courseSidebarService.openSidebar();
}

These changes would enhance code readability and maintain consistency with TypeScript and Angular best practices.

src/main/webapp/i18n/en/metis.json (2)

97-97: 🧹 Nitpick (assertive)

LGTM: Added mobile-specific call-to-action.

The new "mobileDisclaimerCallToAction" key provides a clear action for mobile users, which complements the mobile disclaimer message and supports the PR's goal of improving mobile usability.

For consistency with other action phrases in the file (e.g., "Show content", "Collapse content"), consider capitalizing both words:

-                "mobileDisclaimerCallToAction": "Select conversation"
+                "mobileDisclaimerCallToAction": "Select Conversation"
📝 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.

                "mobileDisclaimerCallToAction": "Select Conversation"

96-96: 🧹 Nitpick (assertive)

LGTM: Added mobile-specific guidance message.

The new "mobileDisclaimer" key provides clear instructions for mobile users, which aligns with the PR's goal of improving mobile usability.

Consider adding a period at the end of the message for consistency with other messages in the file:

-                "mobileDisclaimer": "Please select a conversation first.",
+                "mobileDisclaimer": "Please select a conversation first.",

Committable suggestion was skipped due to low confidence.

src/main/webapp/i18n/de/metis.json (1)

97-97: 🧹 Nitpick (assertive)

LGTM with a minor suggestion: Added mobile-specific call-to-action.

The new "mobileDisclaimerCallToAction" string effectively prompts mobile users to choose a conversation, enhancing the mobile user experience. The German translation correctly uses informal language (dutzen) as per our coding guidelines.

For consistency with the "mobileDisclaimer" string, consider using "Konversation auswählen" instead of "Konversation wählen". This would maintain the same verb ("auswählen") across both related strings.

src/main/webapp/app/overview/course-overview.component.ts (1)

209-224: 🧹 Nitpick (assertive)

LGTM! Ensure proper unsubscription in ngOnDestroy().

The implementation of the CourseSidebarService and the setup of subscriptions in ngOnInit() are well done. The logic for managing the sidebar state is correct and takes into account the activatedComponentReference, which is good for maintaining consistency across components.

To prevent potential memory leaks, ensure that these subscriptions are properly unsubscribed in the ngOnDestroy() method. You can do this by adding the following lines to the ngOnDestroy() method:

ngOnDestroy() {
  // ... existing code ...
  this.closeSidebarEventSubscription?.unsubscribe();
  this.openSidebarEventSubscription?.unsubscribe();
  this.toggleSidebarEventSubscription?.unsubscribe();
}

This will ensure that the subscriptions are cleaned up when the component is destroyed.

src/test/javascript/spec/component/course/course-overview.component.spec.ts (1)

656-662: 🧹 Nitpick (assertive)

LGTM! Consider enhancing test robustness.

The test case effectively verifies the basic functionality of opening the dropdown. It correctly checks that the isOpen property is set to true when the dropdown is opened.

To improve the test's robustness and clarity, consider adding an initial state check and using a more descriptive test name:

it('should open the dropdown and set isOpen to true', () => {
    fixture.detectChanges();
    
    if (component.itemsDrop) {
        // Check initial state
        expect(component.itemsDrop.isOpen).toBeFalse();

        component.itemsDrop.open();
        fixture.detectChanges();
        
        expect(component.itemsDrop.isOpen).toBeTrue();
    } else {
        fail('itemsDrop is not defined');
    }
});

This approach ensures that the initial state is verified, provides a more descriptive test name, and handles the case where itemsDrop might be undefined.

src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.html (3)

16-70: 🛠️ Refactor suggestion

Refactor repetitive checkbox code into a reusable component.

The checkbox inputs for filtering options are repetitive. Consider creating a reusable component for these checkboxes to improve maintainability and reduce code duplication.

For example, you could create a FilterCheckboxComponent that accepts inputs like formControlName, labelTranslateKey, and id. This would also make it easier to add or modify filters in the future.


58-63: 🛠️ Refactor suggestion

Simplify tooltip logic by moving it to the component class.

The inline conditional logic within the [ngbTooltip] attribute can make the template harder to read and maintain. Consider moving this logic to the component class.

In your component class, add a getter method:

get sortingOrderTooltip(): string {
  return this.sortingOrder === SortDirection.ASCENDING
    ? this.translateService.instant('artemisApp.metis.overview.sortAscending')
    : this.translateService.instant('artemisApp.metis.overview.sortDescending');
}

Then, update the template:

<fa-icon
  [icon]="sortingOrder === SortDirection.ASCENDING ? faLongArrowAltUp : faLongArrowAltDown"
  [ngbTooltip]="sortingOrderTooltip"
/>

76-80: 🧹 Nitpick (assertive)

Enhance accessibility for loading indicator.

The loading indicator lacks ARIA attributes which are important for screen reader accessibility. Consider adding role="status" and aria-live="polite" to inform assistive technologies of the loading state.

<div class="envelope" 
+     role="status" 
+     aria-live="polite">
  <fa-icon size="3x" [icon]="faCircleNotch" animation="spin" />
</div>
📝 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.

                    @if (isFetchingPosts) {
                        <div class="envelope"
                             role="status"
                             aria-live="polite">
                            <fa-icon size="3x" [icon]="faCircleNotch" animation="spin" />
                        </div>
                    }
src/main/webapp/app/overview/course-conversations/course-conversations.component.ts (3)

24-25: 🛠️ Refactor suggestion

Consider using Angular's BreakpointObserver for mobile detection

Instead of using the third-party ismobilejs-es5 library for mobile device detection, consider using Angular's BreakpointObserver from @angular/cdk/layout. This approach aligns with Angular best practices, reduces external dependencies, and provides a more robust solution for responsive design.

Here's how you can implement it:

  1. Import BreakpointObserver and BreakpointState:

    import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
  2. Inject BreakpointObserver in the constructor:

    constructor(
        private breakpointObserver: BreakpointObserver,
        // ... other dependencies
    ) {}
  3. Set up the breakpoint observer in ngOnInit:

    ngOnInit(): void {
        this.breakpointObserver
            .observe(['(max-width: 768px)'])
            .subscribe((state: BreakpointState) => {
                this.isMobile = state.matches;
            });
        // ... rest of your code
    }

    This uses a media query to detect screen sizes typical of mobile devices. Adjust (max-width: 768px) as needed for your application's breakpoints.


78-80: 🛠️ Refactor suggestion

Simplify subscription management with a Subscription container

When handling multiple subscriptions, you can streamline your code by using a Subscription container. This approach simplifies the unsubscription process and enhances code readability.

  1. Declare a composite subscription at the class level:

    private subscriptions = new Subscription();
  2. Add subscriptions to the container in ngOnInit:

    ngOnInit(): void {
        this.subscriptions.add(
            this.courseSidebarService.openSidebar$.subscribe(() => this.setIsCollapsed(true)),
        );
        this.subscriptions.add(
            this.courseSidebarService.closeSidebar$.subscribe(() => this.setIsCollapsed(false)),
        );
        this.subscriptions.add(
            this.courseSidebarService.toggleSidebar$.subscribe(() => this.toggleSidebar()),
        );
        // ... rest of your code
    }
  3. Unsubscribe from all in ngOnDestroy:

    ngOnDestroy() {
        this.subscriptions.unsubscribe();
        this.ngUnsubscribe.next();
        this.ngUnsubscribe.complete();
        this.profileSubscription?.unsubscribe();
    }

    This ensures all subscriptions are properly cleaned up, preventing potential memory leaks.

Also applies to: 153-163, 233-235


289-300: 🧹 Nitpick (assertive)

Add unit tests for new mobile-specific logic

The newly introduced methods and conditional logic for mobile devices, such as hideSearchTerm, closeSidebarOnMobile, and the updates in onSearch and onConversationSelected, are crucial for mobile responsiveness. Adding unit tests for these will ensure they function correctly across different scenarios.

Would you like assistance in creating unit tests for these methods to enhance test coverage and reliability?

Also applies to: 330-333, 338-342

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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 068c060 and 29e0745.

📒 Files selected for processing (2)
  • src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.html (1 hunks)
  • src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.html
🧰 Additional context used
📓 Path-based instructions (1)
src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.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 (3)
src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts (3)

15-15: LGTM: Clean import additions for mobile responsiveness.

The new imports follow Angular's best practices and are necessary for implementing the mobile responsiveness feature.

Also applies to: 31-32


83-85: LGTM: Well-structured mobile detection setup.

The mobile detection setup follows Angular's best practices:

  • Public isMobile flag for template usage
  • Modern dependency injection using inject()
  • Proper naming conventions

Line range hint 134-137: LGTM: Proper cleanup implementation.

The component correctly implements memory leak prevention:

  • Uses takeUntil pattern consistently
  • Properly completes subscriptions in ngOnDestroy

Copy link

⚠️ Unable to deploy to test servers ⚠️

Testserver "artemis-test5.artemis.cit.tum.de" is already in use by PR #9566.

@github-actions github-actions bot added the deployment-error Added by deployment workflows if an error occured label Oct 24, 2024
@PaRangger PaRangger added deploy:artemis-test2 and removed deployment-error Added by deployment workflows if an error occured labels Oct 24, 2024
@PaRangger PaRangger temporarily deployed to artemis-test2.artemis.cit.tum.de October 24, 2024 22:14 — with GitHub Actions Inactive
@krusche krusche merged commit 8bf15b3 into develop Oct 24, 2024
59 of 63 checks passed
@krusche krusche deleted the bugfix/communication/fix-clipping-issues-on-mobile-screens branch October 24, 2024 22:44
@krusche krusche changed the title Communication: Fix clipping issues on mobile devices Communication: Improve the user interface design on mobile devices Oct 25, 2024
@github-actions github-actions bot added the deployment-error Added by deployment workflows if an error occured label Oct 25, 2024
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!) deployment-error Added by deployment workflows if an error occured ready to merge tests
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.