mirror of
https://github.com/MisskeyIO/misskey
synced 2024-11-23 22:56:49 +09:00
Merge pull request MisskeyIO#509 from merge-upstream
This commit is contained in:
commit
411e6471dd
@ -4,7 +4,10 @@
|
|||||||
-
|
-
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
-
|
- Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
|
||||||
|
- Enhance: リアクション・いいねの総数を表示するように
|
||||||
|
- Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように
|
||||||
|
- Fix: 一部のページ内リンクが正しく動作しない問題を修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
-
|
-
|
||||||
|
@ -2338,6 +2338,7 @@ _notification:
|
|||||||
sendTestNotification: "Send test notification"
|
sendTestNotification: "Send test notification"
|
||||||
notificationWillBeDisplayedLikeThis: "Notifications look like this"
|
notificationWillBeDisplayedLikeThis: "Notifications look like this"
|
||||||
reactedBySomeUsers: "{n} users reacted"
|
reactedBySomeUsers: "{n} users reacted"
|
||||||
|
likedBySomeUsers: "{n} users liked"
|
||||||
renotedBySomeUsers: "Renote from {n} users"
|
renotedBySomeUsers: "Renote from {n} users"
|
||||||
followedBySomeUsers: "Followed by {n} users"
|
followedBySomeUsers: "Followed by {n} users"
|
||||||
flushNotification: "Clear notifications"
|
flushNotification: "Clear notifications"
|
||||||
|
4
locales/index.d.ts
vendored
4
locales/index.d.ts
vendored
@ -9089,6 +9089,10 @@ export interface Locale extends ILocale {
|
|||||||
* {n}人がリアクションしました
|
* {n}人がリアクションしました
|
||||||
*/
|
*/
|
||||||
"reactedBySomeUsers": ParameterizedString<"n">;
|
"reactedBySomeUsers": ParameterizedString<"n">;
|
||||||
|
/**
|
||||||
|
* {n}人がいいねしました
|
||||||
|
*/
|
||||||
|
"likedBySomeUsers": ParameterizedString<"n">;
|
||||||
/**
|
/**
|
||||||
* {n}人がリノートしました
|
* {n}人がリノートしました
|
||||||
*/
|
*/
|
||||||
|
@ -2403,6 +2403,7 @@ _notification:
|
|||||||
sendTestNotification: "テスト通知を送信する"
|
sendTestNotification: "テスト通知を送信する"
|
||||||
notificationWillBeDisplayedLikeThis: "通知はこのように表示されます"
|
notificationWillBeDisplayedLikeThis: "通知はこのように表示されます"
|
||||||
reactedBySomeUsers: "{n}人がリアクションしました"
|
reactedBySomeUsers: "{n}人がリアクションしました"
|
||||||
|
likedBySomeUsers: "{n}人がいいねしました"
|
||||||
renotedBySomeUsers: "{n}人がリノートしました"
|
renotedBySomeUsers: "{n}人がリノートしました"
|
||||||
followedBySomeUsers: "{n}人にフォローされました"
|
followedBySomeUsers: "{n}人にフォローされました"
|
||||||
flushNotification: "通知の履歴をリセットする"
|
flushNotification: "通知の履歴をリセットする"
|
||||||
|
@ -2315,6 +2315,7 @@ _notification:
|
|||||||
sendTestNotification: "테스트 알림 보내기"
|
sendTestNotification: "테스트 알림 보내기"
|
||||||
notificationWillBeDisplayedLikeThis: "알림이 이렇게 표시됩니다"
|
notificationWillBeDisplayedLikeThis: "알림이 이렇게 표시됩니다"
|
||||||
reactedBySomeUsers: "{n}명이 반응했습니다"
|
reactedBySomeUsers: "{n}명이 반응했습니다"
|
||||||
|
likedBySomeUsers: "{n}명이 좋아요했습니다"
|
||||||
renotedBySomeUsers: "{n}명이 리노트했습니다"
|
renotedBySomeUsers: "{n}명이 리노트했습니다"
|
||||||
followedBySomeUsers: "{n}명에게 팔로우됨"
|
followedBySomeUsers: "{n}명에게 팔로우됨"
|
||||||
_types:
|
_types:
|
||||||
|
2
packages/backend/src/@types/fastify.d.ts
vendored
2
packages/backend/src/@types/fastify.d.ts
vendored
@ -3,7 +3,7 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import FastifyReply from "fastify";
|
import FastifyReply from 'fastify';
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyReply {
|
interface FastifyReply {
|
||||||
|
@ -8,7 +8,7 @@ import * as OTPAuth from 'otpauth';
|
|||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
import type { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { IdentifiableError } from "@/misc/identifiable-error.js";
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserAuthService {
|
export class UserAuthService {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { AnnouncementsRepository, AnnouncementReadsRepository, MiAnnouncement, MiUser } from "@/models/_.js";
|
import type { AnnouncementsRepository, AnnouncementReadsRepository, MiAnnouncement, MiUser } from '@/models/_.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
@ -250,7 +250,7 @@ export class DriveFileEntityService {
|
|||||||
folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, {
|
folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, {
|
||||||
detail: true,
|
detail: true,
|
||||||
}) : null,
|
}) : null,
|
||||||
userId: opts.withUser ? file.userId : null,
|
userId: file.userId,
|
||||||
user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId, me) : null,
|
user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId, me) : null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -338,6 +338,7 @@ export class NoteEntityService implements OnModuleInit {
|
|||||||
visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined,
|
visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined,
|
||||||
renoteCount: note.renoteCount,
|
renoteCount: note.renoteCount,
|
||||||
repliesCount: note.repliesCount,
|
repliesCount: note.repliesCount,
|
||||||
|
reactionCount: Object.values(note.reactions).reduce((a, b) => a + b, 0),
|
||||||
reactions: this.reactionService.convertLegacyReactions(note.reactions),
|
reactions: this.reactionService.convertLegacyReactions(note.reactions),
|
||||||
reactionEmojis: this.customEmojiService.populateEmojis(reactionEmojiNames, host),
|
reactionEmojis: this.customEmojiService.populateEmojis(reactionEmojiNames, host),
|
||||||
reactionAndUserPairCache: opts.withReactionAndUserPairCache ? note.reactionAndUserPairCache : undefined,
|
reactionAndUserPairCache: opts.withReactionAndUserPairCache ? note.reactionAndUserPairCache : undefined,
|
||||||
|
@ -223,6 +223,10 @@ export const packedNoteSchema = {
|
|||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
reactionCount: {
|
||||||
|
type: 'number',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
renoteCount: {
|
renoteCount: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type * as Bull from 'bullmq';
|
import type * as Bull from 'bullmq';
|
||||||
import type { RedisOptions } from "ioredis";
|
import type { RedisOptions } from 'ioredis';
|
||||||
import type { RedisOptionsSource } from '@/config.js';
|
import type { RedisOptionsSource } from '@/config.js';
|
||||||
|
|
||||||
export const QUEUE = {
|
export const QUEUE = {
|
||||||
|
@ -17,8 +17,8 @@ import { QueryService } from '@/core/QueryService.js';
|
|||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { MiLocalUser } from '@/models/User.js';
|
import { MiLocalUser } from '@/models/User.js';
|
||||||
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
|
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
|
||||||
|
import { FanoutTimelineName } from '@/core/FanoutTimelineService.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
import { FanoutTimelineName } from "@/core/FanoutTimelineService.js";
|
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['notes'],
|
tags: ['notes'],
|
||||||
|
@ -12,8 +12,8 @@ import { DI } from '@/di-symbols.js';
|
|||||||
import { CacheService } from '@/core/CacheService.js';
|
import { CacheService } from '@/core/CacheService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
import { MiNoteReaction } from '@/models/_.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
import { MiNoteReaction } from "@/models/_.js";
|
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['users', 'reactions'],
|
tags: ['users', 'reactions'],
|
||||||
|
@ -72,8 +72,8 @@
|
|||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Script
|
//#region Script
|
||||||
function importAppScript() {
|
async function importAppScript() {
|
||||||
import(`/vite/${CLIENT_ENTRY}`)
|
await import(`/vite/${CLIENT_ENTRY}`)
|
||||||
.catch(async e => {
|
.catch(async e => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
renderError('APP_IMPORT', e);
|
renderError('APP_IMPORT', e);
|
||||||
|
@ -10,7 +10,7 @@ import { ModuleMocker } from 'jest-mock';
|
|||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
import { GlobalModule } from '@/GlobalModule.js';
|
import { GlobalModule } from '@/GlobalModule.js';
|
||||||
import { AnnouncementService } from '@/core/AnnouncementService.js';
|
import { AnnouncementService } from '@/core/AnnouncementService.js';
|
||||||
import { AnnouncementEntityService } from "@/core/entities/AnnouncementEntityService.js";
|
import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
|
||||||
import type {
|
import type {
|
||||||
AnnouncementReadsRepository,
|
AnnouncementReadsRepository,
|
||||||
AnnouncementsRepository,
|
AnnouncementsRepository,
|
||||||
|
@ -82,9 +82,7 @@ const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.data
|
|||||||
const menuShowing = ref(false);
|
const menuShowing = ref(false);
|
||||||
|
|
||||||
function showMenu(ev: MouseEvent) {
|
function showMenu(ev: MouseEvent) {
|
||||||
let menu: MenuItem[] = [];
|
const menu: MenuItem[] = [
|
||||||
|
|
||||||
menu = [
|
|
||||||
// TODO: 再生キューに追加
|
// TODO: 再生キューに追加
|
||||||
{
|
{
|
||||||
text: i18n.ts.hide,
|
text: i18n.ts.hide,
|
||||||
@ -95,15 +93,37 @@ function showMenu(ev: MouseEvent) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (iAmModerator) {
|
if ($i?.id === props.audio.userId || iAmModerator) {
|
||||||
menu.push({
|
menu.push({
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
}, {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iAmModerator) {
|
||||||
|
menu.push({
|
||||||
text: props.audio.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
|
text: props.audio.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
|
||||||
icon: props.audio.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation',
|
icon: props.audio.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation',
|
||||||
danger: true,
|
danger: true,
|
||||||
action: () => toggleSensitive(props.audio),
|
action: () => toggleSensitive(props.audio),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ($i?.id !== props.audio.userId) {
|
||||||
|
menu.push({
|
||||||
|
type: 'link' as const,
|
||||||
|
text: i18n.ts._fileViewer.title,
|
||||||
|
icon: 'ti ti-info-circle',
|
||||||
|
to: `/admin/file/${props.audio.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($i?.id === props.audio.userId) {
|
||||||
|
menu.push({
|
||||||
|
type: 'link' as const,
|
||||||
|
text: i18n.ts._fileViewer.title,
|
||||||
|
icon: 'ti ti-info-circle',
|
||||||
|
to: `/my/drive/file/${props.audio.id}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
menuShowing.value = true;
|
menuShowing.value = true;
|
||||||
|
@ -53,6 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watch, ref, computed } from 'vue';
|
import { watch, ref, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
|
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
|
||||||
import bytes from '@/filters/bytes.js';
|
import bytes from '@/filters/bytes.js';
|
||||||
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
||||||
@ -84,6 +85,51 @@ const url = computed(() => (props.raw || defaultStore.state.loadRawImages)
|
|||||||
: props.image.thumbnailUrl,
|
: props.image.thumbnailUrl,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function showMenu(ev: MouseEvent) {
|
||||||
|
const menu: MenuItem[] = [{
|
||||||
|
text: i18n.ts.hide,
|
||||||
|
icon: 'ti ti-eye-off',
|
||||||
|
action: () => {
|
||||||
|
hide.value = true;
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
|
||||||
|
if ($i?.id === props.image.userId || iAmModerator) {
|
||||||
|
menu.push({
|
||||||
|
type: 'divider',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iAmModerator) {
|
||||||
|
menu.push({
|
||||||
|
text: props.image.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
|
||||||
|
icon: props.image.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation',
|
||||||
|
danger: true,
|
||||||
|
action: () => toggleSensitive(props.image),
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($i?.id !== props.image.userId) {
|
||||||
|
menu.push({
|
||||||
|
type: 'link' as const,
|
||||||
|
text: i18n.ts._fileViewer.title,
|
||||||
|
icon: 'ti ti-info-circle',
|
||||||
|
to: `/admin/file/${props.image.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($i?.id === props.image.userId) {
|
||||||
|
menu.push({
|
||||||
|
type: 'link' as const,
|
||||||
|
text: i18n.ts._fileViewer.title,
|
||||||
|
icon: 'ti ti-info-circle',
|
||||||
|
to: `/my/drive/file/${props.image.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
os.popupMenu(menu, ev.currentTarget ?? ev.target);
|
||||||
|
}
|
||||||
|
|
||||||
function showHiddenContent(ev: MouseEvent) {
|
function showHiddenContent(ev: MouseEvent) {
|
||||||
if (!props.controls) {
|
if (!props.controls) {
|
||||||
return;
|
return;
|
||||||
@ -103,6 +149,13 @@ function showHiddenContent(ev: MouseEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleSensitive(file: Misskey.entities.DriveFile) {
|
||||||
|
os.apiWithDialog('drive/files/update', {
|
||||||
|
fileId: file.id,
|
||||||
|
isSensitive: !file.isSensitive,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
|
// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
|
||||||
watch(() => props.image, () => {
|
watch(() => props.image, () => {
|
||||||
hide.value = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
|
hide.value = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
|
||||||
@ -110,24 +163,6 @@ watch(() => props.image, () => {
|
|||||||
deep: true,
|
deep: true,
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
function showMenu(ev: MouseEvent) {
|
|
||||||
os.popupMenu([{
|
|
||||||
text: i18n.ts.hide,
|
|
||||||
icon: 'ti ti-eye-off',
|
|
||||||
action: () => {
|
|
||||||
hide.value = true;
|
|
||||||
},
|
|
||||||
}, ...(iAmModerator ? [{
|
|
||||||
text: i18n.ts.markAsSensitive,
|
|
||||||
icon: 'ti ti-eye-exclamation',
|
|
||||||
danger: true,
|
|
||||||
action: () => {
|
|
||||||
os.apiWithDialog('drive/files/update', { fileId: props.image.id, isSensitive: true });
|
|
||||||
},
|
|
||||||
}] : [])], ev.currentTarget ?? ev.target);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
@ -112,9 +112,7 @@ const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.data
|
|||||||
const menuShowing = ref(false);
|
const menuShowing = ref(false);
|
||||||
|
|
||||||
function showMenu(ev: MouseEvent) {
|
function showMenu(ev: MouseEvent) {
|
||||||
let menu: MenuItem[] = [];
|
const menu: MenuItem[] = [
|
||||||
|
|
||||||
menu = [
|
|
||||||
// TODO: 再生キューに追加
|
// TODO: 再生キューに追加
|
||||||
{
|
{
|
||||||
text: i18n.ts.hide,
|
text: i18n.ts.hide,
|
||||||
@ -125,15 +123,37 @@ function showMenu(ev: MouseEvent) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (iAmModerator) {
|
if ($i?.id === props.video.userId || iAmModerator) {
|
||||||
menu.push({
|
menu.push({
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
}, {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iAmModerator) {
|
||||||
|
menu.push({
|
||||||
text: props.video.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
|
text: props.video.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
|
||||||
icon: props.video.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation',
|
icon: props.video.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation',
|
||||||
danger: true,
|
danger: true,
|
||||||
action: () => toggleSensitive(props.video),
|
action: () => toggleSensitive(props.video),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ($i?.id !== props.video.userId) {
|
||||||
|
menu.push({
|
||||||
|
type: 'link' as const,
|
||||||
|
text: i18n.ts._fileViewer.title,
|
||||||
|
icon: 'ti ti-info-circle',
|
||||||
|
to: `/admin/file/${props.video.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($i?.id === props.video.userId) {
|
||||||
|
menu.push({
|
||||||
|
type: 'link' as const,
|
||||||
|
text: i18n.ts._fileViewer.title,
|
||||||
|
icon: 'ti ti-info-circle',
|
||||||
|
to: `/my/drive/file/${props.video.id}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
menuShowing.value = true;
|
menuShowing.value = true;
|
||||||
|
@ -93,7 +93,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
|
<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
|
||||||
</div>
|
</div>
|
||||||
<MkReactionsViewer :note="appearNote" :maxNumber="16" @mockUpdateMyReaction="emitUpdReaction">
|
<MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" :note="appearNote" :maxNumber="16" @mockUpdateMyReaction="emitUpdReaction">
|
||||||
<template #more>
|
<template #more>
|
||||||
<div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div>
|
<div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div>
|
||||||
</template>
|
</template>
|
||||||
@ -101,7 +101,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<footer :class="$style.footer">
|
<footer :class="$style.footer">
|
||||||
<button :class="$style.footerButton" class="_button" @click="reply()">
|
<button :class="$style.footerButton" class="_button" @click="reply()">
|
||||||
<i class="ti ti-arrow-back-up"></i>
|
<i class="ti ti-arrow-back-up"></i>
|
||||||
<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p>
|
<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.repliesCount) }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="canRenote"
|
v-if="canRenote"
|
||||||
@ -111,17 +111,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
@mousedown="renote()"
|
@mousedown="renote()"
|
||||||
>
|
>
|
||||||
<i class="ti ti-repeat"></i>
|
<i class="ti ti-repeat"></i>
|
||||||
<p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p>
|
<p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.renoteCount) }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button v-else :class="$style.footerButton" class="_button" disabled>
|
<button v-else :class="$style.footerButton" class="_button" disabled>
|
||||||
<i class="ti ti-ban"></i>
|
<i class="ti ti-ban"></i>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" @mousedown="react()">
|
<button ref="reactButton" :class="$style.footerButton" class="_button" @click="toggleReact()">
|
||||||
<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
|
<i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i>
|
||||||
|
<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i>
|
||||||
|
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
|
||||||
<i v-else class="ti ti-plus"></i>
|
<i v-else class="ti ti-plus"></i>
|
||||||
</button>
|
<p v-if="appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
|
||||||
<button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" @click="undoReact(appearNote)">
|
|
||||||
<i class="ti ti-minus"></i>
|
|
||||||
</button>
|
</button>
|
||||||
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()">
|
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()">
|
||||||
<i class="ti ti-paperclip"></i>
|
<i class="ti ti-paperclip"></i>
|
||||||
@ -169,6 +169,7 @@ import { pleaseLogin } from '@/scripts/please-login.js';
|
|||||||
import { focusPrev, focusNext } from '@/scripts/focus.js';
|
import { focusPrev, focusNext } from '@/scripts/focus.js';
|
||||||
import { checkWordMute } from '@/scripts/check-word-mute.js';
|
import { checkWordMute } from '@/scripts/check-word-mute.js';
|
||||||
import { userPage } from '@/filters/user.js';
|
import { userPage } from '@/filters/user.js';
|
||||||
|
import number from '@/filters/number.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import * as sound from '@/scripts/sound.js';
|
import * as sound from '@/scripts/sound.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
@ -406,6 +407,14 @@ function undoReact(targetNote: Misskey.entities.Note): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleReact() {
|
||||||
|
if (appearNote.value.myReaction == null) {
|
||||||
|
react();
|
||||||
|
} else {
|
||||||
|
undoReact(appearNote.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onContextmenu(ev: MouseEvent): void {
|
function onContextmenu(ev: MouseEvent): void {
|
||||||
if (props.mock) {
|
if (props.mock) {
|
||||||
return;
|
return;
|
||||||
|
@ -106,10 +106,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkTime :time="appearNote.createdAt" mode="detail" colored/>
|
<MkTime :time="appearNote.createdAt" mode="detail" colored/>
|
||||||
</MkA>
|
</MkA>
|
||||||
</div>
|
</div>
|
||||||
<MkReactionsViewer ref="reactionsViewer" :note="appearNote"/>
|
<MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" :note="appearNote"/>
|
||||||
<button class="_button" :class="$style.noteFooterButton" @click="reply()">
|
<button class="_button" :class="$style.noteFooterButton" @click="reply()">
|
||||||
<i class="ti ti-arrow-back-up"></i>
|
<i class="ti ti-arrow-back-up"></i>
|
||||||
<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.repliesCount }}</p>
|
<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="canRenote"
|
v-if="canRenote"
|
||||||
@ -119,17 +119,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
@mousedown="renote()"
|
@mousedown="renote()"
|
||||||
>
|
>
|
||||||
<i class="ti ti-repeat"></i>
|
<i class="ti ti-repeat"></i>
|
||||||
<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.renoteCount }}</p>
|
<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.renoteCount) }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button v-else class="_button" :class="$style.noteFooterButton" disabled>
|
<button v-else class="_button" :class="$style.noteFooterButton" disabled>
|
||||||
<i class="ti ti-ban"></i>
|
<i class="ti ti-ban"></i>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()">
|
<button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()">
|
||||||
<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
|
<i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i>
|
||||||
|
<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i>
|
||||||
|
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
|
||||||
<i v-else class="ti ti-plus"></i>
|
<i v-else class="ti ti-plus"></i>
|
||||||
</button>
|
<p v-if="appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
|
||||||
<button v-if="appearNote.myReaction != null" ref="reactButton" class="_button" :class="[$style.noteFooterButton, $style.reacted]" @click="undoReact(appearNote)">
|
|
||||||
<i class="ti ti-minus"></i>
|
|
||||||
</button>
|
</button>
|
||||||
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()">
|
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()">
|
||||||
<i class="ti ti-paperclip"></i>
|
<i class="ti ti-paperclip"></i>
|
||||||
@ -209,6 +209,7 @@ import { pleaseLogin } from '@/scripts/please-login.js';
|
|||||||
import { checkWordMute } from '@/scripts/check-word-mute.js';
|
import { checkWordMute } from '@/scripts/check-word-mute.js';
|
||||||
import { userPage } from '@/filters/user.js';
|
import { userPage } from '@/filters/user.js';
|
||||||
import { notePage } from '@/filters/note.js';
|
import { notePage } from '@/filters/note.js';
|
||||||
|
import number from '@/filters/number.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import * as sound from '@/scripts/sound.js';
|
import * as sound from '@/scripts/sound.js';
|
||||||
@ -402,14 +403,22 @@ function react(viaKeyboard = false): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function undoReact(note): void {
|
function undoReact(targetNote: Misskey.entities.Note): void {
|
||||||
const oldReaction = note.myReaction;
|
const oldReaction = targetNote.myReaction;
|
||||||
if (!oldReaction) return;
|
if (!oldReaction) return;
|
||||||
misskeyApi('notes/reactions/delete', {
|
misskeyApi('notes/reactions/delete', {
|
||||||
noteId: note.id,
|
noteId: targetNote.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleReact() {
|
||||||
|
if (appearNote.value.myReaction == null) {
|
||||||
|
react();
|
||||||
|
} else {
|
||||||
|
undoReact(appearNote.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onContextmenu(ev: MouseEvent): void {
|
function onContextmenu(ev: MouseEvent): void {
|
||||||
const isLink = (el: HTMLElement): boolean => {
|
const isLink = (el: HTMLElement): boolean => {
|
||||||
if (el.tagName === 'A') return true;
|
if (el.tagName === 'A') return true;
|
||||||
|
@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<div :class="$style.head">
|
<div :class="$style.head">
|
||||||
<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && notification.note" :class="$style.icon" :user="notification.note.user" link preview/>
|
<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && notification.note" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||||
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
||||||
|
<div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ti ti-heart" style="line-height: 1;"></i></div>
|
||||||
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
|
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
|
||||||
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
|
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
|
||||||
<img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/>
|
<img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/>
|
||||||
@ -57,6 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
|
<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
|
||||||
<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
|
<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
|
||||||
<MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
|
<MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
|
||||||
|
<span v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'">{{ i18n.tsx._notification.likedBySomeUsers({ n: notification.reactions.length }) }}</span>
|
||||||
<span v-else-if="notification.type === 'reaction:grouped'">{{ i18n.tsx._notification.reactedBySomeUsers({ n: notification.reactions.length }) }}</span>
|
<span v-else-if="notification.type === 'reaction:grouped'">{{ i18n.tsx._notification.reactedBySomeUsers({ n: notification.reactions.length }) }}</span>
|
||||||
<span v-else-if="notification.type === 'renote:grouped'">{{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }}</span>
|
<span v-else-if="notification.type === 'renote:grouped'">{{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }}</span>
|
||||||
<span v-else-if="notification.type === 'app'">{{ notification.header }}</span>
|
<span v-else-if="notification.type === 'app'">{{ notification.header }}</span>
|
||||||
@ -201,6 +203,7 @@ const rejectFollowRequest = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon_reactionGroup,
|
.icon_reactionGroup,
|
||||||
|
.icon_reactionGroupHeart,
|
||||||
.icon_renoteGroup {
|
.icon_renoteGroup {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -213,11 +216,15 @@ const rejectFollowRequest = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon_reactionGroup {
|
.icon_reactionGroup {
|
||||||
background: #e99a0b;
|
background: var(--eventReaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_reactionGroupHeart {
|
||||||
|
background: var(--eventReactionHeart);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon_renoteGroup {
|
.icon_renoteGroup {
|
||||||
background: #36d298;
|
background: var(--eventRenote);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon_app {
|
.icon_app {
|
||||||
@ -246,49 +253,49 @@ const rejectFollowRequest = () => {
|
|||||||
|
|
||||||
.t_follow, .t_followRequestAccepted, .t_receiveFollowRequest {
|
.t_follow, .t_followRequestAccepted, .t_receiveFollowRequest {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: #36aed2;
|
background: var(--eventFollow);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.t_renote {
|
.t_renote {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: #36d298;
|
background: var(--eventRenote);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.t_quote {
|
.t_quote {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: #36d298;
|
background: var(--eventRenote);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.t_reply {
|
.t_reply {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: #007aff;
|
background: var(--eventReply);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.t_mention {
|
.t_mention {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: #88a6b7;
|
background: var(--eventOther);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.t_pollEnded {
|
.t_pollEnded {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: #88a6b7;
|
background: var(--eventOther);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.t_achievementEarned {
|
.t_achievementEarned {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: #cb9a11;
|
background: var(--eventAchievement);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.t_roleAssigned {
|
.t_roleAssigned {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: #88a6b7;
|
background: var(--eventOther);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ const exampleNote = reactive<Misskey.entities.Note>({
|
|||||||
reactionAcceptance: null,
|
reactionAcceptance: null,
|
||||||
renoteCount: 0,
|
renoteCount: 0,
|
||||||
repliesCount: 1,
|
repliesCount: 1,
|
||||||
|
reactionCount: 0,
|
||||||
reactions: {},
|
reactions: {},
|
||||||
reactionEmojis: {},
|
reactionEmojis: {},
|
||||||
fileIds: [],
|
fileIds: [],
|
||||||
|
@ -68,6 +68,7 @@ const exampleCWNote = reactive<Misskey.entities.Note>({
|
|||||||
reactionAcceptance: null,
|
reactionAcceptance: null,
|
||||||
renoteCount: 0,
|
renoteCount: 0,
|
||||||
repliesCount: 1,
|
repliesCount: 1,
|
||||||
|
reactionCount: 0,
|
||||||
reactions: {},
|
reactions: {},
|
||||||
reactionEmojis: {},
|
reactionEmojis: {},
|
||||||
fileIds: [],
|
fileIds: [],
|
||||||
|
@ -58,6 +58,7 @@ const exampleNote = reactive<Misskey.entities.Note>({
|
|||||||
reactionAcceptance: null,
|
reactionAcceptance: null,
|
||||||
renoteCount: 0,
|
renoteCount: 0,
|
||||||
repliesCount: 1,
|
repliesCount: 1,
|
||||||
|
reactionCount: 0,
|
||||||
reactions: {},
|
reactions: {},
|
||||||
reactionEmojis: {},
|
reactionEmojis: {},
|
||||||
fileIds: ['0000000002'],
|
fileIds: ['0000000002'],
|
||||||
|
@ -373,7 +373,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
|||||||
this.currentRoute.value = res.route;
|
this.currentRoute.value = res.route;
|
||||||
this.currentKey = res.route.globalCacheKey ?? key ?? path;
|
this.currentKey = res.route.globalCacheKey ?? key ?? path;
|
||||||
|
|
||||||
if (emitChange) {
|
if (emitChange && res.route.path !== '/:(*)') {
|
||||||
this.emit('change', {
|
this.emit('change', {
|
||||||
beforePath,
|
beforePath,
|
||||||
path,
|
path,
|
||||||
@ -408,13 +408,17 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
|
|||||||
if (cancel) return;
|
if (cancel) return;
|
||||||
}
|
}
|
||||||
const res = this.navigate(path, null);
|
const res = this.navigate(path, null);
|
||||||
this.emit('push', {
|
if (res.route.path === '/:(*)') {
|
||||||
beforePath,
|
location.href = path;
|
||||||
path: res._parsedRoute.fullPath,
|
} else {
|
||||||
route: res.route,
|
this.emit('push', {
|
||||||
props: res.props,
|
beforePath,
|
||||||
key: this.currentKey,
|
path: res._parsedRoute.fullPath,
|
||||||
});
|
route: res.route,
|
||||||
|
props: res.props,
|
||||||
|
key: this.currentKey,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public replace(path: string, key?: string | null) {
|
public replace(path: string, key?: string | null) {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import type { SoundStore } from '@/store.js';
|
import type { SoundStore } from '@/store.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { $i } from "@/account.js";
|
import { $i } from '@/account.js';
|
||||||
|
|
||||||
let ctx: AudioContext;
|
let ctx: AudioContext;
|
||||||
const cache = new Map<string, AudioBuffer>();
|
const cache = new Map<string, AudioBuffer>();
|
||||||
|
@ -35,6 +35,7 @@ export function useNoteCapture(props: {
|
|||||||
const currentCount = (note.value.reactions || {})[reaction] || 0;
|
const currentCount = (note.value.reactions || {})[reaction] || 0;
|
||||||
|
|
||||||
note.value.reactions[reaction] = currentCount + 1;
|
note.value.reactions[reaction] = currentCount + 1;
|
||||||
|
note.value.reactionCount += 1;
|
||||||
|
|
||||||
if ($i && (body.userId === $i.id)) {
|
if ($i && (body.userId === $i.id)) {
|
||||||
note.value.myReaction = reaction;
|
note.value.myReaction = reaction;
|
||||||
@ -49,6 +50,7 @@ export function useNoteCapture(props: {
|
|||||||
const currentCount = (note.value.reactions || {})[reaction] || 0;
|
const currentCount = (note.value.reactions || {})[reaction] || 0;
|
||||||
|
|
||||||
note.value.reactions[reaction] = Math.max(0, currentCount - 1);
|
note.value.reactions[reaction] = Math.max(0, currentCount - 1);
|
||||||
|
note.value.reactionCount = Math.max(0, note.value.reactionCount - 1);
|
||||||
if (note.value.reactions[reaction] === 0) delete note.value.reactions[reaction];
|
if (note.value.reactions[reaction] === 0) delete note.value.reactions[reaction];
|
||||||
|
|
||||||
if ($i && (body.userId === $i.id)) {
|
if ($i && (body.userId === $i.id)) {
|
||||||
|
@ -22,6 +22,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//--ad: rgb(255 169 0 / 10%);
|
//--ad: rgb(255 169 0 / 10%);
|
||||||
|
--eventFollow: #36aed2;
|
||||||
|
--eventRenote: #36d298;
|
||||||
|
--eventReply: #007aff;
|
||||||
|
--eventReactionHeart: #dd2e44;
|
||||||
|
--eventReaction: #e99a0b;
|
||||||
|
--eventAchievement: #cb9a11;
|
||||||
|
--eventOther: #88a6b7;
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
|
@ -48,6 +48,9 @@ const devConfig = {
|
|||||||
},
|
},
|
||||||
'/url': httpUrl,
|
'/url': httpUrl,
|
||||||
'/proxy': httpUrl,
|
'/proxy': httpUrl,
|
||||||
|
'/_info_card_': httpUrl,
|
||||||
|
'/bios': httpUrl,
|
||||||
|
'/cli': httpUrl,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
|
@ -4095,6 +4095,7 @@ export type components = {
|
|||||||
reactions: {
|
reactions: {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
};
|
};
|
||||||
|
reactionCount: number;
|
||||||
renoteCount: number;
|
renoteCount: number;
|
||||||
repliesCount: number;
|
repliesCount: number;
|
||||||
uri?: string;
|
uri?: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user