summaryrefslogtreecommitdiff
path: root/packages/frontend
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2024-04-14 21:30:24 +0900
committerGitHub <noreply@github.com>2024-04-14 21:30:24 +0900
commitbba3097765317cbf95d09627961b5b5dce16a972 (patch)
tree49df1638d6622dd807281a0324a199593e049084 /packages/frontend
parentfix(backend): incorrect logic for determining whether Quote or not (#13700) (diff)
downloadmisskey-bba3097765317cbf95d09627961b5b5dce16a972.tar.gz
misskey-bba3097765317cbf95d09627961b5b5dce16a972.tar.bz2
misskey-bba3097765317cbf95d09627961b5b5dce16a972.zip
enhance: クリップのノート数を表示するように (#13686)
* enhance: クリップのノート数を表示できるように * Update Changelog
Diffstat (limited to 'packages/frontend')
-rw-r--r--packages/frontend/src/components/MkClipPreview.vue52
-rw-r--r--packages/frontend/src/pages/clip.vue13
-rw-r--r--packages/frontend/src/pages/my-clips/index.vue10
-rw-r--r--packages/frontend/src/pages/note.vue4
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts36
5 files changed, 83 insertions, 32 deletions
diff --git a/packages/frontend/src/components/MkClipPreview.vue b/packages/frontend/src/components/MkClipPreview.vue
index c51ad4356d..6299a28e9f 100644
--- a/packages/frontend/src/components/MkClipPreview.vue
+++ b/packages/frontend/src/components/MkClipPreview.vue
@@ -4,37 +4,59 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="$style.root" class="_panel">
- <b>{{ clip.name }}</b>
- <div v-if="clip.description" :class="$style.description">{{ clip.description }}</div>
- <div v-if="clip.lastClippedAt">{{ i18n.ts.updatedAt }}: <MkTime :time="clip.lastClippedAt" mode="detail"/></div>
- <div :class="$style.user">
- <MkAvatar :user="clip.user" :class="$style.userAvatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
+<MkA :to="`/clips/${clip.id}`" :class="$style.link">
+ <div :class="$style.root" class="_panel _gaps_s">
+ <b>{{ clip.name }}</b>
+ <div :class="$style.description">
+ <div v-if="clip.description"><Mfm :text="clip.description" :plain="true" :nowrap="true"/></div>
+ <div v-if="clip.lastClippedAt">{{ i18n.ts.updatedAt }}: <MkTime :time="clip.lastClippedAt" mode="detail"/></div>
+ <div v-if="clip.notesCount != null">{{ i18n.ts.notesCount }}: {{ number(clip.notesCount) }} / {{ $i?.policies.noteEachClipsLimit }} ({{ i18n.tsx.remainingN({ n: remaining }) }})</div>
+ </div>
+ <div :class="$style.divider"></div>
+ <div>
+ <MkAvatar :user="clip.user" :class="$style.userAvatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
+ </div>
</div>
-</div>
+</MkA>
</template>
<script lang="ts" setup>
+import * as Misskey from 'misskey-js';
+import { computed } from 'vue';
import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import number from '@/filters/number.js';
-defineProps<{
- clip: any;
+const props = defineProps<{
+ clip: Misskey.entities.Clip;
}>();
+
+const remaining = computed(() => {
+ return ($i?.policies && props.clip.notesCount != null) ? ($i.policies.noteEachClipsLimit - props.clip.notesCount) : i18n.ts.unknown;
+});
</script>
<style lang="scss" module>
-.root {
+.link {
display: block;
+
+ &:hover {
+ text-decoration: none;
+ color: var(--accent);
+ }
+}
+
+.root {
padding: 16px;
}
-.description {
- padding: 8px 0;
+.divider {
+ height: 1px;
+ background: var(--divider);
}
-.user {
- padding-top: 16px;
- border-top: solid 0.5px var(--divider);
+.description {
+ font-size: 90%;
}
.userAvatar {
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index c38cc117bc..fd64a55c65 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -9,11 +9,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :contentMax="800">
<div v-if="clip" class="_gaps">
<div class="_panel">
- <div v-if="clip.description" :class="$style.description">
- <Mfm :text="clip.description" :isNote="false"/>
+ <div class="_gaps_s" :class="$style.description">
+ <div v-if="clip.description">
+ <Mfm :text="clip.description" :isNote="false"/>
+ </div>
+ <div v-else>({{ i18n.ts.noDescription }})</div>
+ <div>
+ <MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike rounded primary @click="unfavorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
+ <MkButton v-else v-tooltip="i18n.ts.favorite" asLike rounded @click="favorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
+ </div>
</div>
- <MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike rounded primary @click="unfavorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
- <MkButton v-else v-tooltip="i18n.ts.favorite" asLike rounded @click="favorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
<div :class="$style.user">
<MkAvatar :user="clip.user" :class="$style.avatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
</div>
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index 803b28899a..1a0d7177fc 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -11,16 +11,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="tab === 'my'" key="my" class="_gaps">
<MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
- <MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="_gaps">
- <MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`">
- <MkClipPreview :clip="item"/>
- </MkA>
+ <MkPagination v-slot="{ items }" ref="pagingComponent" :pagination="pagination" class="_gaps">
+ <MkClipPreview v-for="item in items" :key="item.id" :clip="item"/>
</MkPagination>
</div>
<div v-else-if="tab === 'favorites'" key="favorites" class="_gaps">
- <MkA v-for="item in favorites" :key="item.id" :to="`/clips/${item.id}`">
- <MkClipPreview :clip="item"/>
- </MkA>
+ <MkClipPreview v-for="item in favorites" :key="item.id" :clip="item"/>
</div>
</MkHorizontalSwipe>
</MkSpacer>
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index e14651742a..97f32d35cd 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -26,9 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="clips && clips.length > 0" class="_margin">
<div style="font-weight: bold; padding: 12px;">{{ i18n.ts.clip }}</div>
<div class="_gaps">
- <MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`">
- <MkClipPreview :clip="item"/>
- </MkA>
+ <MkClipPreview v-for="item in clips" :key="item.id" :clip="item"/>
</div>
</div>
<div v-if="!showPrev" class="_buttons" :class="$style.loadPrev">
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index b273bd36f3..87921bc67f 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -26,6 +26,14 @@ export async function getNoteClipMenu(props: {
isDeleted: Ref<boolean>;
currentClip?: Misskey.entities.Clip;
}) {
+ function getClipName(clip: Misskey.entities.Clip) {
+ if ($i && clip.userId === $i.id && clip.notesCount != null) {
+ return `${clip.name} (${clip.notesCount}/${$i.policies.noteEachClipsLimit})`;
+ } else {
+ return clip.name;
+ }
+ }
+
const isRenote = (
props.note.renote != null &&
props.note.text == null &&
@@ -37,7 +45,7 @@ export async function getNoteClipMenu(props: {
const clips = await clipsCache.fetch();
const menu: MenuItem[] = [...clips.map(clip => ({
- text: clip.name,
+ text: getClipName(clip),
action: () => {
claimAchievement('noteClipped1');
os.promiseDialog(
@@ -50,7 +58,18 @@ export async function getNoteClipMenu(props: {
text: i18n.tsx.confirmToUnclipAlreadyClippedNote({ name: clip.name }),
});
if (!confirm.canceled) {
- os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id });
+ os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id }).then(() => {
+ clipsCache.set(clips.map(c => {
+ if (c.id === clip.id) {
+ return {
+ ...c,
+ notesCount: Math.max(0, ((c.notesCount ?? 0) - 1)),
+ };
+ } else {
+ return c;
+ }
+ }));
+ });
if (props.currentClip?.id === clip.id) props.isDeleted.value = true;
}
} else {
@@ -60,7 +79,18 @@ export async function getNoteClipMenu(props: {
});
}
},
- );
+ ).then(() => {
+ clipsCache.set(clips.map(c => {
+ if (c.id === clip.id) {
+ return {
+ ...c,
+ notesCount: (c.notesCount ?? 0) + 1,
+ };
+ } else {
+ return c;
+ }
+ }));
+ });
},
})), { type: 'divider' }, {
icon: 'ti ti-plus',