opf/openproject

View on GitHub
frontend/src/app/shared/components/op-context-menu/wp-context-menu/wp-view-context-menu.directive.ts

Summary

Maintainability
C
1 day
Test Coverage
import { Injector } from '@angular/core';
import {
  WorkPackageAction,
  WorkPackageContextMenuHelperService,
} from 'core-app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service';
import { States } from 'core-app/core/states/states.service';
import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service';
import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service';
import { isClickedWithModifier } from 'core-app/shared/helpers/link-handling/link-handling';
import { OpContextMenuHandler } from 'core-app/shared/components/op-context-menu/op-context-menu-handler';
import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service';
import {
  OpContextMenuItem,
  OpContextMenuLocalsMap,
} from 'core-app/shared/components/op-context-menu/op-context-menu.types';
import { PERMITTED_CONTEXT_MENU_ACTIONS } from 'core-app/shared/components/op-context-menu/wp-context-menu/wp-static-context-menu-actions';
import { OpModalService } from 'core-app/shared/components/modal/modal.service';
import { StateService } from '@uirouter/core';
import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator';
import { CopyToClipboardService } from 'core-app/shared/components/copy-to-clipboard/copy-to-clipboard.service';
import { TimeEntryCreateService } from 'core-app/shared/components/time_entries/create/create.service';
import { splitViewRoute } from 'core-app/features/work-packages/routing/split-view-routes.helper';
import { WpDestroyModalComponent } from 'core-app/shared/components/modals/wp-destroy-modal/wp-destroy.modal';
import isNewResource from 'core-app/features/hal/helpers/is-new-resource';
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service';
import * as moment from 'moment-timezone';

export class WorkPackageViewContextMenu extends OpContextMenuHandler {
  @InjectField() protected states!:States;

  @InjectField() protected wpRelationsHierarchyService:WorkPackageRelationsHierarchyService;

  @InjectField() protected opModalService:OpModalService;

  @InjectField() protected $state!:StateService;

  @InjectField() protected wpTableSelection:WorkPackageViewSelectionService;

  @InjectField() protected WorkPackageContextMenuHelper!:WorkPackageContextMenuHelperService;

  @InjectField() protected timeEntryCreateService:TimeEntryCreateService;

  @InjectField() protected pathHelper:PathHelperService;

  protected workPackage = this.states.workPackages.get(this.workPackageId).value!;

  protected selectedWorkPackages = this.getSelectedWorkPackages();

  protected permittedActions = this.WorkPackageContextMenuHelper.getPermittedActions(
    this.selectedWorkPackages,
    PERMITTED_CONTEXT_MENU_ACTIONS,
    this.allowSplitScreenActions,
  );

  // Get the base route for the current route to ensure we always link correctly
  protected baseRoute = this.$state.current.data.baseRoute || this.$state.current.name;

  protected items = this.buildItems();

  private copyToClipboardService:CopyToClipboardService;

  constructor(
    public injector:Injector,
    protected workPackageId:string,
    protected $element:JQuery,
    protected additionalPositionArgs:any = {},
    protected allowSplitScreenActions:boolean = true,
  ) {
    super(injector.get(OPContextMenuService));
    this.copyToClipboardService = injector.get(CopyToClipboardService);
  }

  public get locals():OpContextMenuLocalsMap {
    return {
      contextMenuId: 'work-package-context-menu',
      label: I18n.t('js.label_work_package_context_menu'),
      items: this.items,
    };
  }

  public positionArgs(evt:JQuery.TriggeredEvent) {
    const position = super.positionArgs(evt);
    _.assign(position, this.additionalPositionArgs);

    return position;
  }

