1
1
mirror of https://github.com/kokonect-link/cherrypick synced 2025-01-23 02:04:33 +09:00

enhance(frontend): 상대방이 나를 차단한 경우 차단되었음을 알 수 있도록 개선함

- 차단되면 다음 기능들이 화면 상에서 사라지고 사용이 제한됩니다.
    - 팔로우 버튼
    - 사용자 페이지에서 기본 정보 외 다른 모든 정보는 열람할 수 없음
    - 사용자 페이지에서 요약 탭 외에 모든 탭이 사용할 수 없게됨
This commit is contained in:
NoriDev 2024-10-31 17:41:02 +09:00
parent 92c8f6721a
commit 652403ca3b
23 changed files with 107 additions and 18 deletions

View File

@ -56,6 +56,11 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2024xx](CHANGE
- Enhance: 사용자 페이지와 사용자 팝업 개선 - Enhance: 사용자 페이지와 사용자 팝업 개선
- 사용자 페이지와 사용자 팝업에 `새 노트 알림 켜기` 버튼이 추가됨 - 사용자 페이지와 사용자 팝업에 `새 노트 알림 켜기` 버튼이 추가됨
- 사용자 페이지에 `사용자 노트 검색` 버튼이 추가됨 - 사용자 페이지에 `사용자 노트 검색` 버튼이 추가됨
- Enhance: 상대방이 나를 차단한 경우 차단되었음을 알 수 있도록 개선함
- 차단되면 다음 기능들이 화면 상에서 사라지고 사용이 제한됩니다.
- 팔로우 버튼
- 사용자 페이지에서 기본 정보 외 다른 모든 정보는 열람할 수 없음
- 사용자 페이지에서 요약 탭 외에 모든 탭이 사용할 수 없게됨
- Fix: 임베디드 코드에서 CherryPick의 색상 설정이 반영되지 않음 - Fix: 임베디드 코드에서 CherryPick의 색상 설정이 반영되지 않음
- Fix: 임베디드 코드에 `fade``Temml(KaTex)`가 반영되지 않음 - Fix: 임베디드 코드에 `fade``Temml(KaTex)`가 반영되지 않음
- Fix: 노트의 QR 코드를 생성했을 때 `링크 복사` 버튼을 누르면 잘못된 토스트 알림이 표시됨 - Fix: 노트의 QR 코드를 생성했을 때 `링크 복사` 버튼을 누르면 잘못된 토스트 알림이 표시됨

View File

@ -1,5 +1,7 @@
--- ---
_lang_: "English" _lang_: "English"
youBlocked: "Youre blocked"
youBlockedDescription: "You cant follow or see {user}s posts."
schedulePost: "Posting a scheduled note" schedulePost: "Posting a scheduled note"
schedulePostList: "List of scheduled notes" schedulePostList: "List of scheduled notes"
welcomeBackToast: "Display a welcome message when you log in after a certain period of time" welcomeBackToast: "Display a welcome message when you log in after a certain period of time"

8
locales/index.d.ts vendored
View File

@ -13,6 +13,14 @@ export interface Locale extends ILocale {
* *
*/ */
"_lang_": string; "_lang_": string;
/**
*
*/
"youBlocked": string;
/**
* {user}
*/
"youBlockedDescription": ParameterizedString<"user">;
/** /**
* 稿 * 稿
*/ */

View File

@ -1,5 +1,7 @@
_lang_: "日本語" _lang_: "日本語"
youBlocked: "ブロックされています"
youBlockedDescription: "{user}さんのフォローやポストの表示はできません。"
schedulePost: "予約投稿" schedulePost: "予約投稿"
schedulePostList: "予約投稿一覧" schedulePostList: "予約投稿一覧"
welcomeBackToast: "一定時間が経過した後に接続したときに歓迎メッセージを表示" welcomeBackToast: "一定時間が経過した後に接続したときに歓迎メッセージを表示"

View File

@ -1,5 +1,7 @@
--- ---
_lang_: "한국어" _lang_: "한국어"
youBlocked: "앗.. 차단당했어요.."
youBlockedDescription: "{user} 님을 팔로우하거나 해당 사용자의 게시물을 볼 수 없어요."
schedulePost: "노트 게시 예약" schedulePost: "노트 게시 예약"
schedulePostList: "게시가 예약된 노트 목록" schedulePostList: "게시가 예약된 노트 목록"
welcomeBackToast: "일정 시간이 지난 후 접속했을 때 환영 메시지 표시" welcomeBackToast: "일정 시간이 지난 후 접속했을 때 환영 메시지 표시"

View File

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class YouBlockedImageUrl1730364663000 {
name = 'YouBlockedImageUrl1730364663000'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "youBlockedImageUrl" character varying(1024)`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "youBlockedImageUrl"`);
}
}

