diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue index 552b22dd3e..56dbfd5bdf 100644 --- a/src/client/components/notifications.vue +++ b/src/client/components/notifications.vue @@ -1,5 +1,5 @@ <template> -<div class="mfcuwfyp"> +<div class="mfcuwfyp _noGap_"> <XList class="notifications" :items="items" v-slot="{ item: notification }"> <XNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :note="notification.note" @update:note="noteUpdated(notification.note, $event)" :key="notification.id"/> <XNotification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/> diff --git a/src/client/ui/chat/date-separated-list.vue b/src/client/ui/chat/date-separated-list.vue index 0120bf33f7..b209330656 100644 --- a/src/client/ui/chat/date-separated-list.vue +++ b/src/client/ui/chat/date-separated-list.vue @@ -9,11 +9,6 @@ export default defineComponent({ type: Array, required: true, }, - direction: { - type: String, - required: false, - default: 'down' - }, reversed: { type: Boolean, required: false, @@ -37,14 +32,10 @@ export default defineComponent({ }); } - return h(!this.reversed ? TransitionGroup : 'div', !this.reversed ? { + return h(TransitionGroup, { class: 'hmjzthxl', - name: 'list', + name: this.reversed ? 'list-reversed' : 'list', tag: 'div', - 'data-direction': this.direction, - 'data-reversed': this.reversed ? 'true' : 'false', - } : { - class: 'hmjzthxl', }, this.items.map((item, i) => { const el = this.$slots.default({ item: item @@ -95,23 +86,20 @@ export default defineComponent({ > .list-move { transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1); } - > .list-enter-active { transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1); } - - &[data-direction="up"] { - > .list-enter-from { - opacity: 0; - transform: translateY(64px); - } + > .list-enter-from { + opacity: 0; + transform: translateY(-64px); } - &[data-direction="down"] { - > .list-enter-from { - opacity: 0; - transform: translateY(-64px); - } + > .list-reversed-enter-active, > .list-reversed-leave-active { + transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1); + } + > .list-reversed-enter-from { + opacity: 0; + transform: translateY(64px); } } </style> diff --git a/src/client/ui/chat/index.vue b/src/client/ui/chat/index.vue index 21caf3bd1b..0d18d428ce 100644 --- a/src/client/ui/chat/index.vue +++ b/src/client/ui/chat/index.vue @@ -36,7 +36,7 @@ </div> </div> <div class="container" v-if="followedChannels"> - <div class="header">{{ $ts.channel }}<button class="_button add"><Fa :icon="faPlus"/></button></div> + <div class="header">{{ $ts.channel }} ({{ $ts.following }})<button class="_button add"><Fa :icon="faPlus"/></button></div> <div class="body"> <MkA v-for="channel in followedChannels" :key="channel.id" :to="`/channels/${ channel.id }`" class="item" :class="{ active: tl === `channel:${ channel.id }`, read: !channel.hasUnreadNote }"><Fa :icon="faSatelliteDish" class="icon"/>{{ channel.name }}</MkA> </div> @@ -81,14 +81,17 @@ </template> <template v-else-if="tl.startsWith('channel:')"> <Fa :icon="faSatelliteDish" class="icon"/> - <div class="title" v-if="currentChannel">{{ currentChannel.name }}</div> - <div class="description" v-if="currentChannel">{{ currentChannel.description }}</div> + <div class="title" v-if="currentChannel">{{ currentChannel.name }}<div class="description">{{ currentChannel.description }}</div></div> </template> </div> <div class="right"> - <XHeaderClock/> - <button class="_button search"> + <XHeaderClock class="clock"/> + <button class="_button button follow" v-if="tl.startsWith('channel:') && currentChannel" :class="{ followed: currentChannel.isFollowing }" @click="toggleChannelFollow"> + <Fa v-if="currentChannel.isFollowing" :icon="faStar"/> + <Fa v-else :icon="farStar"/> + </button> + <button class="_button button search" @click="search"> <Fa :icon="faSearch"/> </button> </div> @@ -111,8 +114,8 @@ <script lang="ts"> import { defineComponent, defineAsyncComponent } from 'vue'; -import { faLayerGroup, faBars, faHome, faCircle, faWindowMaximize, faColumns, faPencilAlt, faShareAlt, faSatelliteDish, faListUl, faSatellite, faCog, faSearch, faPlus } from '@fortawesome/free-solid-svg-icons'; -import { faBell } from '@fortawesome/free-regular-svg-icons'; +import { faLayerGroup, faBars, faHome, faCircle, faWindowMaximize, faColumns, faPencilAlt, faShareAlt, faSatelliteDish, faListUl, faSatellite, faCog, faSearch, faPlus, faStar } from '@fortawesome/free-solid-svg-icons'; +import { faBell, faStar as farStar } from '@fortawesome/free-regular-svg-icons'; import { instanceName } from '@/config'; import XSidebar from '@/components/sidebar.vue'; import XCommon from '../_common_/common.vue'; @@ -121,7 +124,9 @@ import XTimeline from './timeline.vue'; import XPostForm from './post-form.vue'; import XHeaderClock from './header-clock.vue'; import * as os from '@/os'; +import { router } from '@/router'; import { sidebarDef } from '@/sidebar'; +import { search } from '@/scripts/search'; export default defineComponent({ components: { @@ -167,11 +172,17 @@ export default defineComponent({ featuredChannels: null, currentChannel: null, menuDef: sidebarDef, - faLayerGroup, faBars, faBell, faHome, faCircle, faPencilAlt, faShareAlt, faSatelliteDish, faListUl, faSatellite, faCog, faSearch, faPlus, + faLayerGroup, faBars, faBell, faHome, faCircle, faPencilAlt, faShareAlt, faSatelliteDish, faListUl, faSatellite, faCog, faSearch, faPlus, faStar, farStar, }; }, created() { + router.beforeEach((to, from) => { + this.$refs.side.navigate(to.fullPath); + // search?q=foo のようなクエリを受け取れるようにするため、return falseはできない + //return false; + }); + os.api('users/lists/list').then(lists => { this.lists = lists; }); @@ -206,10 +217,28 @@ export default defineComponent({ os.post(); }, + search() { + search(); + }, + top() { window.scroll({ top: 0, behavior: 'smooth' }); }, + async toggleChannelFollow() { + if (this.currentChannel.isFollowing) { + await os.apiWithDialog('channels/unfollow', { + channelId: this.currentChannel.id + }); + this.currentChannel.isFollowing = false; + } else { + await os.apiWithDialog('channels/follow', { + channelId: this.currentChannel.id + }); + this.currentChannel.isFollowing = true; + } + }, + onTransition() { if (window._scroll) window._scroll(); }, @@ -397,13 +426,13 @@ export default defineComponent({ height: $header-height; padding: $padding; box-sizing: border-box; - line-height: ($header-height - ($padding * 2)); background-color: var(--panel); border-bottom: solid 1px var(--divider); user-select: none; > .left { display: flex; + align-items: center; flex: 1; min-width: 0; @@ -416,44 +445,49 @@ export default defineComponent({ opacity: 0.6; } - > .title, > .description { + > .title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; - } - - > .title { - flex-shrink: 0; font-weight: bold; - } - > .description { - margin-left: 16px; - opacity: 0.7; - font-size: 0.9em; + > .description { + opacity: 0.6; + font-size: 0.8em; + font-weight: noraml; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } } } > .right { display: flex; + align-items: center; min-width: 0; margin-left: auto; padding-left: 8px; - > .search { + > .clock { + margin-right: 8px; + } + + > .button { height: ($header-height - ($padding * 2)); width: ($header-height - ($padding * 2)); - padding: 10px; box-sizing: border-box; - margin-left: 8px; position: relative; - line-height: initial; border-radius: 5px; &:hover { background: rgba(0, 0, 0, 0.05); } + + &.follow.followed { + color: var(--accent); + } } } } diff --git a/src/client/ui/chat/note.vue b/src/client/ui/chat/note.vue index 8b31e65e68..11d795d93e 100644 --- a/src/client/ui/chat/note.vue +++ b/src/client/ui/chat/note.vue @@ -1074,8 +1074,13 @@ export default defineComponent({ } } + > .files { + max-width: 500px; + } + > .url-preview { margin-top: 8px; + max-width: 500px; } > .poll { diff --git a/src/client/ui/chat/post-form.vue b/src/client/ui/chat/post-form.vue index f03e7ebb99..833c4b4069 100644 --- a/src/client/ui/chat/post-form.vue +++ b/src/client/ui/chat/post-form.vue @@ -108,7 +108,7 @@ export default defineComponent({ autofocus: { type: Boolean, required: false, - default: true + default: false }, }, diff --git a/src/client/ui/chat/timeline.vue b/src/client/ui/chat/timeline.vue index d73a40dab5..e9ca449b36 100644 --- a/src/client/ui/chat/timeline.vue +++ b/src/client/ui/chat/timeline.vue @@ -192,5 +192,8 @@ export default defineComponent({ <style lang="scss" scoped> .dbiokgaf { padding: 16px 0; + + // TODO: これはノート追加アニメーションによるスクロール発生を抑えるために必要だが、position stickyが効かなくなるので、両者を両立させる良い方法を考える + overflow: hidden; } </style>