mirror of
https://github.com/kokonect-link/cherrypick
synced 2024-12-03 01:09:01 +09:00
enhance(frontend): ユーザーページヘッダーにユーザーメニューとフォローボタンを追加
This commit is contained in:
parent
80022cb991
commit
5cb72b716a
@ -41,6 +41,7 @@
|
|||||||
- Enhance: '제어판 - 신고' 페이지의 버튼 가독성 향상
|
- Enhance: '제어판 - 신고' 페이지의 버튼 가독성 향상
|
||||||
- Enhance: '모달에 흐림 효과 사용' 옵션이 비활성화된 경우, 이미지를 탭하여 표시할 때 표시되는 배경을 어둡게 조정
|
- Enhance: '모달에 흐림 효과 사용' 옵션이 비활성화된 경우, 이미지를 탭하여 표시할 때 표시되는 배경을 어둡게 조정
|
||||||
- Enhance: 대화 페이지 디자인 개선
|
- Enhance: 대화 페이지 디자인 개선
|
||||||
|
- Enhance: 유저 페이지 헤더에 유저 메뉴, 팔로우 버튼 추가
|
||||||
- Fix: (Friendly) 흐림 효과를 사용할 때 하단 내비게이션 바의 가독성이 매우 떨어지는 문제
|
- Fix: (Friendly) 흐림 효과를 사용할 때 하단 내비게이션 바의 가독성이 매우 떨어지는 문제
|
||||||
- Fix: (Friendly) 위젯 버튼에서 'UI 애니메이션 줄이기' 옵션이 적용되지 않는 문제
|
- Fix: (Friendly) 위젯 버튼에서 'UI 애니메이션 줄이기' 옵션이 적용되지 않는 문제
|
||||||
- Fix: (Friendly) 스크롤을 해도 위젯 버튼이 숨겨지지 않는 문제
|
- Fix: (Friendly) 스크롤을 해도 위젯 버튼이 숨겨지지 않는 문제
|
||||||
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
v-if="!disableIfFollowing || !isFollowing"
|
v-if="(!disableIfFollowing || !isFollowing) && ($i != null && $i.id != user.id)"
|
||||||
class="_button"
|
class="_button"
|
||||||
:class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou, [$style.full]: full, [$style.large]: large }]"
|
:class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou, [$style.full]: full, [$style.large]: large }]"
|
||||||
:disabled="wait"
|
:disabled="wait"
|
||||||
@ -45,6 +45,9 @@ import { i18n } from '@/i18n';
|
|||||||
import { claimAchievement } from '@/scripts/achievements';
|
import { claimAchievement } from '@/scripts/achievements';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
import { userName } from '@/filters/user';
|
import { userName } from '@/filters/user';
|
||||||
|
import { eventBus } from '@/scripts/cherrypick/eventBus';
|
||||||
|
|
||||||
|
let showFollowButton = $ref(false);
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
user: Misskey.entities.UserDetailed,
|
user: Misskey.entities.UserDetailed,
|
||||||
@ -133,6 +136,9 @@ async function onClick() {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
connection.on('follow', onFollowChange);
|
connection.on('follow', onFollowChange);
|
||||||
connection.on('unfollow', onFollowChange);
|
connection.on('unfollow', onFollowChange);
|
||||||
|
|
||||||
|
showFollowButton = $i != null && $i.id != props.user.id;
|
||||||
|
eventBus.emit('showFollowButton', showFollowButton);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
@ -39,13 +39,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<div ref="tabHighlightEl" :class="$style.tabHighlight"></div>
|
<div ref="tabHighlightEl" :class="$style.tabHighlight"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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">
|
<div v-else-if="(!thin_ && narrow && !hideTitle) || (actions && actions.length > 0)" :class="$style.buttonsRight">
|
||||||
<template v-for="action in actions">
|
<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>
|
<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>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="!thin_ && !canBack && !(actions && actions.length > 0)" :class="$style.buttonsRight"/>
|
<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>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -61,6 +65,10 @@ import { mainRouter } from '@/router';
|
|||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { defaultStore } from '@/store';
|
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 isFriendly = ref(miLocalStorage.getItem('ui') === 'friendly');
|
||||||
const canBack = ref(['index', 'explore', 'my-notifications', 'messaging'].includes(<string>mainRouter.currentRoute.value.name));
|
const canBack = ref(['index', 'explore', 'my-notifications', 'messaging'].includes(<string>mainRouter.currentRoute.value.name));
|
||||||
@ -210,6 +218,10 @@ onMounted(() => {
|
|||||||
|
|
||||||
calcBg();
|
calcBg();
|
||||||
globalEvents.on('themeChanged', calcBg);
|
globalEvents.on('themeChanged', calcBg);
|
||||||
|
|
||||||
|
eventBus.on('showFollowButton', (showFollowButton_receive) => {
|
||||||
|
showFollowButton = showFollowButton_receive;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@ -284,6 +296,11 @@ onUnmounted(() => {
|
|||||||
margin: 0 0 0 var(--margin);
|
margin: 0 0 0 var(--margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.followButton {
|
||||||
|
composes: buttons;
|
||||||
|
margin: 0 var(--margin) 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.goBack {
|
.goBack {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
|
|
||||||
@ -419,4 +436,10 @@ onUnmounted(() => {
|
|||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@container (max-width: 500px) {
|
||||||
|
.followButton {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -32,13 +32,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</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"/>
|
<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>
|
</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">
|
<div v-else-if="(!thin_ && narrow && !hideTitle) || (actions && actions.length > 0)" :class="$style.buttonsRight">
|
||||||
<template v-for="action in actions">
|
<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>
|
<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>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="!thin_ && !canBack && !(actions && actions.length > 0)" :class="$style.buttonsRight"/>
|
<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>
|
||||||
<div v-if="((narrow && !hideTitle) || isFriendly) && hasTabs" :class="[$style.lower, { [$style.slim]: narrow && !isFriendly, [$style.thin]: thin_ }]">
|
<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"/>
|
<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 * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { defaultStore } from '@/store';
|
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 isFriendly = ref(miLocalStorage.getItem('ui') === 'friendly');
|
||||||
const canBack = ref(['index', 'explore', 'my-notifications', 'messaging'].includes(<string>mainRouter.currentRoute.value.name));
|
const canBack = ref(['index', 'explore', 'my-notifications', 'messaging'].includes(<string>mainRouter.currentRoute.value.name));
|
||||||
@ -152,6 +160,10 @@ onMounted(() => {
|
|||||||
|
|
||||||
calcBg();
|
calcBg();
|
||||||
globalEvents.on('themeChanged', calcBg);
|
globalEvents.on('themeChanged', calcBg);
|
||||||
|
|
||||||
|
eventBus.on('showFollowButton', (showFollowButton_receive) => {
|
||||||
|
showFollowButton = showFollowButton_receive;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@ -252,6 +264,11 @@ onUnmounted(() => {
|
|||||||
margin: 0 0 0 var(--margin);
|
margin: 0 0 0 var(--margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.followButton {
|
||||||
|
composes: buttons;
|
||||||
|
margin: 0 var(--margin) 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.goBack {
|
.goBack {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
|
|
||||||
@ -358,4 +375,10 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@container (max-width: 500px) {
|
||||||
|
.followButton {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -35,6 +35,8 @@ import * as os from '@/os';
|
|||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
|
import { getUserMenu } from '@/scripts/get-user-menu';
|
||||||
|
import { mainRouter } from '@/router';
|
||||||
|
|
||||||
const XHome = defineAsyncComponent(() => import('./home.vue'));
|
const XHome = defineAsyncComponent(() => import('./home.vue'));
|
||||||
const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue'));
|
const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue'));
|
||||||
@ -73,7 +75,11 @@ watch(() => props.acct, fetchUser, {
|
|||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = $computed(() => [{
|
||||||
|
icon: 'ti ti-dots',
|
||||||
|
text: i18n.ts.menu,
|
||||||
|
handler: menu,
|
||||||
|
}]);
|
||||||
|
|
||||||
const headerTabs = $computed(() => user ? [{
|
const headerTabs = $computed(() => user ? [{
|
||||||
key: 'home',
|
key: 'home',
|
||||||
@ -121,6 +127,11 @@ const headerTabs = $computed(() => user ? [{
|
|||||||
icon: 'ti ti-icons',
|
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 ? {
|
definePageMetadata(computed(() => user ? {
|
||||||
icon: 'ti ti-user',
|
icon: 'ti ti-user',
|
||||||
title: user.name ? `${user.name} (@${user.username})` : `@${user.username}`,
|
title: user.name ? `${user.name} (@${user.username})` : `@${user.username}`,
|
||||||
|
Loading…
Reference in New Issue
Block a user