From dde809825d52a91694a9f11713e694dde19b0d34 Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Sun, 14 Oct 2018 19:44:30 +0900 Subject: [PATCH] Resolve #2900 --- locales/ja-JP.yml | 5 + .../app/desktop/views/components/settings.vue | 12 + .../desktop/views/components/ui.sidebar.vue | 237 ++++++++++++++++++ .../app/desktop/views/components/ui.vue | 25 +- src/client/app/store.ts | 1 + 5 files changed, 277 insertions(+), 3 deletions(-) create mode 100644 src/client/app/desktop/views/components/ui.sidebar.vue diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 560dca665..68503425a 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -883,6 +883,11 @@ desktop/views/components/settings.vue: task-manager: "タスクマネージャ" third-parties: "サードパーティ" + navbar-position: "ナビゲーションバーの位置" + navbar-position-top: "上" + navbar-position-left: "左" + navbar-position-right: "右" + desktop/views/components/settings.2fa.vue: intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" detail: "詳細..." diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue index 1cb8d4d4c..778302a7a 100644 --- a/src/client/app/desktop/views/components/settings.vue +++ b/src/client/app/desktop/views/components/settings.vue @@ -88,6 +88,13 @@ <ui-switch v-model="disableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch> <ui-switch v-model="games_reversi_showBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch> <ui-switch v-model="games_reversi_useContrastStones">%i18n:common.use-contrast-reversi-stones%</ui-switch> + + <section> + <header>%i18n:@navbar-position%</header> + <ui-radio v-model="navbar" value="top">%i18n:@navbar-position-top%</ui-radio> + <ui-radio v-model="navbar" value="left">%i18n:@navbar-position-left%</ui-radio> + <ui-radio v-model="navbar" value="right">%i18n:@navbar-position-right%</ui-radio> + </section> </section> <section class="web" v-show="page == 'web'"> @@ -293,6 +300,11 @@ export default Vue.extend({ set(value) { this.$store.commit('device/set', { key: 'darkmode', value }); } }, + navbar: { + get() { return this.$store.state.device.navbar; }, + set(value) { this.$store.commit('device/set', { key: 'navbar', value }); } + }, + enableSounds: { get() { return this.$store.state.device.enableSounds; }, set(value) { this.$store.commit('device/set', { key: 'enableSounds', value }); } diff --git a/src/client/app/desktop/views/components/ui.sidebar.vue b/src/client/app/desktop/views/components/ui.sidebar.vue new file mode 100644 index 000000000..1455421a9 --- /dev/null +++ b/src/client/app/desktop/views/components/ui.sidebar.vue @@ -0,0 +1,237 @@ +<template> +<div class="header" :class="$store.state.device.navbar"> + <div class="post"> + <button @click="post" title="%i18n:@post%">%fa:pencil-alt%</button> + </div> + + <div class="nav" v-if="$store.getters.isSignedIn"> + <div class="home" :class="{ active: $route.name == 'index' }" @click="goToTop"> + <router-link to="/">%fa:home%</router-link> + </div> + <div class="deck" :class="{ active: $route.name == 'deck' }" @click="goToTop"> + <router-link to="/deck">%fa:columns%</router-link> + </div> + <div class="messaging"> + <a @click="messaging">%fa:comments%<template v-if="hasUnreadMessagingMessage">%fa:circle%</template></a> + </div> + <div class="game"> + <a @click="game">%fa:gamepad%<template v-if="hasGameInvitations">%fa:circle%</template></a> + </div> + </div> + + <div class="nav bottom" v-if="$store.getters.isSignedIn"> + <div> + <a @click="drive">%fa:cloud%</a> + </div> + <div> + <router-link to="/i/favorites">%fa:star%</router-link> + </div> + <div v-if="($store.state.i.isLocked || $store.state.i.carefulBot)"> + <a @click="followRequests">%fa:envelope R%<i v-if="$store.state.i.pendingReceivedFollowRequestsCount">{{ $store.state.i.pendingReceivedFollowRequestsCount }}</i></a> + </div> + <div> + <a @click="settings">%fa:cog%</a> + </div> + <div> + <a @click="dark"><template v-if="$store.state.device.darkmode">%fa:moon%</template><template v-else>%fa:R moon%</template></a> + </div> + <div class="signout"> + <a @click="signout">%fa:power-off%</a> + </div> + </div> + + <div class="account"> + <router-link :to="`/@${ $store.state.i.username }`"> + <mk-avatar class="avatar" :user="$store.state.i"/> + </router-link> + </div> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import MkUserListsWindow from './user-lists-window.vue'; +import MkFollowRequestsWindow from './received-follow-requests-window.vue'; +import MkSettingsWindow from './settings-window.vue'; +import MkDriveWindow from './drive-window.vue'; +import MkMessagingWindow from './messaging-window.vue'; +import MkGameWindow from './game-window.vue'; + +export default Vue.extend({ + data() { + return { + hasGameInvitations: false, + connection: null + }; + }, + + computed: { + hasUnreadMessagingMessage(): boolean { + return this.$store.getters.isSignedIn && this.$store.state.i.hasUnreadMessagingMessage; + } + }, + + mounted() { + if (this.$store.getters.isSignedIn) { + this.connection = (this as any).os.stream.useSharedConnection('main'); + + this.connection.on('reversiInvited', this.onReversiInvited); + this.connection.on('reversi_no_invites', this.onReversiNoInvites); + } + }, + + beforeDestroy() { + if (this.$store.getters.isSignedIn) { + this.connection.dispose(); + } + }, + + methods: { + onReversiInvited() { + this.hasGameInvitations = true; + }, + + onReversiNoInvites() { + this.hasGameInvitations = false; + }, + + messaging() { + (this as any).os.new(MkMessagingWindow); + }, + + game() { + (this as any).os.new(MkGameWindow); + }, + + post() { + (this as any).apis.post(); + }, + + drive() { + (this as any).os.new(MkDriveWindow); + }, + + list() { + const w = (this as any).os.new(MkUserListsWindow); + w.$once('choosen', list => { + this.$router.push(`i/lists/${ list.id }`); + }); + }, + + followRequests() { + (this as any).os.new(MkFollowRequestsWindow); + }, + + settings() { + (this as any).os.new(MkSettingsWindow); + }, + + signout() { + (this as any).os.signout(); + }, + + dark() { + this.$store.commit('device/set', { + key: 'darkmode', + value: !this.$store.state.device.darkmode + }); + }, + + goToTop() { + window.scrollTo({ + top: 0, + behavior: 'smooth' + }); + } + } +}); +</script> + +<style lang="stylus" scoped> +.header + $width = 68px + + position fixed + top 0 + z-index 1000 + width $width + height 100% + background var(--desktopHeaderBg) + box-shadow var(--shadow) + + &.left + left 0 + + &.right + right 0 + + > .nav + > * + > * + display block + width $width + line-height 56px + text-align center + font-size 18px + color var(--desktopHeaderFg) + + &:hover + color var(--desktopHeaderHoverFg) + text-decoration none + + > .nav.bottom + position absolute + bottom 64px + left 0 + + > .post + width $width + height $width + padding 10px + + > button + display inline-block + margin 0 + padding 0 10px + height 100% + width 100% + font-size 1.2em + font-weight normal + text-decoration none + color var(--primaryForeground) + background var(--primary) !important + outline none + border none + border-radius 4px + transition background 0.1s ease + cursor pointer + + * + pointer-events none + + &:hover + background var(--primaryLighten10) !important + + &:active + background var(--primaryDarken10) !important + transition background 0s ease + + > .account + position absolute + bottom 0 + left 0 + width $width + height $width + padding 12px + + > * + display block + width 100% + height 100% + + > .avatar + pointer-events none + width 100% + height 100% + +</style> diff --git a/src/client/app/desktop/views/components/ui.vue b/src/client/app/desktop/views/components/ui.vue index 2d1e98447..2aa259ab1 100644 --- a/src/client/app/desktop/views/components/ui.vue +++ b/src/client/app/desktop/views/components/ui.vue @@ -1,8 +1,9 @@ <template> <div class="mk-ui" v-hotkey.global="keymap"> <div class="bg" v-if="$store.getters.isSignedIn && $store.state.i.wallpaperUrl" :style="style"></div> - <x-header class="header" v-show="!zenMode" ref="header"/> - <div class="content"> + <x-header class="header" v-if="navbar == 'top'" v-show="!zenMode" ref="header"/> + <x-sidebar class="sidebar" v-if="navbar != 'top'" ref="sidebar"/> + <div class="content" :class="[{ sidebar: navbar != 'top' }, navbar]"> <slot></slot> </div> <mk-stream-indicator v-if="$store.getters.isSignedIn"/> @@ -12,10 +13,12 @@ <script lang="ts"> import Vue from 'vue'; import XHeader from './ui.header.vue'; +import XSidebar from './ui.sidebar.vue'; export default Vue.extend({ components: { - XHeader + XHeader, + XSidebar }, data() { @@ -25,6 +28,10 @@ export default Vue.extend({ }, computed: { + navbar(): string { + return this.$store.state.device.navbar; + }, + style(): any { if (!this.$store.getters.isSignedIn || this.$store.state.i.wallpaperUrl == null) return {}; return { @@ -45,6 +52,12 @@ export default Vue.extend({ watch: { '$store.state.uiHeaderHeight'() { this.$el.style.paddingTop = this.$store.state.uiHeaderHeight + 'px'; + }, + + navbar() { + if (this.navbar != 'top') { + this.$store.commit('setUiHeaderHeight', 0); + } } }, @@ -87,4 +100,10 @@ export default Vue.extend({ @media (max-width 1000px) display none + > .content.sidebar.left + padding-left 64px + + > .content.sidebar.right + padding-right 64px + </style> diff --git a/src/client/app/store.ts b/src/client/app/store.ts index 545261225..63365f7fb 100644 --- a/src/client/app/store.ts +++ b/src/client/app/store.ts @@ -56,6 +56,7 @@ const defaultDeviceSettings = { loadRawImages: false, alwaysShowNsfw: false, postStyle: 'standard', + navbar: 'top', mobileNotificationPosition: 'bottom' };