  public triggerContextMenuAction(action:WorkPackageAction) {
    const { link } = action;
    const id = this.workPackage.id as string;

    switch (action.key) {
      case 'delete':
        this.deleteSelectedWorkPackages();
        break;

      case 'edit':
        this.editSelectedWorkPackages(link!);
        break;

      case 'copy':
        this.copySelectedWorkPackages(link!);
        break;

      case 'copy_link_to_clipboard': {
        const url = new URL(String(link), window.location.origin);
        this.copyToClipboardService.copy(url.toString());
        break;
      }
      case 'copy_to_other_project':
        window.location.href = `${this.pathHelper.staticBase}/work_packages/move/new?copy=true&ids[]=${id}`;
        break;

      case 'relation-new-child':
        this.wpRelationsHierarchyService.addNewChildWp(this.baseRoute, this.workPackage);
        break;

      case 'log_time':
        this.logTimeForSelectedWorkPackage();
        break;
      case 'relations':
        void this.$state.go(
          `${splitViewRoute(this.$state)}.tabs`,
          { workPackageId: this.workPackageId, tabIdentifier: 'relations' },
        );
        break;

      default:
        window.location.href = link!;
        break;
    }
  }

  private deleteSelectedWorkPackages() {
    const selected = this.getSelectedWorkPackages();
    this.opModalService.show(WpDestroyModalComponent, this.injector, { workPackages: selected });
  }

  private editSelectedWorkPackages(link:any) {
    const selected = this.getSelectedWorkPackages();

    if (selected.length > 1) {
      window.location.href = link;
    }
  }

  private copySelectedWorkPackages(link:any) {
    const selected = this.getSelectedWorkPackages();

    if (selected.length > 1) {
      window.location.href = link;
      return;
    }

    const params = {
      copiedFromWorkPackageId: selected[0].id,
    };

    this.$state.go(`${this.baseRoute}.copy`, params);
  }

  private logTimeForSelectedWorkPackage() {
    this.timeEntryCreateService
      .create(moment(new Date()), this.workPackage)
      .catch(() => {
        // do nothing, the user closed without changes
      });
  }

  private getSelectedWorkPackages() {
    const selectedWorkPackages = this.wpTableSelection.getSelectedWorkPackages();

    if (selectedWorkPackages.length === 0) {
      return [this.workPackage];
    }

    if (selectedWorkPackages.indexOf(this.workPackage) === -1) {
      selectedWorkPackages.push(this.workPackage);
    }

    return selectedWorkPackages;
  }

  protected buildItems():OpContextMenuItem[] {
    const items = this.permittedActions.map((action:WorkPackageAction) => ({
      class: undefined as string|undefined,
      disabled: false,
      linkText: action.text,
      href: action.href,
      icon: action.icon != null ? action.icon : `icon-${action.key}`,
      onClick: ($event:JQuery.TriggeredEvent) => {
        if (action.href && isClickedWithModifier($event)) {
          return false;
        }

        this.triggerContextMenuAction(action);
        return true;
      },
    }));

    if (!isNewResource(this.workPackage)) {
      items.unshift({
        disabled: false,
        icon: 'icon-view-fullscreen',
        class: 'openFullScreenView',
        href: this.$state.href('work-packages.show', { workPackageId: this.workPackageId }),
        linkText: I18n.t('js.button_open_fullscreen'),
        onClick: ($event:JQuery.TriggeredEvent) => {
          if (isClickedWithModifier($event)) {
            return false;
          }

          this.$state.go(
            'work-packages.show',
            { workPackageId: this.workPackageId },
          );
          return true;
        },
      });

      if (this.allowSplitScreenActions) {
        items.unshift({
          disabled: false,
          icon: 'icon-view-split',
          class: 'detailsViewMenuItem',
          href: this.$state.href(
            `${splitViewRoute(this.$state)}.tabs`,
            { workPackageId: this.workPackageId, tabIdentifier: 'overview' },
          ),
          linkText: I18n.t('js.button_open_details'),
          onClick: ($event:JQuery.TriggeredEvent) => {
            if (isClickedWithModifier($event)) {
              return false;
            }

            this.$state.go(
              `${splitViewRoute(this.$state)}.tabs`,
              { workPackageId: this.workPackageId, tabIdentifier: 'overview' },
            );
            return true;
          },
        });
      }
    }

    return items;
  }
}