View File

@ -104,6 +104,7 @@ export class MetaEntityService {
infoImageUrl: instance.infoImageUrl, infoImageUrl: instance.infoImageUrl,
serverErrorImageUrl: instance.serverErrorImageUrl, serverErrorImageUrl: instance.serverErrorImageUrl,
notFoundImageUrl: instance.notFoundImageUrl, notFoundImageUrl: instance.notFoundImageUrl,
youBlockedImageUrl: instance.youBlockedImageUrl,
iconUrl: instance.iconUrl, iconUrl: instance.iconUrl,
backgroundImageUrl: instance.backgroundImageUrl, backgroundImageUrl: instance.backgroundImageUrl,
logoImageUrl: instance.logoImageUrl, logoImageUrl: instance.logoImageUrl,

View File

@ -157,6 +157,12 @@ export class MiMeta {
}) })
public infoImageUrl: string | null; public infoImageUrl: string | null;
@Column('varchar', {
length: 1024,
nullable: true,
})
public youBlockedImageUrl: string | null;
@Column('boolean', { @Column('boolean', {
default: false, default: false,
}) })

View File

@ -144,6 +144,10 @@ export const packedMetaLiteSchema = {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
}, },
youBlockedImageUrl: {
type: 'string',
optional: false, nullable: true,
},
iconUrl: { iconUrl: {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,

View File

@ -94,6 +94,10 @@ export const meta = {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
}, },
youBlockedImageUrl: {
type: 'string',
optional: false, nullable: true,
},
iconUrl: { iconUrl: {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
@ -654,6 +658,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
serverErrorImageUrl: instance.serverErrorImageUrl, serverErrorImageUrl: instance.serverErrorImageUrl,
notFoundImageUrl: instance.notFoundImageUrl, notFoundImageUrl: instance.notFoundImageUrl,
infoImageUrl: instance.infoImageUrl, infoImageUrl: instance.infoImageUrl,
youBlockedImageUrl: instance.youBlockedImageUrl,
iconUrl: instance.iconUrl, iconUrl: instance.iconUrl,
app192IconUrl: instance.app192IconUrl, app192IconUrl: instance.app192IconUrl,
app512IconUrl: instance.app512IconUrl, app512IconUrl: instance.app512IconUrl,

View File

@ -54,6 +54,7 @@ export const paramDef = {
serverErrorImageUrl: { type: 'string', nullable: true }, serverErrorImageUrl: { type: 'string', nullable: true },
infoImageUrl: { type: 'string', nullable: true }, infoImageUrl: { type: 'string', nullable: true },
notFoundImageUrl: { type: 'string', nullable: true }, notFoundImageUrl: { type: 'string', nullable: true },
youBlockedImageUrl: { type: 'string', nullable: true },
iconUrl: { type: 'string', nullable: true }, iconUrl: { type: 'string', nullable: true },
app192IconUrl: { type: 'string', nullable: true }, app192IconUrl: { type: 'string', nullable: true },
app512IconUrl: { type: 'string', nullable: true }, app512IconUrl: { type: 'string', nullable: true },
@ -302,6 +303,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.notFoundImageUrl = ps.notFoundImageUrl; set.notFoundImageUrl = ps.notFoundImageUrl;
} }
if (ps.youBlockedImageUrl !== undefined) {
set.youBlockedImageUrl = ps.youBlockedImageUrl;
}
if (ps.backgroundImageUrl !== undefined) { if (ps.backgroundImageUrl !== undefined) {
set.backgroundImageUrl = ps.backgroundImageUrl; set.backgroundImageUrl = ps.backgroundImageUrl;
} }

View File

@ -198,6 +198,7 @@ export class ClientServerService {
serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg', serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg',
infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg', infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg',
notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg', notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg',
youBlockedImageUrl: meta.youBlockedImageUrl ?? 'https://xn--931a.moe/assets/error.jpg',
instanceUrl: this.config.url, instanceUrl: this.config.url,
metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)), metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)),
now: Date.now(), now: Date.now(),

