| @@ -9,9 +9,11 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||
| 		<MkAvatar v-if="notification.type === 'pollEnded'" :class="$style.icon" :user="notification.note.user" link preview/> | ||||
| 		<MkAvatar v-else-if="notification.type === 'note'" :class="$style.icon" :user="notification.note.user" link preview/> | ||||
| 		<MkAvatar v-else-if="notification.type === 'achievementEarned'" :class="$style.icon" :user="$i" link preview/> | ||||
| 		<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> | ||||
| 		<img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/> | ||||
| 		<MkAvatar v-else-if="notification.user" :class="$style.icon" :user="notification.user" link preview/> | ||||
| 		<img v-else-if="notification.icon" :class="$style.icon" :src="notification.icon" alt=""/> | ||||
| 		<img v-else-if="notification.icon" :class="[$style.icon, $style.icon_app]" :src="notification.icon" alt=""/> | ||||
| 		<div | ||||
| 			:class="[$style.subIcon, { | ||||
| 				[$style.t_follow]: notification.type === 'follow', | ||||
| @@ -39,7 +41,6 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||
| 				v-else-if="notification.type === 'reaction'" | ||||
| 				ref="reactionRef" | ||||
| 				:reaction="notification.reaction ? notification.reaction.replace(/^:(\w+):$/, ':$1@.:') : notification.reaction" | ||||
| 				:customEmojis="notification.note.emojis" | ||||
| 				:noStyle="true" | ||||
| 				style="width: 100%; height: 100%;" | ||||
| 			/> | ||||
| @@ -52,16 +53,18 @@ 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 === 'test'">{{ i18n.ts._notification.testNotification }}</span> | ||||
| 			<MkA v-else-if="notification.user" 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'">{{ i18n.t('_notification.reactedBySomeUsers', { n: notification.reactions.length }) }}</span> | ||||
| 			<span v-else-if="notification.type === 'renote:grouped'">{{ i18n.t('_notification.renotedBySomeUsers', { n: notification.users.length }) }}</span> | ||||
| 			<span v-else>{{ notification.header }}</span> | ||||
| 			<MkTime v-if="withTime" :time="notification.createdAt" :class="$style.headerTime"/> | ||||
| 		</header> | ||||
| 		<div> | ||||
| 			<MkA v-if="notification.type === 'reaction'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> | ||||
| 			<MkA v-if="notification.type === 'reaction' || notification.type === 'reaction:grouped'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> | ||||
| 				<i class="ti ti-quote" :class="$style.quote"></i> | ||||
| 				<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/> | ||||
| 				<i class="ti ti-quote" :class="$style.quote"></i> | ||||
| 			</MkA> | ||||
| 			<MkA v-else-if="notification.type === 'renote'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)"> | ||||
| 			<MkA v-else-if="notification.type === 'renote' || notification.type === 'renote:grouped'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)"> | ||||
| 				<i class="ti ti-quote" :class="$style.quote"></i> | ||||
| 				<Mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="true" :author="notification.note.renote.user"/> | ||||
| 				<i class="ti ti-quote" :class="$style.quote"></i> | ||||
| @@ -102,6 +105,24 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||
| 			<span v-else-if="notification.type === 'app'" :class="$style.text"> | ||||
| 				<Mfm :text="notification.body" :nowrap="false"/> | ||||
| 			</span> | ||||
|  | ||||
| 			<div v-if="notification.type === 'reaction:grouped'"> | ||||
| 				<div v-for="reaction of notification.reactions" :class="$style.reactionsItem"> | ||||
| 					<MkAvatar :class="$style.reactionsItemAvatar" :user="reaction.user" link preview/> | ||||
| 					<div :class="$style.reactionsItemReaction"> | ||||
| 						<MkReactionIcon | ||||
| 							:reaction="reaction.reaction ? reaction.reaction.replace(/^:(\w+):$/, ':$1@.:') : reaction.reaction" | ||||
| 							:noStyle="true" | ||||
| 							style="width: 100%; height: 100%;" | ||||
| 						/> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div v-else-if="notification.type === 'renote:grouped'"> | ||||
| 				<div v-for="user of notification.users" :class="$style.reactionsItem"> | ||||
| 					<MkAvatar :class="$style.reactionsItemAvatar" :user="user" link preview/> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
| @@ -181,6 +202,29 @@ useTooltip(reactionRef, (showing) => { | ||||
| 	display: block; | ||||
| 	width: 100%; | ||||
| 	height: 100%; | ||||
| } | ||||
|  | ||||
| .icon_reactionGroup, | ||||
| .icon_renoteGroup { | ||||
| 	display: grid; | ||||
| 	align-items: center; | ||||
| 	justify-items: center; | ||||
| 	width: 80%; | ||||
| 	height: 80%; | ||||
| 	font-size: 15px; | ||||
| 	border-radius: 100%; | ||||
| 	color: #fff; | ||||
| } | ||||
|  | ||||
| .icon_reactionGroup { | ||||
| 	background: #e99a0b; | ||||
| } | ||||
|  | ||||
| .icon_renoteGroup { | ||||
| 	background: #36d298; | ||||
| } | ||||
|  | ||||
| .icon_app { | ||||
| 	border-radius: 6px; | ||||
| } | ||||
|  | ||||
| @@ -305,6 +349,36 @@ useTooltip(reactionRef, (showing) => { | ||||
| 	flex: 1; | ||||
| } | ||||
|  | ||||
| .reactionsItem { | ||||
| 	display: inline-block; | ||||
| 	position: relative; | ||||
| 	width: 38px; | ||||
| 	height: 38px; | ||||
| 	margin-top: 8px; | ||||
| 	margin-right: 8px; | ||||
| } | ||||
|  | ||||
| .reactionsItemAvatar { | ||||
| 	width: 100%; | ||||
| 	height: 100%; | ||||
| } | ||||
|  | ||||
| .reactionsItemReaction { | ||||
| 	position: absolute; | ||||
| 	z-index: 1; | ||||
| 	bottom: -2px; | ||||
| 	right: -2px; | ||||
| 	width: 20px; | ||||
| 	height: 20px; | ||||
| 	box-sizing: border-box; | ||||
| 	border-radius: 100%; | ||||
| 	background: var(--panel); | ||||
| 	box-shadow: 0 0 0 3px var(--panel); | ||||
| 	font-size: 11px; | ||||
| 	text-align: center; | ||||
| 	color: #fff; | ||||
| } | ||||
|  | ||||
| @container (max-width: 600px) { | ||||
| 	.root { | ||||
| 		padding: 16px; | ||||
|   | ||||
| @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||
| 	<template #default="{ items: notifications }"> | ||||
| 		<MkDateSeparatedList v-slot="{ item: notification }" :class="$style.list" :items="notifications" :noGap="true"> | ||||
| 			<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note"/> | ||||
| 			<XNotification v-else :key="notification.id" :notification="notification" :withTime="true" :full="true" class="_panel notification"/> | ||||
| 			<XNotification v-else :key="notification.id" :notification="notification" :withTime="true" :full="true" class="_panel"/> | ||||
| 		</MkDateSeparatedList> | ||||
| 	</template> | ||||
| </MkPagination> | ||||
| @@ -32,6 +32,7 @@ import { $i } from '@/account.js'; | ||||
| import { i18n } from '@/i18n.js'; | ||||
| import { notificationTypes } from '@/const.js'; | ||||
| import { infoImageUrl } from '@/instance.js'; | ||||
| import { defaultStore } from '@/store.js'; | ||||
|  | ||||
| const props = defineProps<{ | ||||
| 	excludeTypes?: typeof notificationTypes[number][]; | ||||
| @@ -39,7 +40,13 @@ const props = defineProps<{ | ||||
|  | ||||
| const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); | ||||
|  | ||||
| const pagination: Paging = { | ||||
| const pagination: Paging = defaultStore.state.useGroupedNotifications ? { | ||||
| 	endpoint: 'i/notifications-grouped' as const, | ||||
| 	limit: 20, | ||||
| 	params: computed(() => ({ | ||||
| 		excludeTypes: props.excludeTypes ?? undefined, | ||||
| 	})), | ||||
| } : { | ||||
| 	endpoint: 'i/notifications' as const, | ||||
| 	limit: 20, | ||||
| 	params: computed(() => ({ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo