summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2024-07-11 18:41:04 +0900
committerGitHub <noreply@github.com>2024-07-11 18:41:04 +0900
commit6b876da44af0b7e5dd553676d230f68995df148d (patch)
treeede6a6fc6d510fb80b0bf7781194f40f7855ca83
parentリリースPRがないときにrelease-edit-with-push.ymlがfailして見栄... (diff)
downloadmisskey-6b876da44af0b7e5dd553676d230f68995df148d.tar.gz
misskey-6b876da44af0b7e5dd553676d230f68995df148d.tar.bz2
misskey-6b876da44af0b7e5dd553676d230f68995df148d.zip
enhance(frontend): ウェルカムタイムラインのデザインを調整 (#14156)
* enhance(frontend): 非ログイン時のハイライトTLのデザイン調整 * Update Changelog * fix cw handling * ホバーしてたらスクロールを止めるように * fix * lint
-rw-r--r--CHANGELOG.md1
-rw-r--r--packages/frontend/src/pages/welcome.timeline.note.vue109
-rw-r--r--packages/frontend/src/pages/welcome.timeline.vue98
3 files changed, 162 insertions, 46 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aec4e1868c..259420a6c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
### Client
- Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
+- Enhance: 非ログイン時のハイライトTLのデザインを改善
- Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
- Fix: リバーシの対局を正しく共有できないことがある問題を修正
diff --git a/packages/frontend/src/pages/welcome.timeline.note.vue b/packages/frontend/src/pages/welcome.timeline.note.vue
new file mode 100644
index 0000000000..f385938343
--- /dev/null
+++ b/packages/frontend/src/pages/welcome.timeline.note.vue
@@ -0,0 +1,109 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :key="note.id" :class="$style.note">
+ <div class="_panel _gaps_s" :class="$style.content">
+ <div v-if="note.cw != null" :class="$style.richcontent">
+ <div><Mfm :text="note.cw" :author="note.user"/></div>
+ <MkCwButton v-model="showContent" :text="note.text" :renote="note.renote" :files="note.files" :poll="note.poll" style="margin: 4px 0;"/>
+ <div v-if="showContent">
+ <MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
+ <Mfm v-if="note.text" :text="note.text" :author="note.user"/>
+ <MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
+ </div>
+ </div>
+ <div v-else ref="noteTextEl" :class="[$style.text, { [$style.collapsed]: shouldCollapse }]">
+ <MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
+ <Mfm v-if="note.text" :text="note.text" :author="note.user"/>
+ <MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
+ </div>
+ <div v-if="note.files && note.files.length > 0" :class="$style.richcontent">
+ <MkMediaList :mediaList="note.files.slice(0, 4)"/>
+ </div>
+ <div v-if="note.poll">
+ <MkPoll :noteId="note.id" :poll="note.poll" :readOnly="true"/>
+ </div>
+ <div v-if="note.reactionCount > 0" :class="$style.reactions">
+ <MkReactionsViewer :note="note" :maxNumber="16"/>
+ </div>
+ </div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { ref, shallowRef, onUpdated, onMounted } from 'vue';
+import * as Misskey from 'misskey-js';
+import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
+import MkMediaList from '@/components/MkMediaList.vue';
+import MkPoll from '@/components/MkPoll.vue';
+import MkCwButton from '@/components/MkCwButton.vue';
+
+defineProps<{
+ note: Misskey.entities.Note;
+}>();
+
+const noteTextEl = shallowRef<HTMLDivElement>();
+const shouldCollapse = ref(false);
+const showContent = ref(false);
+
+function calcCollapse() {
+ if (noteTextEl.value) {
+ const height = noteTextEl.value.scrollHeight;
+ if (height > 200) {
+ shouldCollapse.value = true;
+ }
+ }
+}
+
+onMounted(() => {
+ calcCollapse();
+});
+
+onUpdated(() => {
+ calcCollapse();
+});
+</script>
+
+<style lang="scss" module>
+.note {
+ margin-left: auto;
+}
+
+.text {
+ position: relative;
+ max-height: 200px;
+ overflow: hidden;
+
+ &.collapsed::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 64px;
+ background: linear-gradient(0deg, var(--panel), var(--X15));
+ }
+}
+
+.content {
+ padding: 16px;
+ margin: 0 0 0 auto;
+ max-width: max-content;
+ border-radius: 16px;
+}
+
+.reactions {
+ box-sizing: border-box;
+ margin: 8px -16px -8px;
+ padding: 8px 16px 0;
+ width: calc(100% + 32px);
+ border-top: 1px solid var(--divider);
+}
+
+.richcontent {
+ min-width: 250px;
+}
+</style>
diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue
index 139b2e0a07..db326f9e6c 100644
--- a/packages/frontend/src/pages/welcome.timeline.vue
+++ b/packages/frontend/src/pages/welcome.timeline.vue
@@ -4,24 +4,17 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="$style.root">
- <div ref="scrollEl" :class="[$style.scrollbox, { [$style.scroll]: isScrolling }]">
- <div v-for="note in notes" :key="note.id" :class="$style.note">
- <div class="_panel" :class="$style.content">
- <div>
- <MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
- <Mfm v-if="note.text" :text="note.text" :author="note.user"/>
- <MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
- </div>
- <div v-if="note.files.length > 0" :class="$style.richcontent">
- <MkMediaList :mediaList="note.files"/>
- </div>
- <div v-if="note.poll">
- <MkPoll :noteId="note.id" :poll="note.poll" :readOnly="true"/>
- </div>
- </div>
- <MkReactionsViewer ref="reactionsViewer" :note="note"/>
- </div>
+<div :class="$style.root" class="_gaps">
+ <div
+ ref="notesMainContainerEl"
+ class="_gaps"
+ :class="[$style.scrollBoxMain, { [$style.scrollIntro]: (scrollState === 'intro'), [$style.scrollLoop]: (scrollState === 'loop') }]"
+ @animationend="changeScrollState"
+ >
+ <XNote v-for="note in notes" :key="`${note.id}_1`" :class="$style.note" :note="note"/>
+ </div>
+ <div v-if="isScrolling" class="_gaps" :class="[$style.scrollBoxSub, { [$style.scrollIntro]: (scrollState === 'intro'), [$style.scrollLoop]: (scrollState === 'loop') }]">
+ <XNote v-for="note in notes" :key="`${note.id}_2`" :class="$style.note" :note="note"/>
</div>
</div>
</template>
@@ -29,43 +22,54 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import * as Misskey from 'misskey-js';
import { onUpdated, ref, shallowRef } from 'vue';
-import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
-import MkMediaList from '@/components/MkMediaList.vue';
-import MkPoll from '@/components/MkPoll.vue';
+import XNote from '@/pages/welcome.timeline.note.vue';
import { misskeyApiGet } from '@/scripts/misskey-api.js';
import { getScrollContainer } from '@/scripts/scroll.js';
const notes = ref<Misskey.entities.Note[]>([]);
const isScrolling = ref(false);
-const scrollEl = shallowRef<HTMLElement>();
+const scrollState = ref<null | 'intro' | 'loop'>(null);
+const notesMainContainerEl = shallowRef<HTMLElement>();
misskeyApiGet('notes/featured').then(_notes => {
notes.value = _notes;
});
+function changeScrollState() {
+ if (scrollState.value !== 'loop') {
+ scrollState.value = 'loop';
+ }
+}
+
onUpdated(() => {
- if (!scrollEl.value) return;
- const container = getScrollContainer(scrollEl.value);
+ if (!notesMainContainerEl.value) return;
+ const container = getScrollContainer(notesMainContainerEl.value);
const containerHeight = container ? container.clientHeight : window.innerHeight;
- if (scrollEl.value.offsetHeight > containerHeight) {
+ if (notesMainContainerEl.value.offsetHeight > containerHeight) {
+ if (scrollState.value === null) {
+ scrollState.value = 'intro';
+ }
isScrolling.value = true;
}
});
</script>
<style lang="scss" module>
-@keyframes scroll {
+@keyframes scrollIntro {
0% {
transform: translate3d(0, 0, 0);
}
- 5% {
- transform: translate3d(0, 0, 0);
+ 100% {
+ transform: translate3d(0, calc(calc(-100% - 128px) - var(--margin)), 0);
}
- 75% {
- transform: translate3d(0, calc(-100% + 90vh), 0);
+}
+
+@keyframes scrollConstant {
+ 0% {
+ transform: translate3d(0, -128px, 0);
}
- 90% {
- transform: translate3d(0, calc(-100% + 90vh), 0);
+ 100% {
+ transform: translate3d(0, calc(calc(-100% - 128px) - var(--margin)), 0);
}
}
@@ -73,24 +77,26 @@ onUpdated(() => {
text-align: right;
}
-.scrollbox {
- &.scroll {
- animation: scroll 45s linear infinite;
+.scrollBoxMain {
+ &.scrollIntro {
+ animation: scrollIntro 30s linear forwards;
+ }
+ &.scrollLoop {
+ animation: scrollConstant 30s linear infinite;
}
}
-.note {
- margin: 16px 0 16px auto;
-}
-
-.content {
- padding: 16px;
- margin: 0 0 0 auto;
- max-width: max-content;
- border-radius: 16px;
+.scrollBoxSub {
+ &.scrollIntro {
+ animation: scrollIntro 30s linear forwards;
+ }
+ &.scrollLoop {
+ animation: scrollConstant 30s linear infinite;
+ }
}
-.richcontent {
- min-width: 250px;
+.root:has(.note:hover) .scrollBoxMain,
+.root:has(.note:hover) .scrollBoxSub {
+ animation-play-state: paused;
}
</style>