View File

@ -5334,6 +5334,7 @@ export type components = {
serverErrorImageUrl: string | null; serverErrorImageUrl: string | null;
infoImageUrl: string | null; infoImageUrl: string | null;
notFoundImageUrl: string | null; notFoundImageUrl: string | null;
youBlockedImageUrl: string | null;
iconUrl: string | null; iconUrl: string | null;
maxNoteTextLength: number; maxNoteTextLength: number;
ads: { ads: {
@ -5465,6 +5466,7 @@ export type operations = {
serverErrorImageUrl: string | null; serverErrorImageUrl: string | null;
infoImageUrl: string | null; infoImageUrl: string | null;
notFoundImageUrl: string | null; notFoundImageUrl: string | null;
youBlockedImageUrl: string | null;
iconUrl: string | null; iconUrl: string | null;
app192IconUrl: string | null; app192IconUrl: string | null;
app512IconUrl: string | null; app512IconUrl: string | null;
@ -10152,6 +10154,7 @@ export type operations = {
serverErrorImageUrl?: string | null; serverErrorImageUrl?: string | null;
infoImageUrl?: string | null; infoImageUrl?: string | null;
notFoundImageUrl?: string | null; notFoundImageUrl?: string | null;
youBlockedImageUrl?: string | null;
iconUrl?: string | null; iconUrl?: string | null;
app192IconUrl?: string | null; app192IconUrl?: string | null;
app512IconUrl?: string | null; app512IconUrl?: string | null;

View File

@ -120,6 +120,7 @@ export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM';
export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://xn--931a.moe/assets/error.jpg'; export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://xn--931a.moe/assets/error.jpg';
export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://xn--931a.moe/assets/not-found.jpg'; export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://xn--931a.moe/assets/not-found.jpg';
export const DEFAULT_INFO_IMAGE_URL = 'https://xn--931a.moe/assets/info.jpg'; export const DEFAULT_INFO_IMAGE_URL = 'https://xn--931a.moe/assets/info.jpg';
export const DEFAULT_YOU_BLOCKED_IMAGE_URL = 'https://xn--931a.moe/assets/error.jpg';
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'fade', 'rotate', 'ruby', 'unixtime']; export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'fade', 'rotate', 'ruby', 'unixtime'];
export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = {

View File

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<button <button
v-if="(!disableIfFollowing || !isFollowing) && ($i != null && $i.id != user.id)" v-if="(!disableIfFollowing || !isFollowing) && ($i != null && $i.id != user.id) && !user.isBlocked"
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"

View File

@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<p :class="$style.statusItemLabel">{{ i18n.ts.followers }}</p><span :class="$style.statusItemValue">{{ number(user.followersCount) }}</span> <p :class="$style.statusItemLabel">{{ i18n.ts.followers }}</p><span :class="$style.statusItemValue">{{ number(user.followersCount) }}</span>
</div> </div>
</div> </div>
<MkFollowButton v-if="user.id != $i?.id" :class="$style.follow" :user="user" mini/> <MkFollowButton v-if="user.id != $i?.id && !user.isBlocked" :class="$style.follow" :user="user" mini/>
</div> </div>
</template> </template>

View File

@ -44,9 +44,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>{{ number(user.followersCount) }}</div> <div>{{ number(user.followersCount) }}</div>
</div> </div>
</div> </div>
<button class="_button" :class="$style.menu" @click="showMenu"><i class="ti ti-dots"></i></button> <button class="_button" :class="[$style.menu, { [$style.isBlocked]: user.isBlocked }]" @click="showMenu"><i class="ti ti-dots"></i></button>
<button v-tooltip="user.notify === 'none' ? i18n.ts.notifyNotes : i18n.ts.unnotifyNotes" class="_button" :class="$style.notify" @click="toggleNotify"><i :class="user.notify === 'none' ? 'ti ti-bell-plus' : 'ti ti-bell-minus'"></i></button> <button v-tooltip="user.notify === 'none' ? i18n.ts.notifyNotes : i18n.ts.unnotifyNotes" class="_button" :class="[$style.notify, { [$style.isBlocked]: user.isBlocked }]" @click="toggleNotify"><i :class="user.notify === 'none' ? 'ti ti-bell-plus' : 'ti ti-bell-minus'"></i></button>
<MkFollowButton v-model:user="user" :class="$style.follow" mini/> <MkFollowButton v-if="!user.isBlocked" v-model:user="user" :class="$style.follow" mini/>
</div> </div>
<div v-else> <div v-else>
<MkLoading/> <MkLoading/>
@ -260,8 +260,18 @@ onMounted(() => {
border-radius: 999px; border-radius: 999px;
} }
.menu {
&.isBlocked {
right: 44px;
}
}
.notify { .notify {
right: 44px; right: 44px;
&.isBlocked {
right: 8px;
}
} }
.follow { .follow {

View File

@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</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="pageMetadata && pageMetadata.avatar && ($i && $i.id !== pageMetadata.userName?.id)" :class="$style.followButton"> <div v-if="pageMetadata && pageMetadata.avatar && ($i && $i.id !== pageMetadata.userName?.id) && !disableFollowButton" :class="$style.followButton">
<MkFollowButton v-if="mainRouter.currentRoute.value.name === 'user'" :user="pageMetadata.avatar" :transparent="false" :full="!narrow"/> <MkFollowButton v-if="mainRouter.currentRoute.value.name === 'user'" :user="pageMetadata.avatar" :transparent="false" :full="!narrow"/>
</div> </div>
</div> </div>
@ -90,6 +90,7 @@ const props = withDefaults(defineProps<{
actions?: PageHeaderItem[] | null; actions?: PageHeaderItem[] | null;
thin?: boolean; thin?: boolean;
displayMyAvatar?: boolean; displayMyAvatar?: boolean;
disableFollowButton?: boolean;
}>(), { }>(), {
tabs: () => ([] as Tab[]), tabs: () => ([] as Tab[]),
}); });

View File

@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</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="pageMetadata && pageMetadata.avatar && ($i && $i.id !== pageMetadata.userName?.id) && mainRouter.currentRoute.value.name === 'user'" :class="$style.followButton"> <div v-if="pageMetadata && pageMetadata.avatar && ($i && $i.id !== pageMetadata.userName?.id) && mainRouter.currentRoute.value.name === 'user' && !disableFollowButton" :class="$style.followButton">
<MkFollowButton :user="pageMetadata.avatar" :transparent="false" :full="!narrow"/> <MkFollowButton :user="pageMetadata.avatar" :transparent="false" :full="!narrow"/>
</div> </div>
</div> </div>
@ -92,6 +92,7 @@ const props = withDefaults(defineProps<{
actions?: PageHeaderItem[] | null; actions?: PageHeaderItem[] | null;
thin?: boolean; thin?: boolean;
displayMyAvatar?: boolean; displayMyAvatar?: boolean;
disableFollowButton?: boolean;
}>(), { }>(), {
tabs: () => ([] as Tab[]), tabs: () => ([] as Tab[]),
}); });

View File

@ -5,7 +5,7 @@
import { computed, reactive } from 'vue'; import { computed, reactive } from 'vue';
import * as Misskey from 'cherrypick-js'; import * as Misskey from 'cherrypick-js';
import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERROR_IMAGE_URL } from '@@/js/const.js'; import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERROR_IMAGE_URL, DEFAULT_YOU_BLOCKED_IMAGE_URL } from '@@/js/const.js';
import { misskeyApi } from '@/scripts/misskey-api.js'; import { misskeyApi } from '@/scripts/misskey-api.js';
import { miLocalStorage } from '@/local-storage.js'; import { miLocalStorage } from '@/local-storage.js';
@ -36,6 +36,8 @@ export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO
export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL); export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL);
export const youBlockedImageUrl = computed(() => instance.youBlockedImageUrl ?? DEFAULT_YOU_BLOCKED_IMAGE_URL);
export const isEnabledUrlPreview = computed(() => instance.enableUrlPreview ?? true); export const isEnabledUrlPreview = computed(() => instance.enableUrlPreview ?? true);
export async function fetchInstance(force = false): Promise<Misskey.entities.MetaDetailed> { export async function fetchInstance(force = false): Promise<Misskey.entities.MetaDetailed> {

View File

@ -62,6 +62,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.somethingHappened }}</template> <template #label>{{ i18n.ts.somethingHappened }}</template>
</MkInput> </MkInput>
<MkInput v-model="youBlockedImageUrl" type="url">
<template #prefix><i class="ti ti-link"></i></template>
<template #label>{{ i18n.ts.youBlocked }}</template>
</MkInput>
<MkColorInput v-model="themeColor"> <MkColorInput v-model="themeColor">
<template #label>{{ i18n.ts.themeColor }}</template> <template #label>{{ i18n.ts.themeColor }}</template>
</MkColorInput> </MkColorInput>
@ -135,6 +140,7 @@ const defaultDarkTheme = ref<string | null>(null);
const serverErrorImageUrl = ref<string | null>(null); const serverErrorImageUrl = ref<string | null>(null);
const infoImageUrl = ref<string | null>(null); const infoImageUrl = ref<string | null>(null);
const notFoundImageUrl = ref<string | null>(null); const notFoundImageUrl = ref<string | null>(null);
const youBlockedImageUrl = ref<string | null>(null);
const repositoryUrl = ref<string | null>(null); const repositoryUrl = ref<string | null>(null);
const feedbackUrl = ref<string | null>(null); const feedbackUrl = ref<string | null>(null);
const manifestJsonOverride = ref<string>('{}'); const manifestJsonOverride = ref<string>('{}');
@ -153,6 +159,7 @@ async function init() {
serverErrorImageUrl.value = meta.serverErrorImageUrl; serverErrorImageUrl.value = meta.serverErrorImageUrl;
infoImageUrl.value = meta.infoImageUrl; infoImageUrl.value = meta.infoImageUrl;
notFoundImageUrl.value = meta.notFoundImageUrl; notFoundImageUrl.value = meta.notFoundImageUrl;
youBlockedImageUrl.value = meta.youBlockedImageUrl;
repositoryUrl.value = meta.repositoryUrl; repositoryUrl.value = meta.repositoryUrl;
feedbackUrl.value = meta.feedbackUrl; feedbackUrl.value = meta.feedbackUrl;
manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t'); manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
@ -171,6 +178,7 @@ function save() {
defaultDarkTheme: defaultDarkTheme.value === '' ? null : defaultDarkTheme.value, defaultDarkTheme: defaultDarkTheme.value === '' ? null : defaultDarkTheme.value,
infoImageUrl: infoImageUrl.value === '' ? null : infoImageUrl.value, infoImageUrl: infoImageUrl.value === '' ? null : infoImageUrl.value,
notFoundImageUrl: notFoundImageUrl.value === '' ? null : notFoundImageUrl.value, notFoundImageUrl: notFoundImageUrl.value === '' ? null : notFoundImageUrl.value,
youBlockedImageUrl: youBlockedImageUrl.value === '' ? null : youBlockedImageUrl.value,
serverErrorImageUrl: serverErrorImageUrl.value === '' ? null : serverErrorImageUrl.value, serverErrorImageUrl: serverErrorImageUrl.value === '' ? null : serverErrorImageUrl.value,
repositoryUrl: repositoryUrl.value === '' ? null : repositoryUrl.value, repositoryUrl: repositoryUrl.value === '' ? null : repositoryUrl.value,
feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value, feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value,

View File

@ -148,11 +148,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
<div class="contents _gaps"> <div class="contents _gaps">
<div v-if="user.pinnedNotes.length > 0" class="_gaps"> <div v-if="user.pinnedNotes.length > 0 && !user.isBlocked" class="_gaps">
<MkNote v-for="note in user.pinnedNotes" :key="note.id" class="note _panel" :note="note" :pinned="true"/> <MkNote v-for="note in user.pinnedNotes" :key="note.id" class="note _panel" :note="note" :pinned="true"/>
</div> </div>
<MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo> <MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo>
<template v-if="narrow"> <template v-if="narrow && !user.isBlocked">
<MkLazy> <MkLazy>
<XFiles :key="user.id" :user="user"/> <XFiles :key="user.id" :user="user"/>
</MkLazy> </MkLazy>
@ -160,14 +160,19 @@ SPDX-License-Identifier: AGPL-3.0-only
<XActivity :key="user.id" :user="user"/> <XActivity :key="user.id" :user="user"/>
</MkLazy> </MkLazy>
</template> </template>
<div v-if="!disableNotes"> <div v-if="!disableNotes && !user.isBlocked">
<MkLazy> <MkLazy>
<XTimeline :user="user"/> <XTimeline :user="user"/>
</MkLazy> </MkLazy>
</div> </div>
<div v-if="user.isBlocked" class="_fullinfo">
<img :src="youBlockedImageUrl" class="_ghost"/>
<div style="font-size: 1.4rem; font-weight: bold; padding-bottom: 4px;">{{ i18n.ts.youBlocked }}</div>
<div style="opacity: 0.7">{{ i18n.tsx.youBlockedDescription({ user: `@${ user.username }` }) }}</div>
</div> </div>
</div> </div>
<div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;"> </div>
<div v-if="!narrow && !user.isBlocked" class="sub _gaps" style="container-type: inline-size;">
<XFiles :key="user.id" :user="user"/> <XFiles :key="user.id" :user="user"/>
<XActivity :key="user.id" :user="user"/> <XActivity :key="user.id" :user="user"/>
</div> </div>
@ -207,6 +212,7 @@ import { vibrate } from '@/scripts/vibrate.js';
import detectLanguage from '@/scripts/detect-language.js'; import detectLanguage from '@/scripts/detect-language.js';
import { globalEvents } from '@/events.js'; import { globalEvents } from '@/events.js';
import { notesSearchAvailable, canSearchNonLocalNotes } from '@/scripts/check-permissions.js'; import { notesSearchAvailable, canSearchNonLocalNotes } from '@/scripts/check-permissions.js';
import { youBlockedImageUrl } from '@/instance.js';
function calcAge(birthdate: string): number { function calcAge(birthdate: string): number {
const date = new Date(birthdate); const date = new Date(birthdate);

View File

@ -6,8 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<MkStickyContainer> <MkStickyContainer>
<template #header> <template #header>
<CPPageHeader v-if="isMobile && defaultStore.state.mobileHeaderChange" v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/> <CPPageHeader v-if="isMobile && defaultStore.state.mobileHeaderChange" v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :disableFollowButton="(user && user.isBlocked) == true"/>
<MkPageHeader v-else v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/> <MkPageHeader v-else v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :disableFollowButton="(user && user.isBlocked) == true"/>
</template> </template>
<div> <div>
<div v-if="user"> <div v-if="user">
@ -102,7 +102,7 @@ const headerTabs = computed(() => user.value ? [{
key: 'home', key: 'home',
title: i18n.ts.overview, title: i18n.ts.overview,
icon: 'ti ti-home', icon: 'ti ti-home',
}, { }, ...(!user.value.isBlocked ? [{
key: 'notes', key: 'notes',
title: i18n.ts.notes, title: i18n.ts.notes,
icon: 'ti ti-pencil', icon: 'ti ti-pencil',
@ -138,7 +138,7 @@ const headerTabs = computed(() => user.value ? [{
key: 'gallery', key: 'gallery',
title: i18n.ts.gallery, title: i18n.ts.gallery,
icon: 'ti ti-icons', icon: 'ti ti-icons',
}, { }] : []), {
key: 'raw', key: 'raw',
title: 'Raw', title: 'Raw',
icon: 'ti ti-code', icon: 'ti ti-code',