diff options
| -rw-r--r-- | .quasar/entry.js | 4 | ||||
| -rw-r--r-- | quasar.conf.js | 3 | ||||
| -rw-r--r-- | src/components/CommentVoter.vue | 59 | ||||
| -rw-r--r-- | src/components/UploadVoter.vue | 11 | ||||
| -rw-r--r-- | src/pages/UploadDetail.vue | 83 | ||||
| -rw-r--r-- | src/pages/UploadList.vue | 9 |
6 files changed, 150 insertions, 19 deletions
diff --git a/.quasar/entry.js b/.quasar/entry.js index e50aef7..4e35988 100644 --- a/.quasar/entry.js +++ b/.quasar/entry.js @@ -40,12 +40,12 @@ import 'src/css/app.styl' import Vue from 'vue' -import Quasar, {QAlert,QLayout,QLayoutHeader,QLayoutDrawer,QPageContainer,QPage,QToolbar,QToolbarTitle,QBtn,QIcon,QList,QListHeader,QInput,QItem,QItemMain,QItemSide,QTable,QTabs,QRouteTab,QInnerLoading,QField,QStep,QStepper,QStepperNavigation,QTd,QSpinner,QCard,QCardTitle,QCardMain,QCardMedia,QCardSeparator,QCardActions,QParallax,QCheckbox,QChip,Ripple,LocalStorage,Dialog} from 'quasar' +import Quasar, {QAlert,QLayout,QLayoutHeader,QLayoutDrawer,QPageContainer,QPage,QToolbar,QToolbarTitle,QBtn,QIcon,QList,QListHeader,QInput,QItem,QItemMain,QItemSide,QTable,QTabs,QRouteTab,QInnerLoading,QSpinnerComment,QField,QStep,QStepper,QStepperNavigation,QTd,QSpinner,QCard,QCardTitle,QCardMain,QCardMedia,QCardSeparator,QCardActions,QParallax,QCheckbox,QChip,QChatMessage,Ripple,LocalStorage,Dialog,Notify} from 'quasar' Vue.config.productionTip = false import App from 'src/App' -Vue.use(Quasar, {components: {QAlert,QLayout,QLayoutHeader,QLayoutDrawer,QPageContainer,QPage,QToolbar,QToolbarTitle,QBtn,QIcon,QList,QListHeader,QInput,QItem,QItemMain,QItemSide,QTable,QTabs,QRouteTab,QInnerLoading,QField,QStep,QStepper,QStepperNavigation,QTd,QSpinner,QCard,QCardTitle,QCardMain,QCardMedia,QCardSeparator,QCardActions,QParallax,QCheckbox,QChip},directives: {Ripple},plugins: {LocalStorage,Dialog}}) +Vue.use(Quasar, {components: {QAlert,QLayout,QLayoutHeader,QLayoutDrawer,QPageContainer,QPage,QToolbar,QToolbarTitle,QBtn,QIcon,QList,QListHeader,QInput,QItem,QItemMain,QItemSide,QTable,QTabs,QRouteTab,QInnerLoading,QSpinnerComment,QField,QStep,QStepper,QStepperNavigation,QTd,QSpinner,QCard,QCardTitle,QCardMain,QCardMedia,QCardSeparator,QCardActions,QParallax,QCheckbox,QChip,QChatMessage},directives: {Ripple},plugins: {LocalStorage,Dialog,Notify}}) diff --git a/quasar.conf.js b/quasar.conf.js index 8705458..610ef26 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -76,6 +76,7 @@ module.exports = function (ctx) { 'QTabs', 'QRouteTab', 'QInnerLoading', + 'QSpinnerComment', 'QField', 'QStep', 'QStepper', @@ -91,6 +92,7 @@ module.exports = function (ctx) { 'QParallax', 'QCheckbox', 'QChip', + 'QChatMessage', ], directives: [ 'Ripple' @@ -98,6 +100,7 @@ module.exports = function (ctx) { plugins: [ 'LocalStorage', 'Dialog', + 'Notify', ] }, // animations: 'all' --- includes all animations diff --git a/src/components/CommentVoter.vue b/src/components/CommentVoter.vue new file mode 100644 index 0000000..6f2ce0d --- /dev/null +++ b/src/components/CommentVoter.vue @@ -0,0 +1,59 @@ +<template> + <div> + <div class="flex row items-center group" v-if="$store.getters['user/loggedIn']"> + <span style="margin: 0 1rem"> + <i class="fa" + :class="{'fa-caret-up text-positive': comment.voting.sum > 0, 'fa-caret-down text-negative': comment.voting.sum < 0, 'fa-sort text-dark': comment.voting.sum === 0}"> + </i> + {{ comment.voting.sum }} + </span> + <span v-if="comment.author.username !== $store.state.user.decodedToken.username && !myVote"> + <q-btn color="negative" round small icon="fa-thumbs-down" @click="vote(-1)"></q-btn> + <q-btn color="positive" round small icon="fa-thumbs-up" @click="vote(1)"></q-btn> + </span> + <span v-if="myVote">You voted <i :class="`fas fa-thumbs-${myVote.impact === 1 ? 'up text-positive' : 'down text-negative'}`"></i></span> + </div> + <div class="group" v-else> + <i class="fa" + :class="{'fa-caret-up text-positive': comment.voting.sum > 0, 'fa-caret-down text-negative': comment.voting.sum < 0, 'fa-sort text-dark': comment.voting.sum === 0}"> + </i> + {{ comment.voting.sum }} votes + <em>Please log in to vote</em> + </div> + </div> +</template> + +<script> + import { + QBtn, + } from 'quasar' + + export default { + props: { + comment: Object, + }, + components: { + QBtn, + }, + data () { + return {} + }, + computed: { + myVote () { + if (!this.comment || !this.comment.voting || !Array.isArray(this.comment.voting.votes) || this.comment.voting.votes.length === 0) { + return undefined + } + return this.comment.voting.votes[0] + }, + }, + methods: { + vote (impact) { + let that = this + this.$http.post(`/comments/${this.comment.upload}/comments/${this.comment._id}/vote`, {impact: impact}).then(response => that.$emit('voted')) + }, + }, + } +</script> + +<style lang="styl" type="text/stylus" scoped> +</style> diff --git a/src/components/UploadVoter.vue b/src/components/UploadVoter.vue index 65a5453..563bc79 100644 --- a/src/components/UploadVoter.vue +++ b/src/components/UploadVoter.vue @@ -7,10 +7,11 @@ </i> {{ upload.voting.sum }} </h5> - <span v-if="upload.author.username !== $store.state.user.decodedToken.username"> + <span v-if="upload.author.username !== $store.state.user.decodedToken.username && !myVote"> <q-btn color="negative" round small icon="fa-thumbs-down" @click="vote(-1)"></q-btn> <q-btn color="positive" round small icon="fa-thumbs-up" @click="vote(1)"></q-btn> </span> + <span v-if="myVote">You voted <i :class="`fas fa-thumbs-${myVote.impact === 1 ? 'up text-positive' : 'down text-negative'}`"></i></span> </div> <div class="group" v-else> <i class="fa" @@ -37,6 +38,14 @@ data () { return {} }, + computed: { + myVote () { + if (!this.upload || !this.upload.voting || !Array.isArray(this.upload.voting.votes) || this.upload.voting.votes.length === 0) { + return undefined + } + return this.upload.voting.votes[0] + }, + }, methods: { vote (impact) { let that = this diff --git a/src/pages/UploadDetail.vue b/src/pages/UploadDetail.vue index b2042b6..9b6f73a 100644 --- a/src/pages/UploadDetail.vue +++ b/src/pages/UploadDetail.vue @@ -21,14 +21,6 @@ <span slot="right" class="text-light" style="margin-left: 3rem">updated {{ upload.updatedAt | moment("from") }}</span> </q-card-title> <q-card-separator /> - <q-card-main> - <p class="text-faded"> - {{ upload.description }} - </p> - <div class="group"> - <q-chip v-for="tag of upload.tags" :key="tag">{{ tag }}</q-chip> - </div> - </q-card-main> <q-card-actions> <q-btn @click="openInOpenclonk" color="positive" outline> Install mod with OpenClonk @@ -41,6 +33,46 @@ Delete mod </q-btn> </q-card-actions> + <q-card-main> + <p class="text-faded"> + {{ upload.description }} + </p> + <div class="group"> + <q-chip v-for="tag of upload.tags" :key="tag">{{ tag }}</q-chip> + </div> + </q-card-main> + <q-card-title>Comments</q-card-title> + <q-card-main> + <div class="flex row no-wrap items-center" + v-for="comment of comments.comments" + :key="comment._id"> + <q-chat-message + style="max-width: 90%" + :name="(comment.author || {}).username" + :text="comment.body.split('\n').filter(el => el.trim() !== '')" + :stamp="$moment(comment.createdAt).format('LLLL')" /> + <comment-voter :comment="comment"></comment-voter> + </div> + <div> + <q-input + v-model="comment" + type="textarea" + placeholder="Write a comment" + :max-height="100" + @keyup.enter="sendComment" + :after="[ + { + icon: 'fa-paper-plane', + content: true, + handler () { + sendComment() + } + } + ]" + /> + <q-inner-loading :visible="commentSaving"><q-spinner-comment></q-spinner-comment></q-inner-loading> + </div> + </q-card-main> <q-card-media v-if="upload.pic" overlay-position="bottom"> <q-card-title slot="overlay"> Voting @@ -139,10 +171,12 @@ <script> import { openURL } from 'quasar' import UploadVoter from 'components/UploadVoter' + import CommentVoter from 'components/CommentVoter' import FileSaver from 'file-saver' export default { components: { + CommentVoter, UploadVoter, }, computed: { @@ -164,6 +198,9 @@ return { upload: null, downloadProgresses: {}, + comments: [], + comment: '', + commentSaving: false, } }, methods: { @@ -171,10 +208,23 @@ let that = this this.$http.get(`/uploads/${this.routeId}`).then(response => { that.upload = response.data + that.loadComments() }).catch((error) => { if (error.response.status === 404) { that.$router.push({name: 'upload-list'}) } + else { + console.error(error) + } + }) + }, + loadComments () { + let that = this + this.$http.get(`/uploads/${this.routeId}/comments`).then(response => { + that.comments = response.data + }).catch((error) => { + that.$q.notify('Failed loading comments') + console.error(error) }) }, deleteUpload (upload) { @@ -218,6 +268,23 @@ }, openInOpenclonk () { openURL(`openclonk://installmod/${this.upload._id}`) + }, + sendComment () { + let that = this + this.commentSaving = true + if (this.comment.length > 0) { + this.$http.post(`/uploads/${this.upload._id}/comments`, {body: this.comment}).then(() => { + that.comment = '' + that.$q.notify({color: 'positive', icon: 'fa-check', message: 'Comment sent'}) + that.loadComments() + this.commentSaving = false + }).catch((error) => { + that.$q.notify('Error sending your comment') + that.loadComments() + this.commentSaving = false + console.error(error) + }) + } } } } diff --git a/src/pages/UploadList.vue b/src/pages/UploadList.vue index a37d8a1..b90fce1 100644 --- a/src/pages/UploadList.vue +++ b/src/pages/UploadList.vue @@ -20,15 +20,8 @@ small outline icon="fa-info-circle" - label="Details" + label="Show" @click="$router.push({name: 'upload-detail', params: {uploadId: props.row._id}})" /> - <q-btn color="negative" - outline - small - icon="fa-trash" - label="Delete" - v-if="$route.params.uploadId !== props.row._id && props.row.author.username === $store.state.user.decodedToken.username" - @click="deleteUpload(props.row)" /> </q-td> <q-td slot='body-cell-voting' slot-scope="props" :props='props'> {{ props.value.sum }} <i class="fa" :class="{'fa-caret-up text-positive': props.value.sum > 0, 'fa-caret-down text-negative': props.value.sum < 0, 'fa-sort text-dark': props.value.sum === 0}"></i> |
