mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 15:34:13 +00:00 
			
		
		
		
	upd: add buttons to replies
This commit is contained in:
		
							parent
							
								
									7b179d3a92
								
							
						
					
					
						commit
						2ea7e799fe
					
				
					 3 changed files with 206 additions and 3 deletions
				
			
		| 
						 | 
					@ -110,7 +110,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
				</button>
 | 
									</button>
 | 
				
			||||||
				<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" @mousedown="react()">
 | 
									<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" @mousedown="react()">
 | 
				
			||||||
					<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i>
 | 
										<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i>
 | 
				
			||||||
					<i v-else class="ph-plus ph-bold ph-lg"></i>
 | 
										<i v-else class="ph-smiley ph-bold ph-lg"></i>
 | 
				
			||||||
				</button>
 | 
									</button>
 | 
				
			||||||
				<button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" @click="undoReact(appearNote)">
 | 
									<button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" @click="undoReact(appearNote)">
 | 
				
			||||||
					<i class="ph-minus ph-bold ph-lg"></i>
 | 
										<i class="ph-minus ph-bold ph-lg"></i>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -118,7 +118,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
			</button>
 | 
								</button>
 | 
				
			||||||
			<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()">
 | 
								<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()">
 | 
				
			||||||
				<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i>
 | 
									<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i>
 | 
				
			||||||
				<i v-else class="ph-plus ph-bold ph-lg"></i>
 | 
									<i v-else class="ph-smiley ph-bold ph-lg"></i>
 | 
				
			||||||
			</button>
 | 
								</button>
 | 
				
			||||||
			<button v-if="appearNote.myReaction != null" ref="reactButton" class="_button" :class="[$style.noteFooterButton, $style.reacted]" @click="undoReact(appearNote)">
 | 
								<button v-if="appearNote.myReaction != null" ref="reactButton" class="_button" :class="[$style.noteFooterButton, $style.reacted]" @click="undoReact(appearNote)">
 | 
				
			||||||
				<i class="ph-minus ph-bold ph-lg"></i>
 | 
									<i class="ph-minus ph-bold ph-lg"></i>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,36 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
					<MkSubNoteContent :class="$style.text" :note="note"/>
 | 
										<MkSubNoteContent :class="$style.text" :note="note"/>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
								<footer>
 | 
				
			||||||
 | 
									<MkReactionsViewer ref="reactionsViewer" :note="note"/>
 | 
				
			||||||
 | 
									<button class="_button" :class="$style.noteFooterButton" @click="reply()">
 | 
				
			||||||
 | 
										<i class="ph-arrow-u-up-left ph-bold pg-lg"></i>
 | 
				
			||||||
 | 
										<p v-if="note.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ note.repliesCount }}</p>
 | 
				
			||||||
 | 
									</button>
 | 
				
			||||||
 | 
									<button
 | 
				
			||||||
 | 
									v-if="canRenote"
 | 
				
			||||||
 | 
									ref="renoteButton"
 | 
				
			||||||
 | 
									class="_button"
 | 
				
			||||||
 | 
									:class="$style.noteFooterButton"
 | 
				
			||||||
 | 
									@mousedown="renote()"
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
										<i class="ph-repeat ph-bold ph-lg"></i>
 | 
				
			||||||
 | 
										<p v-if="note.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ note.renoteCount }}</p>
 | 
				
			||||||
 | 
									</button>
 | 
				
			||||||
 | 
									<button v-else class="_button" :class="$style.noteFooterButton" disabled>
 | 
				
			||||||
 | 
										<i class="ph-prohibit ph-bold ph-lg"></i>
 | 
				
			||||||
 | 
									</button>
 | 
				
			||||||
 | 
									<button v-if="note.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()">
 | 
				
			||||||
 | 
										<i v-if="note.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i>
 | 
				
			||||||
 | 
										<i v-else class="ph-smiley ph-bold ph-lg"></i>
 | 
				
			||||||
 | 
									</button>
 | 
				
			||||||
 | 
									<button v-if="note.myReaction != null" ref="reactButton" class="_button" :class="[$style.noteFooterButton, $style.reacted]" @click="undoReact(note)">
 | 
				
			||||||
 | 
										<i class="ph-minus ph-bold ph-lg"></i>
 | 
				
			||||||
 | 
									</button>
 | 
				
			||||||
 | 
									<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="menu()">
 | 
				
			||||||
 | 
										<i class="ph-dots-three ph-bold ph-lg"></i>
 | 
				
			||||||
 | 
									</button>
 | 
				
			||||||
 | 
								</footer>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<template v-if="depth < 5">
 | 
						<template v-if="depth < 5">
 | 
				
			||||||
