From d1e9e74cb87463cb4b5d9c0f3a127e374a1a1e32 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 19 Feb 2020 06:16:49 +0900 Subject: [PATCH] Resolve #5978 --- locales/ja-JP.yml | 1 + src/client/app.vue | 43 +++++++++------ src/client/components/notifications.vue | 70 +++++++++++++------------ src/client/pages/notifications.vue | 42 +++++++++++++++ src/client/pages/settings/index.vue | 8 ++- src/client/router.ts | 1 + src/client/store.ts | 1 + 7 files changed, 114 insertions(+), 52 deletions(-) create mode 100644 src/client/pages/notifications.vue diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 117a8f997b..5fcd562720 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -423,6 +423,7 @@ serverLogs: "サーバーログ" deleteAll: "全て削除" showFixedPostForm: "タイムライン上部に投稿フォームを表示する" newNoteRecived: "新しいノートがあります" +useNotificationsPopup: "通知一覧をポップアップで表示" _ago: unknown: "謎" diff --git a/src/client/app.vue b/src/client/app.vue index 9b0745d195..a3290486a7 100644 --- a/src/client/app.vue +++ b/src/client/app.vue @@ -50,21 +50,27 @@ <router-link class="item index" active-class="active" to="/" exact v-else> <fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span> </router-link> - <button class="item _button notifications" @click="notificationsOpen = !notificationsOpen" ref="notificationButton" v-if="$store.getters.isSignedIn"> - <fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span> - <i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i> - </button> - <router-link class="item" active-class="active" to="/my/messaging" v-if="$store.getters.isSignedIn"> - <fa :icon="faComments" fixed-width/><span class="text">{{ $t('messaging') }}</span> - <i v-if="$store.state.i.hasUnreadMessagingMessage"><fa :icon="faCircle"/></i> - </router-link> - <router-link class="item" active-class="active" to="/my/drive" v-if="$store.getters.isSignedIn"> - <fa :icon="faCloud" fixed-width/><span class="text">{{ $t('drive') }}</span> - </router-link> - <router-link class="item" active-class="active" to="/my/follow-requests" v-if="$store.getters.isSignedIn && $store.state.i.isLocked"> - <fa :icon="faUserClock" fixed-width/><span class="text">{{ $t('followRequests') }}</span> - <i v-if="$store.state.i.hasPendingReceivedFollowRequest"><fa :icon="faCircle"/></i> - </router-link> + <template v-if="$store.getters.isSignedIn"> + <button class="item _button notifications" @click="notificationsOpen = !notificationsOpen" ref="notificationButton" v-if="$store.state.device.useNotificationsPopup"> + <fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span> + <i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i> + </button> + <router-link class="item notifications" active-class="active" to="/my/notifications" ref="notificationButton" v-else> + <fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span> + <i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i> + </router-link> + <router-link class="item" active-class="active" to="/my/messaging"> + <fa :icon="faComments" fixed-width/><span class="text">{{ $t('messaging') }}</span> + <i v-if="$store.state.i.hasUnreadMessagingMessage"><fa :icon="faCircle"/></i> + </router-link> + <router-link class="item" active-class="active" to="/my/drive"> + <fa :icon="faCloud" fixed-width/><span class="text">{{ $t('drive') }}</span> + </router-link> + <router-link class="item" active-class="active" to="/my/follow-requests" v-if="$store.state.i.isLocked"> + <fa :icon="faUserClock" fixed-width/><span class="text">{{ $t('followRequests') }}</span> + <i v-if="$store.state.i.hasPendingReceivedFollowRequest"><fa :icon="faCircle"/></i> + </router-link> + </template> <div class="divider"></div> <router-link class="item" active-class="active" to="/featured"> <fa :icon="faFireAlt" fixed-width/><span class="text">{{ $t('featured') }}</span> @@ -143,7 +149,8 @@ <button class="button nav _button" @click="showNav = true" ref="navButton"><fa :icon="faBars"/><i v-if="$store.getters.isSignedIn && ($store.state.i.hasUnreadSpecifiedNotes || $store.state.i.hasPendingReceivedFollowRequest || $store.state.i.hasUnreadMessagingMessage || $store.state.i.hasUnreadAnnouncement)"><fa :icon="faCircle"/></i></button> <button v-if="$route.name === 'index'" class="button home _button" @click="top()"><fa :icon="faHome"/></button> <button v-else class="button home _button" @click="$router.push('/')"><fa :icon="faHome"/></button> - <button v-if="$store.getters.isSignedIn" class="button notifications _button" @click="notificationsOpen = !notificationsOpen" ref="notificationButton2"><fa :icon="notificationsOpen ? faTimes : faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button> + <button v-if="$store.getters.isSignedIn && $store.state.device.useNotificationsPopup" class="button notifications _button" @click="notificationsOpen = !notificationsOpen" ref="notificationButton2"><fa :icon="notificationsOpen ? faTimes : faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button> + <button v-if="$store.getters.isSignedIn && !$store.state.device.useNotificationsPopup" class="button notifications _button" @click="$router.push('/my/notifications')" ref="notificationButton2"><fa :icon="faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button> <button v-if="$store.getters.isSignedIn" class="button post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button> </div> @@ -1206,15 +1213,17 @@ export default Vue.extend({ left: 0; right: 0; margin: 0 auto; + padding: 8px 8px 0 8px; z-index: 10001; width: 350px; height: 400px; + box-sizing: border-box; background: var(--vocsgcxy); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); border-radius: 6px; box-shadow: 0 3px 12px rgba(27, 31, 35, 0.15); - overflow: hidden; + overflow: auto; @media (max-width: 800px) { width: 320px; diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue index ff2fc4af8a..3c2eb1bd51 100644 --- a/src/client/components/notifications.vue +++ b/src/client/components/notifications.vue @@ -1,19 +1,17 @@ <template> -<div class="mk-notifications"> - <div class="contents"> - <x-list class="notifications" :items="items" v-slot="{ item: notification, i }"> - <x-notification :notification="notification" :with-time="true" :full="true" class="notification" :key="notification.id"/> - </x-list> +<div class="mk-notifications" :class="{ page }"> + <x-list class="notifications" :items="items" v-slot="{ item: notification }"> + <x-notification :notification="notification" :with-time="true" :full="true" class="notification" :class="{ _panel: page }" :key="notification.id"/> + </x-list> - <button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching"> - <template v-if="!moreFetching">{{ $t('loadMore') }}</template> - <template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template> - </button> + <button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching"> + <template v-if="!moreFetching">{{ $t('loadMore') }}</template> + <template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template> + </button> - <p class="empty" v-if="empty">{{ $t('noNotifications') }}</p> + <p class="empty" v-if="empty">{{ $t('noNotifications') }}</p> - <mk-error v-if="error" @retry="init()"/> - </div> + <mk-error v-if="error" @retry="init()"/> </div> </template> @@ -42,7 +40,7 @@ export default Vue.extend({ type: String, required: false }, - wide: { + page: { type: Boolean, required: false, default: false @@ -93,11 +91,15 @@ export default Vue.extend({ <style lang="scss" scoped> .mk-notifications { - > .contents { - overflow: auto; - height: 100%; - padding: 8px 8px 0 8px; + &.page { + > .notifications { + > ::v-deep * { + margin-bottom: var(--margin); + } + } + } + &:not(.page) { > .notifications { > ::v-deep * { margin-bottom: 8px; @@ -109,28 +111,28 @@ export default Vue.extend({ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } } + } - > .more { - display: block; - width: 100%; - padding: 16px; + > .more { + display: block; + width: 100%; + padding: 16px; - > [data-icon] { - margin-right: 4px; - } + > [data-icon] { + margin-right: 4px; } + } - > .empty { - margin: 0; - padding: 16px; - text-align: center; - color: var(--fg); - } + > .empty { + margin: 0; + padding: 16px; + text-align: center; + color: var(--fg); + } - > .placeholder { - padding: 32px; - opacity: 0.3; - } + > .placeholder { + padding: 32px; + opacity: 0.3; } } </style> diff --git a/src/client/pages/notifications.vue b/src/client/pages/notifications.vue new file mode 100644 index 0000000000..49e67bc8f7 --- /dev/null +++ b/src/client/pages/notifications.vue @@ -0,0 +1,42 @@ +<template> +<div> + <portal to="icon"><fa :icon="faBell"/></portal> + <portal to="title">{{ $t('notifications') }}</portal> + <x-notifications @before="before" @after="after" page/> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import { faBell } from '@fortawesome/free-solid-svg-icons'; +import Progress from '../scripts/loading'; +import XNotifications from '../components/notifications.vue'; + +export default Vue.extend({ + metaInfo() { + return { + title: this.$t('notifications') as string + }; + }, + + components: { + XNotifications + }, + + data() { + return { + faBell + }; + }, + + methods: { + before() { + Progress.start(); + }, + + after() { + Progress.done(); + } + } +}); +</script> diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index 986ade8ffc..b3ef4d17b9 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -20,7 +20,8 @@ {{ $t('useOsNativeEmojis') }} <template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template> </mk-switch> - <mk-switch v-model="showFixedPostForm">{{ $t('showFixedPostForm') }}</mk-switch> + <mk-switch v-model="showFixedPostForm">{{ $t('showFixedPostForm') }}</mk-switch> + <mk-switch v-model="useNotificationsPopup">{{ $t('useNotificationsPopup') }}</mk-switch> </div> <div class="_content"> <mk-select v-model="lang"> @@ -111,6 +112,11 @@ export default Vue.extend({ get() { return this.$store.state.device.showFixedPostForm; }, set(value) { this.$store.commit('device/set', { key: 'showFixedPostForm', value }); } }, + + useNotificationsPopup: { + get() { return this.$store.state.device.useNotificationsPopup; }, + set(value) { this.$store.commit('device/set', { key: 'useNotificationsPopup', value }); } + }, }, watch: { diff --git a/src/client/router.ts b/src/client/router.ts index 0a856d580d..86ef4c7056 100644 --- a/src/client/router.ts +++ b/src/client/router.ts @@ -27,6 +27,7 @@ export const router = new VueRouter({ { path: '/explore', component: page('explore') }, { path: '/explore/tags/:tag', props: true, component: page('explore') }, { path: '/search', component: page('search') }, + { path: '/my/notifications', component: page('notifications') }, { path: '/my/favorites', component: page('favorites') }, { path: '/my/messages', component: page('messages') }, { path: '/my/mentions', component: page('mentions') }, diff --git a/src/client/store.ts b/src/client/store.ts index 28c995132e..2d84b7b318 100644 --- a/src/client/store.ts +++ b/src/client/store.ts @@ -40,6 +40,7 @@ const defaultDeviceSettings = { animatedMfm: true, imageNewTab: false, showFixedPostForm: false, + useNotificationsPopup: true, userData: {}, };