mr_item_model.ts 3.3 KB
Newer Older
1 2 3 4 5
import * as vscode from 'vscode';
import { PROGRAMMATIC_COMMANDS } from '../../command_names';
import { createGitLabNewService } from '../../service_factory';
import { ChangedFileItem } from './changed_file_item';
import { ItemModel } from './item_model';
6
import { GqlDiscussion, GqlTextDiffDiscussion } from '../../gitlab/graphql/get_discussions';
7
import { handleError } from '../../log';
8
import { UserFriendlyError } from '../../errors/user_friendly_error';
9
import { GitLabCommentThread } from '../../review/gitlab_comment_thread';
10
import { CommentingRangeProvider } from '../../review/commenting_range_provider';
11
import { WrappedRepository } from '../../git/wrapped_repository';
12

13
const isTextDiffDiscussion = (discussion: GqlDiscussion): discussion is GqlTextDiffDiscussion => {
14 15 16 17
  const firstNote = discussion.notes.nodes[0];
  return firstNote?.position?.positionType === 'text';
};

18
export class MrItemModel extends ItemModel {
19
  constructor(readonly mr: RestIssuable, readonly repository: WrappedRepository) {
20 21 22 23 24 25 26 27 28
    super();
  }

  getTreeItem(): vscode.TreeItem {
    const { iid, title, author } = this.mr;
    const item = new vscode.TreeItem(
      `!${iid} · ${title}`,
      vscode.TreeItemCollapsibleState.Collapsed,
    );
29 30 31
    if (author.avatar_url) {
      item.iconPath = vscode.Uri.parse(author.avatar_url);
    }
32 33 34 35
    return item;
  }

  async getChildren(): Promise<vscode.TreeItem[]> {
36 37 38
    const overview = new vscode.TreeItem('Overview');
    overview.iconPath = new vscode.ThemeIcon('note');
    overview.command = {
39
      command: PROGRAMMATIC_COMMANDS.SHOW_RICH_CONTENT,
40
      arguments: [this.mr, this.repository.rootFsPath],
41
      title: 'Show MR Overview',
42
    };
43
    const gitlabService = await createGitLabNewService(this.repository.rootFsPath);
44
    const mrVersion = await gitlabService.getMrDiff(this.mr);
45
    try {
46
      await this.initializeMrDiscussions(mrVersion);
47
    } catch (e) {
48 49
      handleError(
        new UserFriendlyError(
50 51 52
          `The extension failed to preload discussions on the MR diff.
            It's possible that you've encountered
            https://gitlab.com/gitlab-org/gitlab/-/issues/298827.`,
53 54 55
          e,
        ),
      );
56
    }
57 58

    const changedFiles = mrVersion.diffs.map(
59
      d => new ChangedFileItem(this.mr, mrVersion, d, this.repository.rootFsPath),
60
    );
61
    return [overview, ...changedFiles];
62 63
  }

64
  private async initializeMrDiscussions(mrVersion: RestMrVersion): Promise<void> {
65 66 67 68
    const commentController = vscode.comments.createCommentController(
      this.mr.references.full,
      this.mr.title,
    );
69
    const gitlabService = await createGitLabNewService(this.repository.rootFsPath);
70

71 72 73 74
    if (await gitlabService.canUserCommentOnMr(this.mr)) {
      commentController.commentingRangeProvider = new CommentingRangeProvider(this.mr, mrVersion);
    }

75 76 77
    const discussions = await gitlabService.getDiscussions({
      issuable: this.mr,
    });
78
    const discussionsOnDiff = discussions.filter(isTextDiffDiscussion);
79 80
    const threads = discussionsOnDiff.map(discussion => {
      return GitLabCommentThread.createThread({
81
        commentController,
82
        repositoryRoot: this.repository.rootFsPath,
83
        mr: this.mr,
84
        discussion,
85
        gitlabService,
86
      });
87 88 89
    });
    this.setDisposableChildren([...threads, commentController]);
  }
90
}