未验证 提交 3036d279 编写于 作者: F Fatih Acet

Implement discussion components.

上级 03852446
......@@ -386,6 +386,29 @@ async function fetchDiscussions(issuable) {
return discussions;
}
// TODO: Remove project fetch
async function renderMarkdown(markdown) {
let rendered = { html: markdown };
const [ major ] = version.split('.');
if (parseInt(major, 10) < 11) {
return markdown;
}
try {
const project = await fetchCurrentProject();
rendered = await fetch('/markdown', 'POST', {
text: markdown,
project: project.path_with_namespace,
gfm: 'true', // Needs to be a string for the API
});
} catch(e) {
return markdown;
}
return rendered.html;
};
exports.fetchUser = fetchUser;
exports.fetchIssuesAssignedToMe = fetchIssuesAssignedToMe;
exports.fetchIssuesCreatedByMe = fetchIssuesCreatedByMe;
......@@ -404,3 +427,4 @@ exports.createSnippet = createSnippet;
exports.validateCIConfig = validateCIConfig;
exports.fetchVersion = fetchVersion;
exports.fetchDiscussions = fetchDiscussions;
exports.renderMarkdown = renderMarkdown;
......@@ -2,6 +2,8 @@
import IssuableDetails from './components/IssuableDetails';
import IssuableDiscussions from './components/IssuableDiscussions';
const vscode = acquireVsCodeApi();
export default {
name: 'app',
data() {
......@@ -15,12 +17,36 @@ export default {
IssuableDetails,
IssuableDiscussions,
},
computed: {
notesById() {
const notes = {}
this.discussions.forEach((d) => {
d.notes.forEach((n) => {
notes[n.id] = n;
});
});
notes[this.issuable.id] = this.issuable;
return notes;
},
},
created() {
window.vsCodeApi = vscode;
this.isLoading = true;
window.addEventListener('message', event => {
this.issuable = event.data.issuable;
this.isLoading = false;
if (event.data.type === 'issuableFetch') {
this.issuable = event.data.issuable;
this.discussions = event.data.discussions;
this.isLoading = false;
} else if (event.data.type === 'markdownRendered') {
const { ref, key, markdown } = event.data;
const note = this.notesById[ref] || {};
note[key] = markdown;
note.markdownRenderedOnServer = true;
}
});
},
}
......@@ -35,6 +61,3 @@ export default {
</template>
</div>
</template>
<style lang="scss">
</style>
<script>
import UserAvatar from './UserAvatar';
const moment = require('moment');
const md = require('markdown-it')().use(require('markdown-it-checkbox'));
......@@ -23,6 +24,10 @@ export default {
return states[this.issuable.state] || '';
},
description() {
if (this.issuable.markdownRenderedOnServer) {
return this.issuable.description;
}
const path = `${this.issuable.web_url.split('/issues/')[0]}/uploads/`;
const normalized = this.issuable.description.replace(/\/uploads/gm, path);
......@@ -32,7 +37,15 @@ export default {
return moment(this.issuable.created_at).fromNow();
},
},
}
mounted() {
window.vsCodeApi.postMessage({
command: 'renderMarkdown',
markdown: this.issuable.description,
ref: this.issuable.id,
key: 'description',
});
},
};
</script>
<template>
......@@ -51,7 +64,7 @@ export default {
{{ createdAgo }}
</span>
by
<user-avatar :issuable="issuable" />
<user-avatar :user="issuable.author" />
<a :href="issuable.web_url">
View in GitLab
</a>
......
<script>
const moment = require('moment');
const md = require('markdown-it')().use(require('markdown-it-checkbox'));
import Note from './Note';
import SystemNote from './SystemNote';
export default {
props: {
......@@ -9,13 +9,27 @@ export default {
required: true,
},
},
}
methods: {
getComponentName(discussion) {
if (discussion.individual_note) {
if (discussion.notes[0].system) {
return SystemNote;
}
return Note;
}
},
},
};
</script>
<template>
<div class="issuable-discussions">
<ul>
</ul>
<component
v-for="discussion in discussions"
:key="discussion.id"
:is="getComponentName(discussion)"
:noteable="discussion"
/>
</div>
</template>
<script>
import UserAvatar from './UserAvatar';
import NoteBody from './NoteBody'
const moment = require('moment');
export default {
name: 'Note',
props: {
noteable: {
type: Object,
required: true,
}
},
components: {
UserAvatar,
NoteBody,
},
computed: {
note() {
return this.noteable.notes[0];
},
author() {
return this.note.author;
},
createdAgo() {
return moment(this.note.created_at).fromNow();
},
},
};
</script>
<template>
<div class="note">
<div class="note-header">
<user-avatar :user="author" />
{{ createdAgo }}
</div>
<note-body :note="note" />
</div>
</template>
<script>
const md = require('markdown-it')().use(require('markdown-it-checkbox'));
export default {
name: 'NoteBody',
props: {
note: {
type: Object,
required: true,
},
},
computed: {
renderedNoteBody() {
return this.note.markdownRenderedOnServer ? this.note.body : md.render(this.note.body);
},
},
mounted() {
window.vsCodeApi.postMessage({
command: 'renderMarkdown',
markdown: this.note.body,
ref: this.note.id,
key: 'body',
});
},
};
</script>
<template>
<div class="note-body">
<div class="body" v-html="renderedNoteBody"></div>
</div>
</template>
<script>
import Note from './Note';
export default {
props: {
noteable: {
type: Object,
required: true,
},
},
components: {
Note,
},
};
</script>
<template>
<div class="note system-note">
<note :noteable="noteable" />
</div>
</template>
<script>
export default {
props: {
issuable: {
user: {
type: Object,
required: true,
},
......@@ -11,17 +11,9 @@ export default {
<template>
<span>
<img
:src="issuable.author.avatar_url"
class="avatar"
/>
<img :src="user.avatar_url" class="avatar" />
<span class="author">
<a
:href="issuable.author.web_url"
target="_blank"
>
{{ issuable.author.name }}
</a>
<a :href="user.web_url" target="_blank">{{ user.name }}</a>
</span>
</span>
</template>
......
......@@ -10,11 +10,13 @@ const addDeps = (ctx) => {
}
const getNonce = () => {
let text = "";
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
......@@ -50,18 +52,31 @@ async function create(issuable) {
});
const { appScriptUri, vendorUri, styleUri, devScriptUri } = getResources();
let html = fs.readFileSync(path.join(context.extensionPath, getIndexPath()), 'UTF-8');
html = html.replace(/{{nonce}}/gm, getNonce())
.replace('{{styleUri}}', styleUri)
.replace('{{vendorUri}}', vendorUri)
.replace('{{appScriptUri}}', appScriptUri)
.replace('{{devScriptUri}}', devScriptUri);
let html = fs
.readFileSync(path.join(context.extensionPath, getIndexPath()), 'UTF-8')
.replace(/{{nonce}}/gm, getNonce())
.replace('{{styleUri}}', styleUri)
.replace('{{vendorUri}}', vendorUri)
.replace('{{appScriptUri}}', appScriptUri)
.replace('{{devScriptUri}}', devScriptUri);
const discussions = await gitLabService.fetchDiscussions(issuable);
panel.webview.html = html;
panel.webview.postMessage({ issuable, discussions });
panel.webview.postMessage({ type: 'issuableFetch', issuable, discussions });
panel.webview.onDidReceiveMessage(async (message) => {
if (message.command === 'renderMarkdown') {
let rendered = await gitLabService.renderMarkdown(message.markdown);
rendered = rendered.replace(/ src=".*" alt/gim, ' alt').replace(/" data-src/gim, '" src');
panel.webview.postMessage({
type: 'markdownRendered',
ref: message.ref,
key: message.key,
markdown: rendered,
});
}
});
}
exports.addDeps = addDeps;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册