enhance(frontend): ユーザーページヘッダーにユーザーメニューとフォローボタンを追加

This commit is contained in:
NoriDev 2023-08-25 16:38:05 +09:00
parent 80022cb991
commit 5cb72b716a
5 changed files with 68 additions and 4 deletions

View file

@ -41,6 +41,7 @@
- Enhance: '제어판 - 신고' 페이지의 버튼 가독성 향상
- Enhance: '모달에 흐림 효과 사용' 옵션이 비활성화된 경우, 이미지를 탭하여 표시할 때 표시되는 배경을 어둡게 조정
- Enhance: 대화 페이지 디자인 개선
- Enhance: 유저 페이지 헤더에 유저 메뉴, 팔로우 버튼 추가
- Fix: (Friendly) 흐림 효과를 사용할 때 하단 내비게이션 바의 가독성이 매우 떨어지는 문제
- Fix: (Friendly) 위젯 버튼에서 'UI 애니메이션 줄이기' 옵션이 적용되지 않는 문제
- Fix: (Friendly) 스크롤을 해도 위젯 버튼이 숨겨지지 않는 문제

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<button
v-if="!disableIfFollowing || !isFollowing"
v-if="(!disableIfFollowing || !isFollowing) && ($i != null && $i.id != user.id)"
class="_button"
:class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou, [$style.full]: full, [$style.large]: large }]"
:disabled="wait"
@ -45,6 +45,9 @@ import { i18n } from '@/i18n';
import { claimAchievement } from '@/scripts/achievements';
import { $i } from '@/account';
import { userName } from '@/filters/user';
import { eventBus } from '@/scripts/cherrypick/eventBus';
let showFollowButton = $ref(false);
const props = withDefaults(defineProps<{
user: Misskey.entities.UserDetailed,
@ -133,6 +136,9 @@ async function onClick() {
onMounted(() => {
connection.on('follow', onFollowChange);
connection.on('unfollow', onFollowChange);
showFollowButton = $i != null && $i.id != props.user.id;
eventBus.emit('showFollowButton', showFollowButton);
});
onBeforeUnmount(() => {

View file

@ -39,13 +39,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<div ref="tabHighlightEl" :class="$style.tabHighlight"></div>
</div>
</template>
<div v-if="!thin_ && !narrow && actions && actions.length > 0 && hideTitle && ['index'].includes(<string>mainRouter.currentRoute.value.name)" :class="$style.buttonsRight"/>
<div v-if="!thin_ && !narrow && (actions && actions.length > 0) && hideTitle && ['index'].includes(<string>mainRouter.currentRoute.value.name)" :class="$style.buttonsRight"/>
<div v-else-if="(!thin_ && narrow && !hideTitle) || (actions && actions.length > 0)" :class="$style.buttonsRight">
<template v-for="action in actions">
<button v-tooltip.noDelay="action.text" class="_button" :class="[$style.button, { [$style.highlighted]: action.highlighted }]" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button>
</template>
</div>
<div v-else-if="!thin_ && !canBack && !(actions && actions.length > 0)" :class="$style.buttonsRight"/>
<div v-if="metadata && metadata.avatar && showFollowButton" :class="$style.followButton">
<MkFollowButton v-if="narrow" :user="metadata.avatar" :transparent="false" :full="false"/>
<MkFollowButton v-else :user="metadata.avatar" :transparent="false" :full="true"/>
</div>
</div>
</template>
@ -61,6 +65,10 @@ import { mainRouter } from '@/router';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import MkFollowButton from '@/components/MkFollowButton.vue';
import { eventBus } from '@/scripts/cherrypick/eventBus';
let showFollowButton = $ref(false);
const isFriendly = ref(miLocalStorage.getItem('ui') === 'friendly');
const canBack = ref(['index', 'explore', 'my-notifications', 'messaging'].includes(<string>mainRouter.currentRoute.value.name));
@ -210,6 +218,10 @@ onMounted(() => {
calcBg();
globalEvents.on('themeChanged', calcBg);
eventBus.on('showFollowButton', (showFollowButton_receive) => {
showFollowButton = showFollowButton_receive;
});
});
onUnmounted(() => {
@ -284,6 +296,11 @@ onUnmounted(() => {
margin: 0 0 0 var(--margin);
}
.followButton {
composes: buttons;
margin: 0 var(--margin) 0 0;
}
.goBack {
margin-left: 8px;
@ -419,4 +436,10 @@ onUnmounted(() => {
transition: all 0.2s ease;
pointer-events: none;
}
@container (max-width: 500px) {
.followButton {
margin: 0;
}
}
</style>

View file

@ -32,13 +32,17 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<XTabs v-if="(!narrow || hideTitle) && !isFriendly" :class="[$style.tabs, { [$style.tabs_canBack]: !canBack }]" :tab="tab" :tabs="tabs" :rootEl="el" @update:tab="key => emit('update:tab', key)" @tabClick="onTabClick"/>
</template>
<div v-if="!thin_ && !narrow && actions && actions.length > 0 && hideTitle && ['index'].includes(<string>mainRouter.currentRoute.value.name)" :class="$style.buttonsRight"/>
<div v-if="!thin_ && !narrow && (actions && actions.length > 0) && hideTitle && ['index'].includes(<string>mainRouter.currentRoute.value.name)" :class="$style.buttonsRight"/>
<div v-else-if="(!thin_ && narrow && !hideTitle) || (actions && actions.length > 0)" :class="$style.buttonsRight">
<template v-for="action in actions">
<button v-tooltip.noDelay="action.text" class="_button" :class="[$style.button, { [$style.highlighted]: action.highlighted }]" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button>
</template>
</div>
<div v-else-if="!thin_ && !canBack && !(actions && actions.length > 0)" :class="$style.buttonsRight"/>
<div v-if="metadata && metadata.avatar && showFollowButton" :class="$style.followButton">
<MkFollowButton v-if="narrow" :user="metadata.avatar" :transparent="false" :full="false"/>
<MkFollowButton v-else :user="metadata.avatar" :transparent="false" :full="true"/>
</div>
</div>
<div v-if="((narrow && !hideTitle) || isFriendly) && hasTabs" :class="[$style.lower, { [$style.slim]: narrow && !isFriendly, [$style.thin]: thin_ }]">
<XTabs :class="$style.tabs" :tab="tab" :tabs="tabs" :rootEl="el" @update:tab="key => emit('update:tab', key)" @tabClick="onTabClick"/>
@ -59,6 +63,10 @@ import { mainRouter } from '@/router';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import MkFollowButton from '@/components/MkFollowButton.vue';
import { eventBus } from '@/scripts/cherrypick/eventBus';
let showFollowButton = $ref(false);
const isFriendly = ref(miLocalStorage.getItem('ui') === 'friendly');
const canBack = ref(['index', 'explore', 'my-notifications', 'messaging'].includes(<string>mainRouter.currentRoute.value.name));
@ -152,6 +160,10 @@ onMounted(() => {
calcBg();
globalEvents.on('themeChanged', calcBg);
eventBus.on('showFollowButton', (showFollowButton_receive) => {
showFollowButton = showFollowButton_receive;
});
});
onUnmounted(() => {
@ -252,6 +264,11 @@ onUnmounted(() => {
margin: 0 0 0 var(--margin);
}
.followButton {
composes: buttons;
margin: 0 var(--margin) 0 0;
}
.goBack {
margin-left: 8px;
@ -358,4 +375,10 @@ onUnmounted(() => {
}
}
}
@container (max-width: 500px) {
.followButton {
margin: 0;
}
}
</style>

View file

@ -35,6 +35,8 @@ import * as os from '@/os';
import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
import { $i } from '@/account';
import { getUserMenu } from '@/scripts/get-user-menu';
import { mainRouter } from '@/router';
const XHome = defineAsyncComponent(() => import('./home.vue'));
const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue'));
@ -73,7 +75,11 @@ watch(() => props.acct, fetchUser, {
immediate: true,
});
const headerActions = $computed(() => []);
const headerActions = $computed(() => [{
icon: 'ti ti-dots',
text: i18n.ts.menu,
handler: menu,
}]);
const headerTabs = $computed(() => user ? [{
key: 'home',
@ -121,6 +127,11 @@ const headerTabs = $computed(() => user ? [{
icon: 'ti ti-icons',
}] : []);
function menu(ev) {
const { menu, cleanup } = getUserMenu(user, mainRouter);
os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
}
definePageMetadata(computed(() => user ? {
icon: 'ti ti-user',
title: user.name ? `${user.name} (@${user.username})` : `@${user.username}`,