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

Implement discussion components.

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