enhance(frontend): ユーザーページヘッダーにユーザーメニューとフォローボタンを追加
This commit is contained in:
parent
80022cb991
commit
5cb72b716a
|
@ -41,6 +41,7 @@
|
|||
- Enhance: '제어판 - 신고' 페이지의 버튼 가독성 향상
|
||||
- Enhance: '모달에 흐림 효과 사용' 옵션이 비활성화된 경우, 이미지를 탭하여 표시할 때 표시되는 배경을 어둡게 조정
|
||||
- Enhance: 대화 페이지 디자인 개선
|
||||
- Enhance: 유저 페이지 헤더에 유저 메뉴, 팔로우 버튼 추가
|
||||
- Fix: (Friendly) 흐림 효과를 사용할 때 하단 내비게이션 바의 가독성이 매우 떨어지는 문제
|
||||
- Fix: (Friendly) 위젯 버튼에서 'UI 애니메이션 줄이기' 옵션이 적용되지 않는 문제
|
||||
- Fix: (Friendly) 스크롤을 해도 위젯 버튼이 숨겨지지 않는 문제
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}`,
|
||||
|
|
Loading…
Reference in a new issue