mr_item_model.ts 3.0 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/gitlab_new_service';
7
import { handleError } from '../../log';
8
import { UserFriendlyError } from '../../errors/user_friendly_error';
9
import { GitLabCommentThread } from '../../review/gitlab_comment_thread';
10

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

16
export class MrItemModel extends ItemModel {
17
  constructor(readonly mr: RestIssuable, readonly workspace: GitLabWorkspace) {
18 19 20 21 22 23 24 25 26
    super();
  }

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

  async getChildren(): Promise<vscode.TreeItem[]> {
    const description = new vscode.TreeItem('Description');
    description.iconPath = new vscode.ThemeIcon('note');
    description.command = {
      command: PROGRAMMATIC_COMMANDS.SHOW_RICH_CONTENT,
38
      arguments: [this.mr, this.workspace.uri],
39 40
      title: 'Show MR',
    };
41 42 43
    try {
      await this.getMrDiscussions();
    } catch (e) {
44 45
      handleError(
        new UserFriendlyError(
46 47 48
          `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.`,
49 50 51
          e,
        ),
      );
52
    }
53 54 55 56
    const changedFiles = await this.getChangedFiles();
    return [description, ...changedFiles];
  }

57 58 59 60 61 62
  private async getMrDiscussions(): Promise<void> {
    const commentController = vscode.comments.createCommentController(
      this.mr.references.full,
      this.mr.title,
    );

63
    const gitlabService = await createGitLabNewService(this.workspace.uri);
64

65 66 67 68
    const discussions = await gitlabService.getDiscussions({
      issuable: this.mr,
      includePosition: true,
    });
69
    const discussionsOnDiff = discussions.filter(isTextDiffDiscussion);
70 71
    const threads = discussionsOnDiff.map(discussion => {
      return GitLabCommentThread.createThread({
72
        commentController,
73
        workspaceFolder: this.workspace.uri,
74 75
        gitlabProjectId: this.mr.project_id,
        discussion,
76
        gitlabService,
77
      });
78 79 80 81
    });
    this.setDisposableChildren([...threads, commentController]);
  }

82
  private async getChangedFiles(): Promise<vscode.TreeItem[]> {
83
    const gitlabService = await createGitLabNewService(this.workspace.uri);
84
    const mrVersion = await gitlabService.getMrDiff(this.mr);
85
    return mrVersion.diffs.map(d => new ChangedFileItem(this.mr, mrVersion, d, this.workspace));
86 87
  }
}