From b3574eb2742701d1c25f6fa591bd43e14acd32c3 Mon Sep 17 00:00:00 2001 From: Jonas Sulzer <jonas@violoncello.ch> Date: Wed, 2 Sep 2020 16:26:20 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20composer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - button in appnavigation to create post -> open modal - composer in timeline not sticky - reply opens composer in modal - use vuex to store composer reply and modal open state Signed-off-by: Jonas Sulzer <jonas@violoncello.ch> --- src/App.vue | 30 +++++++++++++++++++ src/components/Composer.vue | 19 ++++-------- src/components/TimelinePost.vue | 3 +- src/store/composer.js | 51 +++++++++++++++++++++++++++++++++ src/store/index.js | 2 ++ 5 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 src/store/composer.js diff --git a/src/App.vue b/src/App.vue index 78ac28d1..db008de4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,10 +1,21 @@ <template> <Content v-if="!serverData.setup" app-name="social" :class="{public: serverData.public}"> <AppNavigation v-if="!serverData.public"> + <AppNavigationNew + :text="t('social', 'New post')" + :disabled="false" + button-id="new-socialpost-button" + button-class="icon-add" + @click="showNewPostModal" /> <AppNavigationItem v-for="item in menu.items" :key="item.key" :to="item.to" :title="item.title" :icon="item.icon" :exact="true" /> </AppNavigation> <AppContent> + <Modal v-if="newPostModal" @close="closeNewPostModal"> + <div class="composer-in-modal"> + <Composer /> + </div> + </Modal> <div v-if="serverData.isAdmin && !serverData.checks.success" class="setup social__wrapper"> <h3 v-if="!serverData.checks.checks.wellknown"> {{ t('social', '.well-known/webfinger isn\'t properly set up!') }} @@ -85,6 +96,10 @@ text-decoration: underline; } + .composer-in-modal { + width: 600px; + max-width: 100%; + } </style> <script> @@ -92,8 +107,11 @@ import Content from '@nextcloud/vue/dist/Components/Content' import AppContent from '@nextcloud/vue/dist/Components/AppContent' import AppNavigation from '@nextcloud/vue/dist/Components/AppNavigation' import AppNavigationItem from '@nextcloud/vue/dist/Components/AppNavigationItem' +import AppNavigationNew from '@nextcloud/vue/dist/Components/AppNavigationNew' +import Modal from '@nextcloud/vue/dist/Components/Modal' import axios from '@nextcloud/axios' +import Composer from './components/Composer.vue' import Search from './components/Search.vue' import currentuserMixin from './mixins/currentUserMixin' import { loadState } from '@nextcloud/initial-state' @@ -106,6 +124,9 @@ export default { AppContent, AppNavigation, AppNavigationItem, + AppNavigationNew, + Modal, + Composer, Search }, mixins: [currentuserMixin], @@ -118,6 +139,9 @@ export default { } }, computed: { + newPostModal() { + return this.$store.getters.getComposerModalState + }, timeline: function() { return this.$store.getters.getTimeline }, @@ -242,6 +266,12 @@ export default { if (data.source === 'timeline.direct' && timeline === 'direct') { this.$store.dispatch('addToTimeline', [data.payload]) } + }, + showNewPostModal() { + this.$store.commit('openComposerModal') + }, + closeNewPostModal() { + this.$store.commit('closeComposerModal') } } } diff --git a/src/components/Composer.vue b/src/components/Composer.vue index f14a93a3..8bd0a7ad 100644 --- a/src/components/Composer.vue +++ b/src/components/Composer.vue @@ -36,10 +36,10 @@ </div> <div v-if="replyTo" class="reply-to"> <p> - <span>In reply to</span> + <span>{{ t('social', 'In reply to') }}</span> <actor-avatar :actor="replyTo.actor_info" :size="16" /> <strong>{{ replyTo.actor_info.account }}</strong> - <a class="icon-close" @click="replyTo=null" /> + <a class="icon-close" @click="$store.commit('removeComposerReply')" /> </p> <div class="reply-to-preview"> {{ replyTo.content }} @@ -111,10 +111,6 @@ .new-post { padding: 10px; background-color: var(--color-main-background); - position: sticky; - top: 47px; - z-index: 100; - margin-bottom: 10px; } .new-post-author { @@ -422,7 +418,6 @@ export default { postAttachments: [], // The toot's attachments canType: true, search: '', - replyTo: null, tributeOptions: { spaceSelectsMatch: true, collection: [ @@ -517,6 +512,9 @@ export default { } }, computed: { + replyTo() { + return this.$store.getters.getReply + }, currentVisibilityIconClass() { return this.visibilityIconClass(this.type) }, @@ -607,11 +605,6 @@ export default { ] } }, - mounted() { - this.$root.$on('composer-reply', (data) => { - this.replyTo = data - }) - }, methods: { AddAttachment() { // TODO: handle (or prevent) mulitple files @@ -815,7 +808,7 @@ export default { this.loading = true this.$store.dispatch('post', postData).then((response) => { this.loading = false - this.replyTo = null + this.$store.commit('removeComposerReply') this.post = '' this.$refs.composerInput.innerText = this.post this.postAttachments = [] diff --git a/src/components/TimelinePost.vue b/src/components/TimelinePost.vue index d3ff2318..19a6a99c 100644 --- a/src/components/TimelinePost.vue +++ b/src/components/TimelinePost.vue @@ -144,7 +144,8 @@ export default { return actorInfo.name !== '' ? actorInfo.name : actorInfo.preferredUsername }, reply() { - this.$root.$emit('composer-reply', this.item) + this.$store.commit('setComposerReply', this.item) + this.$store.commit('openComposerModal') }, boost() { let params = { diff --git a/src/store/composer.js b/src/store/composer.js new file mode 100644 index 00000000..67f65e8e --- /dev/null +++ b/src/store/composer.js @@ -0,0 +1,51 @@ +/* + * @copyright Copyright (c) 2030 Jonas Sulzer <jonas@violoncello.ch> + * + * @author Jonas Sulzer <jonas@violoncello.ch> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +const state = { + openInModal: false, + reply: null +} +const mutations = { + openComposerModal(state) { + state.openInModal = true + }, + closeComposerModal(state) { + state.openInModal = false + }, + setComposerReply(state, data) { + state.reply = data + }, + removeComposerReply(state) { + state.reply = null + } +} +const getters = { + getComposerModalState(state) { + return state.openInModal + }, + getReply(state) { + return state.reply + } +} +const actions = {} + +export default { state, mutations, getters, actions } diff --git a/src/store/index.js b/src/store/index.js index 4076bff7..716de29d 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -24,6 +24,7 @@ import Vue from 'vue' import Vuex from 'vuex' import timeline from './timeline' +import composer from './composer' import account from './account' import settings from './settings' @@ -34,6 +35,7 @@ const debug = process.env.NODE_ENV !== 'production' export default new Vuex.Store({ modules: { timeline, + composer, account, settings },