| 
						 | 
					@ -40,9 +70,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { ref } from 'vue';
 | 
					import { computed, ref, shallowRef } from 'vue';
 | 
				
			||||||
import * as Misskey from 'misskey-js';
 | 
					import * as Misskey from 'misskey-js';
 | 
				
			||||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
 | 
					import MkNoteHeader from '@/components/MkNoteHeader.vue';
 | 
				
			||||||
 | 
					import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
 | 
				
			||||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
 | 
					import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
 | 
				
			||||||
import MkCwButton from '@/components/MkCwButton.vue';
 | 
					import MkCwButton from '@/components/MkCwButton.vue';
 | 
				
			||||||
import { notePage } from '@/filters/note.js';
 | 
					import { notePage } from '@/filters/note.js';
 | 
				
			||||||
| 
						 | 
					@ -52,6 +83,14 @@ import { $i } from '@/account.js';
 | 
				
			||||||
import { userPage } from "@/filters/user";
 | 
					import { userPage } from "@/filters/user";
 | 
				
			||||||
import { checkWordMute } from "@/scripts/check-word-mute";
 | 
					import { checkWordMute } from "@/scripts/check-word-mute";
 | 
				
			||||||
import { defaultStore } from "@/store";
 | 
					import { defaultStore } from "@/store";
 | 
				
			||||||
 | 
					import { pleaseLogin } from '@/scripts/please-login.js';
 | 
				
			||||||
 | 
					import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 | 
				
			||||||
 | 
					import MkRippleEffect from '@/components/MkRippleEffect.vue';
 | 
				
			||||||
 | 
					import { reactionPicker } from '@/scripts/reaction-picker.js';
 | 
				
			||||||
 | 
					import { claimAchievement } from '@/scripts/achievements.js';
 | 
				
			||||||
 | 
					import type { MenuItem } from '@/types/menu.js';
 | 
				
			||||||
 | 
					import { getNoteMenu } from '@/scripts/get-note-menu.js';
 | 
				
			||||||
 | 
					const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = withDefaults(defineProps<{
 | 
					const props = withDefaults(defineProps<{
 | 
				
			||||||
	note: Misskey.entities.Note;
 | 
						note: Misskey.entities.Note;
 | 
				
			||||||
| 
						 | 
					@ -63,11 +102,150 @@ const props = withDefaults(defineProps<{
 | 
				
			||||||
	depth: 1,
 | 
						depth: 1,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function focus() {
 | 
				
			||||||
 | 
						el.value.focus();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const muted = ref(checkWordMute(props.note, $i, defaultStore.state.mutedWords));
 | 
					const muted = ref(checkWordMute(props.note, $i, defaultStore.state.mutedWords));
 | 
				
			||||||
 | 
					const translation = ref(null);
 | 
				
			||||||
 | 
					const translating = ref(false);
 | 
				
			||||||
 | 
					const isDeleted = ref(false);
 | 
				
			||||||
 | 
					const reactButton = shallowRef<HTMLElement>();
 | 
				
			||||||
 | 
					const renoteButton = shallowRef<HTMLElement>();
 | 
				
			||||||
 | 
					const menuButton = shallowRef<HTMLElement>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function reply(viaKeyboard = false): void {
 | 
				
			||||||
 | 
						pleaseLogin();
 | 
				
			||||||
 | 
						showMovedDialog();
 | 
				
			||||||
 | 
						os.post({
 | 
				
			||||||
 | 
							reply: props.note,
 | 
				
			||||||
 | 
							channel: props.note.channel,
 | 
				
			||||||
 | 
							animation: !viaKeyboard,
 | 
				
			||||||
 | 
						}, () => {
 | 
				
			||||||
 | 
							focus();
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function react(viaKeyboard = false): void {
 | 
				
			||||||
 | 
						pleaseLogin();
 | 
				
			||||||
 | 
						showMovedDialog();
 | 
				
			||||||
 | 
						if (props.note.reactionAcceptance === 'likeOnly') {
 | 
				
			||||||
 | 
							os.api('notes/reactions/create', {
 | 
				
			||||||
 | 
								noteId: props.note.id,
 | 
				
			||||||
 | 
								reaction: '❤️',
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							const el = reactButton.value as HTMLElement | null | undefined;
 | 
				
			||||||
 | 
							if (el) {
 | 
				
			||||||
 | 
								const rect = el.getBoundingClientRect();
 | 
				
			||||||
 | 
								const x = rect.left + (el.offsetWidth / 2);
 | 
				
			||||||
 | 
								const y = rect.top + (el.offsetHeight / 2);
 | 
				
			||||||
 | 
								os.popup(MkRippleEffect, { x, y }, {}, 'end');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							blur();
 | 
				
			||||||
 | 
							reactionPicker.show(reactButton.value, reaction => {
 | 
				
			||||||
 | 
								os.api('notes/reactions/create', {
 | 
				
			||||||
 | 
									noteId: props.note.id,
 | 
				
			||||||
 | 
									reaction: reaction,
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								if (props.note.text && props.note.text.length > 100 && (Date.now() - new Date(props.note.createdAt).getTime() < 1000 * 3)) {
 | 
				
			||||||
 | 
									claimAchievement('reactWithoutRead');
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}, () => {
 | 
				
			||||||
 | 
								focus();
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function undoReact(note): void {
 | 
				
			||||||
 | 
						const oldReaction = note.myReaction;
 | 
				
			||||||
 | 
						if (!oldReaction) return;
 | 
				
			||||||
 | 
						os.api('notes/reactions/delete', {
 | 
				
			||||||
 | 
							noteId: note.id,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let showContent = $ref(false);
 | 
					let showContent = $ref(false);
 | 
				
			||||||
let replies: Misskey.entities.Note[] = $ref([]);
 | 
					let replies: Misskey.entities.Note[] = $ref([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function renote(viaKeyboard = false) {
 | 
				
			||||||
 | 
						pleaseLogin();
 | 
				
			||||||
 | 
						showMovedDialog();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let items = [] as MenuItem[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (props.note.channel) {
 | 
				
			||||||
 | 
							items = items.concat([{
 | 
				
			||||||
 | 
								text: i18n.ts.inChannelRenote,
 | 
				
			||||||
 | 
								icon: 'ph-repeat ph-bold ph-lg',
 | 
				
			||||||
 | 
								action: () => {
 | 
				
			||||||
 | 
									const el = renoteButton.value as HTMLElement | null | undefined;
 | 
				
			||||||
 | 
									if (el) {
 | 
				
			||||||
 | 
										const rect = el.getBoundingClientRect();
 | 
				
			||||||
 | 
										const x = rect.left + (el.offsetWidth / 2);
 | 
				
			||||||
 | 
										const y = rect.top + (el.offsetHeight / 2);
 | 
				
			||||||
 | 
										os.popup(MkRippleEffect, { x, y }, {}, 'end');
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									os.api('notes/create', {
 | 
				
			||||||
 | 
										renoteId: props.note.id,
 | 
				
			||||||
 | 
										channelId: props.note.channelId,
 | 
				
			||||||
 | 
									}).then(() => {
 | 
				
			||||||
 | 
										os.toast(i18n.ts.renoted);
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}, {
 | 
				
			||||||
 | 
								text: i18n.ts.inChannelQuote,
 | 
				
			||||||
 | 
								icon: 'ph-quotes ph-bold ph-lg',
 | 
				
			||||||
 | 
								action: () => {
 | 
				
			||||||
 | 
									os.post({
 | 
				
			||||||
 | 
										renote: props.note,
 | 
				
			||||||
 | 
										channel: props.note.channel,
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}, null]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						items = items.concat([{
 | 
				
			||||||
 | 
							text: i18n.ts.renote,
 | 
				
			||||||
 | 
							icon: 'ph-repeat ph-bold ph-lg',
 | 
				
			||||||
 | 
							action: () => {
 | 
				
			||||||
 | 
								const el = renoteButton.value as HTMLElement | null | undefined;
 | 
				
			||||||
 | 
								if (el) {
 | 
				
			||||||
 | 
									const rect = el.getBoundingClientRect();
 | 
				
			||||||
 | 
									const x = rect.left + (el.offsetWidth / 2);
 | 
				
			||||||
 | 
									const y = rect.top + (el.offsetHeight / 2);
 | 
				
			||||||
 | 
									os.popup(MkRippleEffect, { x, y }, {}, 'end');
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								os.api('notes/create', {
 | 
				
			||||||
 | 
									renoteId: props.note.id,
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									os.toast(i18n.ts.renoted);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							text: i18n.ts.quote,
 | 
				
			||||||
 | 
							icon: 'ph-quotes ph-bold ph-lg',
 | 
				
			||||||
 | 
							action: () => {
 | 
				
			||||||
 | 
								os.post({
 | 
				
			||||||
 | 
									renote: props.note,
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os.popupMenu(items, renoteButton.value, {
 | 
				
			||||||
 | 
							viaKeyboard,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function menu(viaKeyboard = false): void {
 | 
				
			||||||
 | 
						const { menu, cleanup } = getNoteMenu({ note: props.note, translating, translation, menuButton, isDeleted });
 | 
				
			||||||
 | 
						os.popupMenu(menu, menuButton.value, {
 | 
				
			||||||
 | 
							viaKeyboard,
 | 
				
			||||||
 | 
						}).then(focus).finally(cleanup);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (props.detail) {
 | 
					if (props.detail) {
 | 
				
			||||||
	os.api('notes/children', {
 | 
						os.api('notes/children', {
 | 
				
			||||||
		noteId: props.note.id,
 | 
							noteId: props.note.id,
 | 
				
			||||||
| 
						 | 
					@ -122,6 +300,31 @@ if (props.detail) {
 | 
				
			||||||
	margin-bottom: 2px;
 | 
						margin-bottom: 2px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.noteFooterButton {
 | 
				
			||||||
 | 
						margin: 0;
 | 
				
			||||||
 | 
						padding: 8px;
 | 
				
			||||||
 | 
						padding-top: 10px;
 | 
				
			||||||
 | 
						opacity: 0.7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&:not(:last-child) {
 | 
				
			||||||
 | 
							margin-right: 14px;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&:hover {
 | 
				
			||||||
 | 
							color: var(--fgHighlighted);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.noteFooterButtonCount {
 | 
				
			||||||
 | 
						display: inline;
 | 
				
			||||||
 | 
						margin: 0 0 0 8px;
 | 
				
			||||||
 | 
						opacity: 0.7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&.reacted {
 | 
				
			||||||
 | 
							color: var(--accent);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.cw {
 | 
					.cw {
 | 
				
			||||||
	cursor: default;
 | 
						cursor: default;
 | 
				
			||||||
	display: block;
 | 
						display: block;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue