feat(frontend): stream description editing and proper md rendering and sanitisation

This commit is contained in:
Dimitrie Stefanescu
2020-12-15 20:36:54 +00:00
parent e658905dc5
commit 2201f6e177
4 changed files with 142 additions and 14 deletions
+10
View File
@@ -6215,6 +6215,11 @@
"domelementtype": "1"
}
},
"dompurify": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.4.tgz",
"integrity": "sha512-jE21SelIgWrGKoXGfGPA524Zt1IJFBnktwfFMHDlEYRx5FZOdc+4eEH9mkA6PuhExrq3HVpJnY8hMYUzAMl0OA=="
},
"domutils": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
@@ -9663,6 +9668,11 @@
"object-visit": "^1.0.0"
}
},
"marked": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/marked/-/marked-1.2.6.tgz",
"integrity": "sha512-7vVuSEZ8g/HH3hK/BH/+7u/NJj7x9VY4EHzujLDcqAQLiOUeFJYAsfSAyoWtR17lKrx7b08qyIno4lffwrzTaA=="
},
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+2
View File
@@ -13,7 +13,9 @@
"@vuejs-community/vue-filter-date-parse": "^1.1.6",
"core-js": "^3.8.1",
"crypto-random-string": "^3.3.0",
"dompurify": "^2.2.4",
"lodash.debounce": "^4.0.8",
"marked": "^1.2.6",
"v-tooltip": "^2.0.3",
"vue": "^2.6.12",
"vue-apollo": "^3.0.5",
@@ -0,0 +1,85 @@
<template>
<v-card class="pa-4" color="background2">
<v-card-title class="subtitle-1">Edit Description</v-card-title>
<v-card-text>
<v-row>
<v-col sm="12" md="6">
<p>Markdown is enabled.</p>
<v-textarea
v-model="innerStreamDescription"
auto-grow
filled
rows="10"
style="font-size: 12px; line-height: 10px;"
></v-textarea>
</v-col>
<v-col sm="12" md="6">
<p>Preview</p>
<div class="marked-preview" v-html="compiledMarkdown"></div>
</v-col>
</v-row>
</v-card-text>
<v-card-actions>
<!-- <v-spacer></v-spacer> -->
<v-btn @click.native="save">Save & Close</v-btn>
</v-card-actions>
</v-card>
</template>
<script>
import marked from "marked"
import DOMPurify from "dompurify"
import gql from "graphql-tag"
export default {
props: {
id: String,
description: {
type: String,
default: null
}
},
data: () => ({
innerStreamDescription: null
}),
computed: {
compiledMarkdown() {
if (!this.innerStreamDescription) return ""
let md = marked(this.innerStreamDescription)
return DOMPurify.sanitize(md)
},
streamDescription: {
get() {
return this.innerStreamDescription
},
set(value) {
this.innerStreamDescription = value
}
}
},
mounted() {
this.innerStreamDescription = this.description
},
methods: {
async save() {
try {
this.$apollo.mutate({
mutation: gql`
mutation editDescription($input: StreamUpdateInput!) {
streamUpdate(stream: $input)
}
`,
variables: {
input: {
id: this.id,
description: this.innerStreamDescription
}
}
})
this.$emit("close", this.innerStreamDescription)
} catch (e) {
console.log(e)
}
}
}
}
</script>
+45 -14
View File
@@ -11,10 +11,29 @@
<v-row>
<v-col cols="12" sm="12" lg="12">
<v-card rounded="lg" class="pa-4" elevation="0" color="background2">
<v-card-title>Description</v-card-title>
<v-card-text>
{{ stream.description }}
<v-card-title v-if="!stream.description">
Description
</v-card-title>
<v-card-text v-if="!stream.description">
No description provided.
</v-card-text>
<v-card-text
v-if="stream.description"
class="marked-preview"
v-html="compiledStreamDescription"
></v-card-text>
<v-card-actions>
<v-btn small @click="dialogDescription = true">
Edit Description
</v-btn>
<v-dialog v-model="dialogDescription">
<stream-description-dialog
:id="stream.id"
:description="stream.description"
@close="closeDescription"
/>
</v-dialog>
</v-card-actions>
</v-card>
</v-col>
@@ -23,7 +42,7 @@
<v-col cols="12" sm="12" lg="12">
<v-card rounded="lg" class="pa-4" elevation="0" color="background2">
<v-card-title>
<v-icon class='mr-2'>mdi-source-branch</v-icon>
<v-icon class="mr-2">mdi-source-branch</v-icon>
Branches
</v-card-title>
<v-card-text>
@@ -41,14 +60,8 @@
<v-list-item-title>
<b>{{ item.name }}</b>
&nbsp;
<v-chip outlined>
<v-avatar
size="10"
left
class="primary white--text"
>
{{ item.commits.totalCount }}
</v-avatar>
<v-chip small>
{{ item.commits.totalCount }}
commits
</v-chip>
</v-list-item-title>
@@ -63,7 +76,7 @@
</v-list-item>
</template>
</v-list>
<v-btn block @click="newBranch">Create a new branch</v-btn>
<v-btn small @click="newBranch">new branch</v-btn>
<branch-dialog
ref="branchDialog"
:branches="branches"
@@ -101,18 +114,27 @@
</v-container>
</template>
<script>
import marked from "marked"
import DOMPurify from "dompurify"
import gql from "graphql-tag"
import SidebarStream from "../components/SidebarStream"
import BranchDialog from "../components/dialogs/BranchDialog"
import StreamDescriptionDialog from "../components/dialogs/StreamDescriptionDialog"
import ListItemCommit from "../components/ListItemCommit"
import streamQuery from "../graphql/stream.gql"
import streamCommitsQuery from "../graphql/streamCommits.gql"
export default {
name: "Stream",
components: { SidebarStream, BranchDialog, ListItemCommit },
components: {
SidebarStream,
BranchDialog,
ListItemCommit,
StreamDescriptionDialog
},
data() {
return {
dialogDescription: false,
selectedBranch: 0,
stream: {
id: null,
@@ -153,6 +175,11 @@ export default {
}
},
computed: {
compiledStreamDescription() {
if (!this.stream.description) return ""
let md = marked(this.stream.description)
return DOMPurify.sanitize(md)
},
branches() {
//reverse without changing original array
return this.stream.branches.items.slice().reverse()
@@ -162,6 +189,10 @@ export default {
this.$matomo && this.$matomo.trackPageView("streams/single")
},
methods: {
closeDescription(newDescription) {
this.stream.description = newDescription
this.dialogDescription = false
},
newBranch() {
this.$refs.branchDialog.open().then((dialog) => {
if (!dialog.result) return