webview_controller.js 4.9 KB
Newer Older
1 2
const fs = require('fs');
const path = require('path');
3
const crypto = require('crypto');
4
const vscode = require('vscode');
F
Fatih Acet 已提交
5
const gitLabService = require('./gitlab_service');
6
const { createGitLabNewService } = require('./service_factory');
7
const { logError } = require('./log');
8
const { getInstanceUrl } = require('./utils/get_instance_url');
9 10 11

let context = null;

F
Fatih Acet 已提交
12
const addDeps = ctx => {
13
  context = ctx;
F
Fatih Acet 已提交
14
};
15

J
John Hampton 已提交
16
const getResources = panel => {
17 18 19 20 21
  const paths = {
    appScriptUri: 'src/webview/dist/js/app.js',
    vendorUri: 'src/webview/dist/js/chunk-vendors.js',
    styleUri: 'src/webview/dist/css/app.css',
    devScriptUri: 'src/webview/dist/app.js',
F
Fatih Acet 已提交
22
  };
23

F
Fatih Acet 已提交
24
  Object.keys(paths).forEach(key => {
25 26
    const uri = vscode.Uri.file(path.join(context.extensionPath, paths[key]));

J
John Hampton 已提交
27
    paths[key] = panel.webview.asWebviewUri(uri);
28 29 30
  });

  return paths;
F
Fatih Acet 已提交
31
};
32

F
Fatih Acet 已提交
33
const getIndexPath = () => {
34
  const isDev = process.env.NODE_ENV === 'development';
F
Fatih Acet 已提交
35 36

  return isDev ? 'src/webview/public/dev.html' : 'src/webview/public/index.html';
F
Fatih Acet 已提交
37
};
F
Fatih Acet 已提交
38

J
John Hampton 已提交
39 40
const replaceResources = panel => {
  const { appScriptUri, vendorUri, styleUri, devScriptUri } = getResources(panel);
41
  const nonce = crypto.randomBytes(20).toString('hex');
F
Fatih Acet 已提交
42 43

  return fs
F
Fatih Acet 已提交
44
    .readFileSync(path.join(context.extensionPath, getIndexPath()), 'UTF-8')
45
    .replace(/{{nonce}}/gm, nonce)
F
Fatih Acet 已提交
46 47 48 49
    .replace('{{styleUri}}', styleUri)
    .replace('{{vendorUri}}', vendorUri)
    .replace('{{appScriptUri}}', appScriptUri)
    .replace('{{devScriptUri}}', devScriptUri);
F
Fatih Acet 已提交
50
};
F
Fatih Acet 已提交
51

F
Fatih Acet 已提交
52
const createPanel = issuable => {
F
Fatih Acet 已提交
53
  const title = `${issuable.title.slice(0, 20)}...`;
F
Fatih Acet 已提交
54

F
Fatih Acet 已提交
55 56
  return vscode.window.createWebviewPanel('glWorkflow', title, vscode.ViewColumn.One, {
    enableScripts: true,
F
Fatih Acet 已提交
57
    localResourceRoots: [vscode.Uri.file(path.join(context.extensionPath, 'src'))],
58
    retainContextWhenHidden: true,
F
Fatih Acet 已提交
59
  });
F
Fatih Acet 已提交
60
};
F
Fatih Acet 已提交
61

62
const createMessageHandler = (panel, issuable, workspaceFolder) => async message => {
63
  const instanceUrl = await getInstanceUrl(workspaceFolder);
64 65 66 67 68 69 70 71 72
  if (message.command === 'renderMarkdown') {
    const alteredMarkdown = message.markdown.replace(
      /\(\/.*(\/-)?\/merge_requests\//,
      '(/-/merge_requests/',
    );
    let rendered = await gitLabService.renderMarkdown(alteredMarkdown, workspaceFolder);
    rendered = (rendered || '')
      .replace(/ src=".*" alt/gim, ' alt')
      .replace(/" data-src/gim, '" src')
73
      .replace(/ href="\//gim, ` href="${instanceUrl}/`)
74 75 76 77 78 79 80 81 82
      .replace(/\/master\/-\/merge_requests\//gim, '/-/merge_requests/');

    panel.webview.postMessage({
      type: 'markdownRendered',
      ref: message.ref,
      object: message.object,
      markdown: rendered,
    });
  }
83

84
  if (message.command === 'saveNote') {
85 86 87
    const gitlabNewService = await createGitLabNewService(workspaceFolder);
    try {
      await gitlabNewService.createNote(issuable, message.note, message.replyId);
88 89 90 91 92 93
      const discussionsAndLabels = await gitlabNewService.getDiscussionsAndLabelEvents(issuable);
      panel.webview.postMessage({
        type: 'issuableFetch',
        issuable,
        discussions: discussionsAndLabels,
      });
94
      panel.webview.postMessage({ type: 'noteSaved' });
95 96
    } catch (e) {
      logError(e);
97
      panel.webview.postMessage({ type: 'noteSaved', status: false });
98
    }
99 100
  }
};
101

102
async function initPanelIfActive(panel, issuable, workspaceFolder) {
103
  if (!panel.active) return;
F
Fatih Acet 已提交
104

105 106 107 108 109
  const appReadyPromise = new Promise(resolve => {
    const sub = panel.webview.onDidReceiveMessage(async message => {
      if (message.command === 'appReady') {
        sub.dispose();
        resolve();
F
Fatih Acet 已提交
110
      }
111
    });
F
Fatih Acet 已提交
112
  });
113

114 115
  const gitlabNewService = await createGitLabNewService(workspaceFolder);
  const discussionsAndLabels = await gitlabNewService.getDiscussionsAndLabelEvents(issuable);
116
  await appReadyPromise;
117
  panel.webview.postMessage({ type: 'issuableFetch', issuable, discussions: discussionsAndLabels });
118 119
}

120 121 122 123 124 125 126 127 128 129 130 131 132
const getIconPathForIssuable = issuable => {
  const getIconUri = (shade, file) =>
    vscode.Uri.file(path.join(context.extensionPath, 'src', 'assets', 'images', shade, file));
  const lightIssueIcon = getIconUri('light', 'issues.svg');
  const lightMrIcon = getIconUri('light', 'merge_requests.svg');
  const darkIssueIcon = getIconUri('dark', 'issues.svg');
  const darkMrIcon = getIconUri('dark', 'merge_requests.svg');
  const isMr = issuable.squash_commit_sha !== undefined;
  return isMr
    ? { light: lightMrIcon, dark: darkMrIcon }
    : { light: lightIssueIcon, dark: darkIssueIcon };
};

133
async function create(issuable, workspaceFolder) {
134
  const panel = createPanel(issuable);
J
John Hampton 已提交
135
  const html = replaceResources(panel);
136
  panel.webview.html = html;
137
  panel.iconPath = getIconPathForIssuable(issuable);
P
philip.nicolai 已提交
138

139
  initPanelIfActive(panel, issuable, workspaceFolder);
140
  panel.onDidChangeViewState(() => {
141
    initPanelIfActive(panel, issuable, workspaceFolder);
142 143
  });

144 145
  panel.webview.onDidReceiveMessage(createMessageHandler(panel, issuable, workspaceFolder));
  return panel;
146 147
}

148 149
exports.addDeps = addDeps;
exports.create = create;